using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using QCGM.Boards;
using QCGM.Graphs2;

namespace QCGM.Net
{
	/// <summary>
	/// Client is the class that represents one player. An instance
	/// of a Client class connects and communicates to the server with a socket.
	/// </summary>
	/// 
	public class Client
	{
		#region Private data members
		private const int BUFFER_LENGTH = 1024;

		public const int PLAYER_WHITE = 1;
		public const int PLAYER_BLACK = 2;

		protected int localClients = 0;

		protected object pickedObject;
		public object PickedObject
		{
			get
			{
				return pickedObject;
			}
			set
			{
				pickedObject = value;
			}
		}
		
		protected Player MyPlayer;

		public Player Player
		{
			get
			{
				return MyPlayer;
			}
		}
		
		private string name = "Not Set"; // The human-readable name for the client.
		private string opponentName = "Opponent";
		private int playerNumber; // Indicates which player this client is: 1 or 2.
		
		private Socket socket; // The communication socket between this client and the server.
		private byte[] buffer = new byte[BUFFER_LENGTH]; // Data buffer for network communication.
		private int recievedMessages = 0; // Total number of messages recieved.

		protected MoveStates myState;

		#endregion

		#region Constructors / Destructors

		public static string SocketErrorCodeDescription(int ErrorCode)
		{
			switch(ErrorCode)
			{
				case 10004:
					return "Interrupted function call.";
				case 10013:
					return "Permission denied.";
				case 10014:
					return "Bad address.";
				case 10022:
					return "Invalid argument.";
				case 10024:
					return "Too many open files.";
				case 10035:
					return "Resource temporarily unavailable.";
				case 10036:
					return "Operation now in progress.";
				case 10037:
					return "Operation already in progress.";
				case 10038:
					return "Socket operation on nonsocket.";
				case 10039:
					return "Destination address required.";
				case 10040:
					return "Message too long.";
				case 10041:
					return "Protocol wrong type for socket.";
				case 10042:
					return "Bad protocol option.";
				case 10043:
					return "Protocol not supported.";
				case 10044:
					return "Socket type not supported.";
				case 10045:
					return "Operation not supported.";
				case 10046:
					return "Protocol family not supported.";
				case 10047:
					return "Address family not supported by protocol family.";
				case 10048:
					return "Address already in use.";
				case 10049:
					return "Cannot assign requested address.";
				case 10050:
					return "Network is down.";
				case 10051:
					return "Network unreachable.";
				case 10052:
					return "Network dropped connection on reset.";
				case 10053:
					return "Software caused connection abort.";
				case 10054:
					return "Connection reset by peer.";
				case 10055:
					return "No buffer space available.";
				case 10056:
					return "Socket is already connected.";
				case 10057:
					return "Socket is not connected.";
				case 10058:
					return "Cannot send after socket shutdown.";
				case 10060:
					return "Connection timed out.";
				case 10061:
					return "Connection refused.";
				case 10064:
					return "Host is down.";
				case 10065:
					return "No route to host.";
				case 10067:
					return "Too many processes.";
				case 10091:
					return "Network subsystem is unavailable.";
				case 10092:
					return "Winsock.dll version out of range.";
				case 10093:
					return "Successful WSAStartup not yet performed.";
				case 10101:
					return "Graceful shutdown in progress.";
				case 10109:
					return "Class type not found.";
				case 11001:
					return "Host not found.";
				case 11002:
					return "Nonauthoritative host not found.";
				case 11003:
					return "This is a nonrecoverable error.";
				case 11004:
					return "Valid name, no data record of requested type.";
				default:
					return "OS dependent or unrecognized error. Look up errorcode in Winsock 2 API for more information.";
			}
		}

		private DialogResult HandleException(Exception e)
		{
			if(e is SocketException)
			{
				SocketException se = (SocketException)e;
				return MessageBox.Show(
					"SocketException caught by Client.\r\n\r\n" +
					"Source: " + se.Source + "\r\n\r\n" +
					"Socket error code: " + se.ErrorCode + "\r\n\r\n" +
					"Error code description: " + SocketErrorCodeDescription(se.ErrorCode) + "\r\n\r\n" +
					"Message: " + se.Message,
					"Exception Caught",
					MessageBoxButtons.OK,
					MessageBoxIcon.Error);
			}
			else
			{
				return MessageBox.Show(
					"Exception caught by Client: " + e.GetType() + "\r\n\r\n" +
					"Source: " + e.Source + "\r\n\r\n" +
					"Message: " + e.Message,
					"Exception Caught",
					MessageBoxButtons.OK,
					MessageBoxIcon.Error);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="name"></param>
		/// <param name="board"></param>
		/// <exception cref="ArgumentNullException">Throws ArgumentNullException if either parameter is null.</exception>
		public Client(string name, Board2 board)
		{
			if(name != null)
			{
				this.name = name;
			}
			else
			{
				throw new ArgumentNullException("name");
			}
			
			if(board != null)
			{
				this.Board = board;
			}
			else
			{
				throw new ArgumentNullException("board");
			}
                
			// Linger for 3 seconds on close.
			LingerOption lingerOption = new LingerOption(true, 3);

			this.socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
			socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
		}

		#endregion

		#region Public data members

		public delegate void ClientEventHandler(object sender, ClientEventArgs e);
		public event ClientEventHandler EventHandler;

		public Board2 Board;

		public Tile MyTile
		{
			get
			{
				if(MyPlayer != null)
				{
					return this.Board.GetTile(this.MyPlayer.Position.X, this.MyPlayer.Position.Y);
				}
				else
				{
					throw new Exception("Doesn't exist yet.");
				}
			}
		}

		public enum MoveStates { ChooseStart, ChooseEnd, Complete };

		#endregion
				
		/// <summary>
		/// Delegate functions control events. This function acts as a bridge.
		/// </summary>
		/// <param name="e">Client event type.</param>
		protected virtual void PostEvent(ClientEventArgs e)
		{
			if (EventHandler != null)
				EventHandler(this, e);
		}

		#region Accessor methods

		public MoveStates MoveState
		{
			get
			{
				return myState;
			}
			set
			{
				myState = value;
			}
		}

		public bool Connected
		{
			get
			{
				return this.socket.Connected;
			}
		}

		public int PlayerNumber
		{
			get
			{
				return this.playerNumber;
			}
		}

		public string Name
		{
			get
			{
				return this.name;
			}
		}

		public int RecievedMessages
		{
			get
			{
				return this.recievedMessages;
			}
			set
			{
				this.recievedMessages = value;
			}
		}

		#endregion

		#region Networking Methods
		#region Public networking methods

		public void Connect(IPEndPoint ep) 
		{ 
			IAsyncResult result;

			PostEvent(new ClientEventArgs(new Net.Messages.Say("Client: Attempting to connect to: " + ep.ToString())));

			this.socket.Connect(ep);
			SendLogin();
			result = this.socket.BeginReceive(this.buffer, 0, Net.Client.BUFFER_LENGTH, SocketFlags.None, new AsyncCallback(this.OnReceiveMsg), null);
			//result.AsyncWaitHandle.WaitOne();
		}

		public void SendLogin()
		{
			SendMessage("name " + this.name);
		}

		public void SendMessage(String msg) 
		{ 
			if(socket.Connected)
			{
				// Make sure message ends with period.
				if(msg.EndsWith(".") == false)
				{
					msg += ".";
				}

				byte[] buffer = Encoding.ASCII.GetBytes(msg);

				this.socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
			}
			else
			{
				// ignore
			}
		}

		#endregion

		#region Private networking methods

		private void OnReceiveMsg(IAsyncResult asyncResult)
		{ 
			try
			{
				if(socket.Connected == true)
				{
					RecievedMessages++;
					int length = socket.EndReceive(asyncResult);
					string msg = Encoding.ASCII.GetString(this.buffer,0,length);

					ProcessMessages(msg);

					socket.BeginReceive(this.buffer,0,this.buffer.Length,SocketFlags.None,new AsyncCallback(this.OnReceiveMsg),socket);
				}
				else
				{
					MessageBox.Show("Disconnected y'all");
				}
			}
			catch(Exception)
			{
				Disconnect();
				//MessageBox.Show("Error recieving message");
				//HandleException(e);
			}
		}

		public void Disconnect()
		{
			socket.Shutdown(SocketShutdown.Both);
			socket.Close();
		}

		#endregion



		/// <summary>
		/// Splits a string recieved via sockets into separate messages. The delimiter is '.'
		/// </summary>
		/// <param name="user">User who sent the message</param>
		/// <param name="message">A string of commands, delimited by '.'</param>
		protected void ProcessMessages(string message)
		{
			string [] messages = message.Split('.');

			foreach(string s in messages)
			{
				if(s.Length > 0)
				{
					if (HandleMessage(s) == MsgHandlerResult.Failure)
					{
						System.Windows.Forms.MessageBox.Show("The client recieved an invalid command from the server.\r\nMessage: " + s, "Invalid Server Command", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
					}
				}
			}
		}

		private MsgHandlerResult HandleMessage(string message) 
		{ 
			string response = "Recieved \"" + message + "\".";
			string [] words = message.Split(' ');

			string firstword = words[0].ToLower();

			if (firstword == "player" && words.Length > 1)
			{
				int val;
				try
				{
					val = System.Convert.ToInt32(words[1], 10);
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}
				
				switch(val)
				{
					case 1:
						this.playerNumber = val;
						this.MyPlayer = Board.Player1;
						this.Board.UpdateBFS(MyPlayer);
						PostEvent(new ClientEventArgs(new Net.Messages.Say("(" + this.playerNumber + "): Sending login...")));
						return MsgHandlerResult.Success;
					case 2:
						this.playerNumber = val;
						this.MyPlayer = Board.Player2;
						this.Board.UpdateBFS(MyPlayer);
						PostEvent(new ClientEventArgs(new Net.Messages.Say("(" + this.playerNumber + "): Sending login...")));
						return MsgHandlerResult.Success;
					default:
						return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "name" && words.Length >= 2)
			{
				int val;
				try
				{
					val = System.Convert.ToInt32(words[1], 10);
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}

				string newName = "";
				for(int i=2; i < words.Length; i++)
				{
					newName += words[i] + " ";
				}

				newName = newName.TrimEnd(' ');

				Player P = (val==1?this.Board.Player1:this.Board.Player2);

				P.Pawn.Name = newName;

				if(val == this.playerNumber)
				{
					this.name = newName;
				}
				else
				{
					this.opponentName = newName;
				}
			}
			else if (firstword == "keepalive")
			{
				this.SendMessage(message);
			}
			else if (firstword == "timeleft")
			{
				try
				{
					int val = System.Convert.ToInt32(words[1], 10);
					PostEvent(new ClientEventArgs(new Net.Messages.Timer(val)));
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "bye")
			{
				PostEvent(new ClientEventArgs(new Net.Messages.Say("Disconnected.")));
				Disconnect();
			}
			else if (message.ToLower() == "server full")
			{
				PostEvent(new ClientEventArgs(new Net.Messages.Say("Server was full.")));
				MessageBox.Show("Server was full.");
				Disconnect();
			}
			else if (firstword == "jump" && words.Length == 3)
			{
				int i;
				try
				{
					i = System.Convert.ToInt32(words[1], 10);
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}

				string s = words[2].ToLower();

				try
				{
					switch(s)
					{
						case "up":
							this.Board.JumpUp(i);
							break;
						case "down":
							this.Board.JumpDown(i);
							break;
						case "left":
							this.Board.JumpLeft(i);
							break;
						case "right":
							this.Board.JumpRight(i);
							break;
						default:
							return MsgHandlerResult.Failure;
					}

					return MsgHandlerResult.Success;
				}
				catch(Exception)
				{
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "move" && words.Length == 3)
			{
				int pn;
				try
				{
					pn = System.Convert.ToInt32(words[1], 10);
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}

				string s = words[2].ToLower();

				try
				{
					switch(s)
					{
						case "up":
							this.Board.MoveUp(pn);
							break;
						case "right":
							this.Board.MoveRight(pn);
							break;
						case "left":
							this.Board.MoveLeft(pn);
							break;
						case "down":
							this.Board.MoveDown(pn);
							break;
						default:
							return MsgHandlerResult.Failure;
					}

					return MsgHandlerResult.Success;
				}
				catch(Exception)
				{
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "placehwall" && words.Length == 5)
			{
				try
				{
					int pn = System.Convert.ToInt32(words[1], 10);
					int wx = System.Convert.ToInt32(words[2], 10);
					int wy = System.Convert.ToInt32(words[3], 10);
					int wi = System.Convert.ToInt32(words[4], 10);

					Board.PlaceWall(pn, wi, wx, wy, Wall.Orientations.Horizontal);
					return MsgHandlerResult.Success;
				}
				catch(Exception e)
				{
					PostEvent(new ClientEventArgs(new Net.Messages.Say("Uh oh: " + e)));
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "placevwall" && words.Length == 5)
			{
				try
				{
					int pn = System.Convert.ToInt32(words[1], 10);
					int wx = System.Convert.ToInt32(words[2], 10);
					int wy = System.Convert.ToInt32(words[3], 10);
					int wi = System.Convert.ToInt32(words[4], 10);

					Board.PlaceWall(pn, wi, wx, wy, Wall.Orientations.Vertical);
					return MsgHandlerResult.Success;
				}
				catch (Exception e)
				{
					PostEvent(new ClientEventArgs(new Net.Messages.Say("Uh oh: " + e)));
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "say" && words.Length > 1)
			{
				string m = message.Substring(4, message.Length-4);
				m = m.Trim();
				
				PostEvent(new ClientEventArgs(new Net.Messages.Say(m)));
				return MsgHandlerResult.Success;
			}
			else if (firstword == "playerturn" && words.Length == 2)
			{
				try
				{
					int val = System.Convert.ToInt32(words[1], 10);
					this.Board.UpdateBFS(this.Player);
					
					PostEvent(new ClientEventArgs(new Net.Messages.WhoseTurn(val, (val==1?Board.Player1.Pawn.Name:Board.Player2.Pawn.Name))));

					if(val == this.playerNumber)
					{
						this.myState = Client.MoveStates.ChooseStart;
					}
					else
					{
						this.ReleaseObject();
						this.myState = Client.MoveStates.Complete;
					}
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "winner" && words.Length == 2)
			{
				try
				{
					int w = System.Convert.ToInt32(words[1], 10);
					PostEvent(new ClientEventArgs(new Net.Messages.Winner(w, (w==1?Board.Player1.Pawn.Name:Board.Player2.Pawn.Name))));
				}
				catch (Exception)
				{
					return MsgHandlerResult.Failure;
				}
			}
			else if (firstword == "say" && message.Length > 4)
			{
				PostEvent(new ClientEventArgs(new Net.Messages.Say(message.Substring(4, message.Length-4))));
				return MsgHandlerResult.Success;
			}
			else
			{
				return MsgHandlerResult.Failure;
			}

			return MsgHandlerResult.Success;
		}

		#endregion

		#region Click-Handling regions

		public enum HoverState { Nothing, Invalid, OK };

		public HoverState Handle_MouseHover(int x, int y, ArrayList selectedObjects)
		{
			object o;
			if (selectedObjects.Count > 0)
			{
				o = selectedObjects[selectedObjects.Count-1]; // Pick object on top.
			}
			else
			{
				return HoverState.Nothing; // No object.
			}

			Board.RemoveHighlights();

			if(this.MoveState == MoveStates.ChooseStart)
			{
				if(IsMyPawn(o) && this.MoveState == MoveStates.ChooseStart)
				{
					return HoverState.OK;
				}
				else if (IsMyWall(o) && this.MoveState == MoveStates.ChooseStart)
				{
					Wall w = (Wall)o;
					if(w.Moved == false)
					{
						return HoverState.OK;
					}
					else
					{
						return HoverState.Nothing;
					}
				}
				else
				{
					return HoverState.Nothing;
				}
			}
			else
			{
				return HoverState.Nothing;
			}
		}

		public enum ClickState { Invalid, OK };

		public ClickState Handle_MouseClick(int x, int y, ArrayList selectedObjects)
		{
			object o;

			if(selectedObjects.Count > 0 || myState != MoveStates.ChooseStart) // Not at the right stage of clicking
			{
				o = selectedObjects[selectedObjects.Count-1]; // Pick object on top.
			}
			else
			{
				ReleaseObject();
				this.myState = MoveStates.ChooseStart;
				return ClickState.Invalid;
			}

			if(IsMyPawn(o) && this.MoveState == MoveStates.ChooseStart)
			{
				this.pickedObject = (Pawn)o;
				this.myState = MoveStates.ChooseEnd;
				return ClickState.OK;
			}
			else if (IsMyWall(o) && this.MoveState == MoveStates.ChooseStart)
			{
				Wall w = (Wall)o;
				if(w.Moved == false)
				{
					this.pickedObject = (Wall)o;
					this.myState = MoveStates.ChooseEnd;
					return ClickState.OK;
				}
				else
				{
					return ClickState.Invalid;
				}
			}
			else
			{
				ReleaseObject();
				this.myState = MoveStates.ChooseStart;
				return ClickState.Invalid;
			}
		}

		public enum ReleaseState {Invalid, OK};

		public ReleaseState Handle_MouseRelease(int x, int y, ArrayList selectedObjects)
		{
			object o=null;

			if(myState != MoveStates.ChooseEnd)
			{
				ReleaseObject();
				return ReleaseState.Invalid;
			}

			if(selectedObjects.Count > 0 || myState != MoveStates.ChooseEnd) // Not at the right stage of clicking
			{
				bool mouseOnTile=false;
				// Sort through selected objects by depth to find a tile
				for(int i=selectedObjects.Count-1; i >= 0; i--)
				{
					if(selectedObjects[i] is Tile)
					{
						o = selectedObjects[i];
						mouseOnTile = true;
						break;
					}
				}

				if(!mouseOnTile)
				{
					this.myState = MoveStates.ChooseStart;
					ReleaseObject();
					return ReleaseState.Invalid;
				}
			}

			Tile t = (Tile)o;

			if(IsMyWall(pickedObject))
			{
				Wall w = (Wall)pickedObject;
				try
				{
					Board.CanPlaceWall(PlayerNumber, w.Index, t.X, t.Y, w.Orientation);
					
					switch(w.Orientation)
					{
						case Wall.Orientations.Vertical:
							ReleaseObject();
							SendMessage("placevwall " + t.X + " " + t.Y + " " + w.Index);
							this.myState = MoveStates.Complete;
							break;
						case Wall.Orientations.Horizontal:
							ReleaseObject();
							SendMessage("placehwall " + t.X + " " + t.Y + " " + w.Index);
							this.myState = MoveStates.Complete;
							break;
					}

					return ReleaseState.OK;
				}
				catch (Exceptions.CommandException)
				{
					ReleaseObject();
					this.myState = MoveStates.ChooseStart;
					return ReleaseState.Invalid;
				}
			}
			else if (IsMyPawn(pickedObject)) // see if Move or Jump is valid
			{
				Tile q = Board.GetTile(Player.Position.X, Player.Position.Y);
				int u = q.X - t.X;
				int v = q.Y - t.Y;

				if(u == -1 && v == 0 && Board.CanMoveRight(PlayerNumber))
				{
					SendMessage("move right");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (u == 1 && v == 0 && Board.CanMoveLeft(PlayerNumber))
				{
					SendMessage("move left");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (v == -1 && u == 0 && Board.CanMoveUp(PlayerNumber))
				{
					SendMessage("move up");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (v == 1 && u == 0 && Board.CanMoveDown(PlayerNumber))
				{
					SendMessage("move down");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if(u == -2 && v == 0 && Board.CanJumpRight(PlayerNumber))
				{
					SendMessage("jump right");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (u == 2 && v == 0 && Board.CanJumpLeft(PlayerNumber))
				{
					SendMessage("jump left");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (v == -2 && u == 0 && Board.CanJumpUp(PlayerNumber))
				{
					SendMessage("jump up");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else if (v == 2 && u == 0 && Board.CanJumpDown(PlayerNumber))
				{
					SendMessage("jump down");
					MoveState = Client.MoveStates.Complete;
					ReleaseObject();
					return ReleaseState.OK;
				}
				else
				{
					MoveState = Client.MoveStates.ChooseStart;
					ReleaseObject();
					return ReleaseState.Invalid;
				}
			}
			else
			{
				MoveState = Client.MoveStates.ChooseStart;
				ReleaseObject();
				return ReleaseState.Invalid;
			}
		}

		private void ResetWall(Wall w)
		{
			if(Board.Player1.WallList.Contains(w))
			{
				w.Color = Geometry.Colors.Wallpaper1;
				w.MoveTo(Wall.Orientations.Horizontal, new Geometry.Point3Df(9 * 1.25f, (w.Index-1) * 1.25f - 0.25f, 0.16f ));
				w.MoveTowardDestination(0.999f);
			}
			else if(Board.Player2.WallList.Contains(w))
			{
				w.Color = Geometry.Colors.Wallpaper2;
				w.MoveTo(Wall.Orientations.Horizontal, new Geometry.Point3Df(-2.50f, (w.Index-1) * 1.25f - 0.25f, 0.16f ));
				w.MoveTowardDestination(0.999f);
			}
		}

		#region Handling Mouse Dragging
		
		public enum DragState { Invalid, CanMove, CanJump, CanPlaceWall };

		public DragState Handle_MouseDrag(int x, int y, ArrayList selectedObjects)
		{
			object o=null;

			if(selectedObjects.Count == 0 || pickedObject == null) // Not at the right stage of clicking
			{
				return DragState.Invalid;
			}
			else
			{
				bool mouseOnTile=false;
				// Sort through selected objects by depth to find a tile
				for(int i=selectedObjects.Count-1; i >= 0; i--)
				{
					if(selectedObjects[i] is Tile)
					{
						o = selectedObjects[i];
						mouseOnTile = true;
						break;
					}
				}

				if(!mouseOnTile)
				{
					return DragState.Invalid;
				}
			}
			
			Tile t = (Tile)o;

			Board.RemoveHighlights();

			if(IsMyWall(pickedObject)) // See if place wall is valid
			{
				Wall w = (Wall)pickedObject;
				try
				{
					Board.CanPlaceWall(PlayerNumber, w.Index, t.X, t.Y, w.Orientation);

					w.Color = Geometry.Colors.Green;
					w.Color.Alpha = 0.3f;

					switch(w.Orientation)
					{
						case Wall.Orientations.Vertical:
							w.MoveTo(w.Orientation, new Geometry.Point3Df(t.X * 1.25f + 1.0f, t.Y * 1.25f, 0.01f));
							//w.MoveTowardDestination(0.999f);
							w.Color.Alpha = 0.3f;
							break;
						case Wall.Orientations.Horizontal:
							w.MoveTo(w.Orientation, new Geometry.Point3Df(t.X * 1.25f, t.Y * 1.25f + 1.0f, 0.01f));
							//w.MoveTowardDestination(0.999f);
							w.Color.Alpha = 0.3f;
							break;
						default:
							break;
					}
					return DragState.CanPlaceWall;
				}
				catch(Exceptions.CommandException)
				{
					switch(w.Orientation)
					{
						case Wall.Orientations.Vertical:
							w.MoveTo(w.Orientation, new Geometry.Point3Df(t.X * 1.25f + 1.0f, t.Y * 1.25f, 0.01f));
							//w.MoveTowardDestination(0.999f);
							break;
						case Wall.Orientations.Horizontal:
							w.MoveTo(w.Orientation, new Geometry.Point3Df(t.X * 1.25f, t.Y * 1.25f + 1.0f, 0.01f));
							//w.MoveTowardDestination(0.999f);
							break;
						default:
							break;
					}
					return DragState.Invalid;
				}
			}
			else if (IsMyPawn(pickedObject)) // see if Move or Jump is valid
			{
				Tile q = Board.GetTile(Player.Position.X, Player.Position.Y);
				int u = q.X - t.X;
				int v = q.Y - t.Y;

				if(u == -1 && v == 0 && Board.CanMoveRight(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanMove;
				}
				else if (u == 1 && v == 0 && Board.CanMoveLeft(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanMove;
				}
				else if (v == -1 && u == 0 && Board.CanMoveUp(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanMove;
				}
				else if (v == 1 && u == 0 && Board.CanMoveDown(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanMove;
				}
				else if(u == -2 && v == 0 && Board.CanJumpRight(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanJump;
				}
				else if (u == 2 && v == 0 && Board.CanJumpLeft(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanJump;
				}
				else if (v == -2 && u == 0 && Board.CanJumpUp(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanJump;
				}
				else if (v == 2 && u == 0 && Board.CanJumpDown(PlayerNumber))
				{
					RemoveHighlightPathTo(t);
					HighlightPathTo(t);
					return DragState.CanJump;
				}
				else
				{
					RemoveHighlightPathTo(t);
					return DragState.Invalid;
				}
			}
			else
			{
				RemoveHighlightPathTo(t);
				return DragState.Invalid;
			}
		}

		private void HighlightPathTo(Tile t)
		{
			Node2 end = Board.BFS.GetNode(t);

			ArrayList path = Board.BFS.ShortestPath(end);

			foreach(Node2 n in path)
			{
				n.Tile.Color = Geometry.Colors.LimeJello;
				n.Tile.Color.Alpha = 0.3f;
			}
		}

		private void RemoveHighlightPathTo(Tile t)
		{
			Node2 end = Board.BFS.GetNode(t);

			ArrayList path = Board.BFS.ShortestPath(end);

			foreach(Node2 n in path)
			{
				n.Tile.Color = Geometry.Colors.Tiles;
			}
		}


		public void ResetPawn(Pawn P)
		{
			P.Color.Alpha = Geometry.Colors.Player1.Alpha;
		}

		public void ReleaseObject()
		{
			Board.RemoveHighlights();

			if(IsMyWall(pickedObject)) // See if place wall is valid
			{
				ResetWall((Wall)pickedObject);
				pickedObject = null;
			}
			else if (IsMyPawn(pickedObject))
			{
				ResetPawn((Pawn)pickedObject);
				pickedObject = null;
			}
			else
			{
				pickedObject = null;
			}
		}

		#endregion

		private bool IsMyPawn(object o)
		{
			if(o is Pawn && (Pawn)o == Player.Pawn)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		private bool IsMyWall(object o)
		{
			if(o is Wall && Player.WallList.Contains((Wall)o))
			{
				return true;
			}
			else
			{
				return false;
			}
		}


		#endregion
	}

	public class ClientEventArgs : EventArgs
	{
		public object stateObject;

		public ClientEventArgs(object stateObject)
		{
			this.stateObject = stateObject;
		}
	}
	
	#region Client Messages


	/*public class CommandLoggedIn : ClientCommand
	{
		private QcClient who;

		public QcClient Who
		{
			get
			{
				return this.who;
			}
		}

		public CommandLoggedIn(QcClient who)
		{
			this.who = who;
		}
	}*/

	/*public class CommandPlaceWall : ClientCommand
	{
		private int player;
		private Direction dir;

		public CommandPlaceWall(int player, WallOrientation orientation, int x, int y)
		{

		}
	}*/

/*	public class Say : TextMessage
	{
		public Say(string text) : base(text)
		{
			// nothing
		}
	}*/
	#endregion
}
