using System;
using System.Collections;
using Tao.Platform;
using Tao.OpenGl;
using QCGM.Geometry;
using QCGM.Graphs2;
//using QCGM.Graphs.Dijkstras;
using QCGM.Net;

namespace QCGM.Boards
{
	public class Board2 : IDrawable
	{
		protected Player [] MyPlayers = new Player[2];

		protected Tile [,] MyTiles = new Tile[9,9];
		protected Wall [] MyWalls = new Wall[20];

		public static ArrayList nameStack = new ArrayList();

		protected BreadthFirstSearch MyBFS;
		protected Graph2 G;

		protected Player BFSP = null;

		public bool someoneWon=false;

		public BreadthFirstSearch BFS
		{
			get
			{
				return this.MyBFS;
			}
		}

		public void UpdateBFS(Player P)
		{
			this.BFSP = P;

			this.G = BuildGraph(P);
			Node2 n = this.G.GetNode(this.GetTile(P.Position.X, P.Position.Y));
			if( n == null)
			{
				throw new Exception("Could not find tile!!");
			}
			else
			{
				this.MyBFS = new BreadthFirstSearch(this.G);
				this.MyBFS.Perform(n);
			}
		}


		#region Players

		public Player Player1
		{
			get
			{
				return MyPlayers[0];
			}
		}

		public Player Player2
		{
			get
			{
				return MyPlayers[1];
			}
		}
		#endregion

		#region Constructor / Destructor

		public Board2()
		{
			ResetBoard();
		}

		#endregion

		#region Methods That Manipulate The Board

		public void UpdateNamestack()
		{
			Geometry.Namestack.al.Add(this);
			Geometry.Namestack.al.Add(Player1.Pawn);
			Geometry.Namestack.al.Add(Player2.Pawn);

			for(int i=0; i < 9; i++)
			{
				for(int j=0; j < 9; j++)
				{
					Geometry.Namestack.al.Add(MyTiles[i,j]);
				}
			}

			for(int i=0; i < 20; i++)
			{
				Geometry.Namestack.al.Add(MyWalls[i]);
			}
		}

		/// <summary>
		/// Populates the board with new tiles and players.
		/// </summary>
		public void ResetBoard()
		{
			someoneWon = false;

			MyPlayers[0] = new Player(Colors.Player1, new Point2Di(4, 0), "Player 1");
			MyPlayers[1] = new Player(Colors.Player2, new Point2Di(4, 8), "Player 2");

			MyPlayers[0].MoveTo(4, 0);
			MyPlayers[0].Pawn.MoveTowardDestination(0.999f);
			MyPlayers[1].MoveTo(4, 8);
			MyPlayers[1].Pawn.MoveTowardDestination(0.999f);

			//Hashtable
			Geometry.Namestack.al.Clear();
			Geometry.Namestack.al.Add(this);
			//Geometry.Namestack.ht.Add(new UInt16(0), this); // add the board

			Geometry.Namestack.al.Add(Player1.Pawn);
			Geometry.Namestack.al.Add(Player2.Pawn);

			for(int i=0; i < 9; i++)
			{
				for(int j=0; j < 9; j++)
				{
					MyTiles[i,j] = new Tile(i, j, new Point3Df((float)i * 1.25f, (float)j * 1.25f, 0.01f), new Point3Df(1.0f, 1.0f, 0.15f) );
					//MyTiles[i,j].Color = Colors.Gray;
					MyTiles[i,j].WallUp = false;
					MyTiles[i,j].WallRight = false;
					MyTiles[i,j].Player = PlayerContents.Empty;

					Geometry.Namestack.al.Add(MyTiles[i,j]);
				}
			}

			for(int i=0; i < 10; i++)
			{
				MyWalls[i] = new Wall(new Point3Df(-2.50f, i * 1.25f - 0.25f, 0.16f ), i+1, Colors.Wallpaper2);
				Player2.WallList.Add(MyWalls[i]);
				//Geometry.Namestack.ht.Add( 90+i, MyWalls[i]); // add player 1's walls
				Geometry.Namestack.al.Add(MyWalls[i]);
			}

			for(int i=0; i < 10; i++)
			{
				MyWalls[10+i] = new Wall(new Point3Df(9 * 1.25f, i * 1.25f - 0.25f, 0.16f ), i+1, Colors.Wallpaper1);
				Player1.WallList.Add(MyWalls[10+i]);
				//Geometry.Namestack.ht.Add(100+i, MyWalls[10+i]); // add player 2's walls
				Geometry.Namestack.al.Add(MyWalls[10+i]);
			}

			UpdateBFS(MyPlayers[0]);

		}

		public bool Update(float t)
		{
			bool update = false;

			if(Player1.Pawn.MoveTowardDestination(t) == true)
			{
				update = true;
			}
			
			if(Player2.Pawn.MoveTowardDestination(t) == true)
			{
				update = true;
			}

			foreach(Wall W in MyWalls)
			{
				if(W.MoveTowardDestination(t))
				{
					update = true;
				}
			}

			foreach(Tile tile in this.MyTiles)
			{
				if(tile.UpdateMe)
				{
					update = true;
				}
			}

			return update;
		}
		#region Graphing Methods
		
		public Tile GetTile(int x, int y)
		{
			if(x < 0 || y < 0 || x > 8 || y > 8)
			{
				throw new Exception("Invalid tile: " + x + ", " + y);
			}
			else
			{
				return MyTiles[x,y];
			}
		}
		
		/// <summary>
		/// Creates and returns a new graph from the current state of the board for use with pathfinding algorithms.
		/// </summary>
		/// <returns>A new Graph based off the state of the board</returns>
		public Graph2 BuildGraph(Player P)
		{
			Node2 [,] nodes = new Node2[9,9];

			for(int i=0; i < 9; i++)
			{
				for(int j=0; j < 9; j++)
				{
					nodes[i,j] = new QCGM.Graphs2.Node2(MyTiles[i,j]);
				}
			}
		
			Graph2 G = new Graph2();

			for(int x=0; x < 9; x++)
			{
				for(int y=0; y < 9; y++)
				{
					if(y < 8)
					{
						Tile t = nodes[x,y].Tile;
						nodes[x,y].SetNeighbor(Node2.NUP, nodes[x,y+1], (t.WallUp ? 1000 : 1));
					}
					if(y > 0)
					{
						Tile t = nodes[x,y-1].Tile;
						nodes[x,y].SetNeighbor(Node2.NDOWN, nodes[x,y-1], (t.WallUp ? 1000 : 1));
					}
					if(x > 0)
					{
						Tile t = nodes[x,y].Tile;
						nodes[x,y].SetNeighbor(Node2.NLEFT, nodes[x-1,y], (t.WallRight ? 1000 : 1));
					}
					if(x < 8)
					{
						Tile t = nodes[x+1,y].Tile;
						nodes[x,y].SetNeighbor(Node2.NRIGHT, nodes[x+1,y], (t.WallRight ? 1000 : 1));
					}

					G.AddNode(nodes[x,y]);
				}
			}

			for(int x=0; x < 9; x++)
			{
				for(int y=0; y < 9; y++)
				{
					Tile T = (Tile)nodes[x,y].Tile;
					if (T.WallRight)
					{
						nodes[x,y].RemoveNeighbor(Node2.NRIGHT);
						nodes[x+1,y].RemoveNeighbor(Node2.NLEFT);
					}
					if (T.WallUp)
					{
						nodes[x,y].RemoveNeighbor(Node2.NUP);
						nodes[x,y+1].RemoveNeighbor(Node2.NDOWN);
					}
				}
			}

			return G;
		}

		#endregion

		#region Move Methods

		#region Methods that move players

		public void MoveUp(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);
			if(CanMoveUp(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X, P.Position.Y+1);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void MoveDown(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);
			if(CanMoveDown(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X, P.Position.Y-1);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void MoveLeft(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);
			if(CanMoveLeft(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X-1, P.Position.Y);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void MoveRight(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);
			if(CanMoveRight(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X+1, P.Position.Y);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		#endregion

		#region Methods that test if players can move

		public bool CanMoveUp(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);

			Tile a, b;

			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X, P.Position.Y+1);
			}
			catch(Exception)
			{
				return false;
			}

			if(P.Position.Y < 8 && b.Player == PlayerContents.Empty && a.WallUp == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanMoveLeft(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);

			Tile b;

			try
			{
				b = GetTile(P.Position.X-1, P.Position.Y);
			}
			catch(Exception)
			{
				return false;
			}

			if(P.Position.X > 0 && b.Player == PlayerContents.Empty && b.WallRight == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanMoveRight(int pn)
		{
			Player P = this.GetPlayerFromNumber(pn);

			Tile a, b;

			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X+1, P.Position.Y);
			}
			catch(Exception)
			{
				return false;
			}

			if(P.Position.X < 8 && b.Player == PlayerContents.Empty && a.WallRight == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanMoveDown(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			Tile b;

			try
			{
				b = GetTile(P.Position.X, P.Position.Y-1);
			}
			catch(Exception)
			{
				return false;
			}

			if(P.Position.Y > 0 && b.Player == PlayerContents.Empty && b.WallUp == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		#endregion

		#endregion

		#region Jump Methods

		#region Methods that test validity of jumps

		public bool CanJumpUp(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			Tile a, b;

			if (P.Position.Y > 6)
			{
				return false;
			}
		
			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X, P.Position.Y+1);
				GetTile(P.Position.X, P.Position.Y+2);
			}
			catch (Exception)
			{
				return false; // Adjacent tile not there
			}

			if(b.Player == (pn == 1 ? PlayerContents.Player2 : PlayerContents.Player1) && a.WallUp == false && b.WallUp == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanJumpDown(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			Tile a;
			Tile b;
			Tile c;

			if (P.Position.Y < 2)
			{
				return false;
			}
		
			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X, P.Position.Y-1);
				c = GetTile(P.Position.X, P.Position.Y-2);
			}
			catch (Exception)
			{
				return false; // Adjacent tile not there
			}

			if(b.Player == PlayerContents.Empty)
			{
				return false;
			}
			else if(b.Player == (pn == 1 ? PlayerContents.Player2 : PlayerContents.Player1) && b.WallUp == false && c.WallUp == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanJumpLeft(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			Tile a;
			Tile b;
			Tile c;

			if (P.Position.X < 2)
			{
				return false;
			}
		
			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X-1, P.Position.Y);
				c = GetTile(P.Position.X-2, P.Position.Y);
			}
			catch (Exception)
			{
				return false; // Adjacent tile not there
			}

			if(b.Player == (pn == 1 ? PlayerContents.Player2 : PlayerContents.Player1) && b.WallRight == false && c.WallRight == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public bool CanJumpRight(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			Tile a;
			Tile b;

			if (P.Position.Y < 2)
			{
				return false;
			}
		
			try
			{
				a = GetTile(P.Position.X, P.Position.Y);
				b = GetTile(P.Position.X+1, P.Position.Y);
				GetTile(P.Position.X+2, P.Position.Y);
			}
			catch (Exception)
			{
				return false; // Adjacent tile not there
			}

			if(b.Player == (pn == 1 ? PlayerContents.Player2 : PlayerContents.Player1) && a.WallRight == false && b.WallRight == false)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		#endregion

		#region Methods that perform jumps

		public void JumpRight(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			
			if(CanJumpRight(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X + 2, P.Position.Y);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void JumpLeft(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			
			if(CanJumpLeft(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X - 2, P.Position.Y);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void JumpUp(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			
			if(CanJumpUp(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X, P.Position.Y+2);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
		}

		public void JumpDown(int pn)
		{
			Player P = GetPlayerFromNumber(pn);
			
			if(CanJumpDown(pn))
			{
				GetTile(P.Position.X, P.Position.Y).Player = PlayerContents.Empty;
				P.MoveTo(P.Position.X, P.Position.Y-2);
				GetTile(P.Position.X, P.Position.Y).Player = (pn == 1 ? PlayerContents.Player1 : PlayerContents.Player2);
			}
			else
			{
				throw new Exception("Failed.");
			}
		}

		#endregion

		#endregion

		#region Methods For Testing and Placing Walls

		public bool CanPlaceWall(int player, int wn, int x, int y, Wall.Orientations Orient)
		{
			Player P = this.GetPlayerFromNumber(player);

			if(!(wn > 0 && wn <= 10))
			{
				throw new Exceptions.CommandException("This wall (wall " + wn + ") should not exist. Walls range from 1 - 10.");
			}

			if(P.WallsRemaining <= 0)
			{
				throw new Exceptions.CommandException("Player has " + P.WallsRemaining + " walls left.");
			}

			Wall W = (Wall)P.WallList[wn-1];

			if(W.Moved)
			{
				throw new Exceptions.CommandException("This wall has already been moved, and cannot be moved again.");
			}

			Tile a, b;

			switch(Orient)
			{
				case Wall.Orientations.Vertical:
					if(y == 8 || x == 8)
					{
						throw new Exceptions.CommandException("Placing a vertical wall at " + x + ", " + y + " would place the wall off the board.");
					}
					a = GetTile(x, y);
					b = GetTile(x, y+1);

					if((a.WallUp && !a.AnnexUp) || a.WallRight || b.WallRight)
					{
						throw new Exceptions.CommandException("Placing a vertical wall at " + x + ", " + y + " would collide with another wall.");
					}

					if(PreventsVictory(x, y, Orient))
					{
						throw new Exceptions.CommandException("Placing a vertical wall at " + x + ", " + y + " would prevent at least one player from winning.");
					}
					
					return true;
				case Wall.Orientations.Horizontal:
					if(x == 8 || y == 8)
					{
						throw new Exceptions.CommandException("Placing a horizontal wall at " + x + ", " + y + " would place the wall off the board.");
					}
					a = GetTile(x, y);
					b = GetTile(x+1, y);
					
					if ((a.WallRight && !a.AnnexRight) || a.WallUp || b.WallUp)
					{
						throw new Exceptions.CommandException("Placing a horizontal wall at " + x + ", " + y + " would collide with another wall.");
					}

					if(PreventsVictory(x, y, Orient))
					{
						throw new Exceptions.CommandException("Placing a horizontal wall at " + x + ", " + y + " would collide with another wall.");
					}

					return true;
			}
			throw new Exceptions.CommandException("Tried to place a wall at " + x + ", " + y + " with an unknown orientation!");
		}

		public bool PreventsVictory(int x, int y, Wall.Orientations orient)
		{
			Tile a, b;
			bool q, r, s, t, l, m, n, o;

			try
			{
				a = GetTile(x, y);
				if(orient == Wall.Orientations.Horizontal)
				{
					b = GetTile(x+1, y);
				}
				else
				{
					b = GetTile(x, y+1);
				}
			}
			catch (Exception)
			{
				return false;
			}

			q = a.WallUp;
			r = a.AnnexUp;
			s = a.WallRight;
			t = a.AnnexRight;
			
			l = b.WallUp;
			m = b.AnnexUp;
			n = b.WallRight;
			o = b.AnnexRight;

			bool res = false;
			Player P = this.BFSP;

			switch(orient)
			{
				case Wall.Orientations.Horizontal:
					a.WallUp = true;
					a.AnnexUp = false;
					b.WallUp = true;
					b.AnnexUp = true;

					if(!PathToVictoryExists(this.Player1) || !PathToVictoryExists(this.Player2))
					{
						res = true;
					}
					this.UpdateBFS(P);

					a.WallUp = q;
					a.AnnexUp = r;
					a.WallRight = s;
					a.AnnexRight = t;

					b.WallUp = l;
					b.AnnexUp = m;
					b.WallRight = n;
					b.AnnexRight = o;

					return res;

				case Wall.Orientations.Vertical:
					a.WallRight = true;
					a.AnnexRight = false;
					b.WallRight = true;
					b.AnnexRight = true;

					if(!PathToVictoryExists(this.Player1) || !PathToVictoryExists(this.Player2))
					{
						res = true;
					}
					
					this.UpdateBFS(P);
					a.WallUp = q;
					a.AnnexUp = r;
					a.WallRight = s;
					a.AnnexRight = t;

					b.WallUp = l;
					b.AnnexUp = m;
					b.WallRight = n;
					b.AnnexRight = o;
					return res; // bad news

				default:
					return false;
			}
		}

		public bool PathToVictoryExists(Player P)
		{
			this.UpdateBFS(P);

			Tile t;
			Node2 n;
			
			if(P == this.Player1)
			{
				for(int i=0; i < 9; i++)
				{
					t = GetTile(i, 8);
					n = BFS.GetNode(t);

					if(this.BFS.CostOfTravel(n) != BreadthFirstSearch.Infinity)
					{
						return true;
					}
				}
				return false;
			}
			else if (P == this.Player2)
			{
				for(int i=0; i < 9; i++)
				{
					t = GetTile(i, 0);
					n = BFS.GetNode(t);

					if(this.BFS.CostOfTravel(n) != BreadthFirstSearch.Infinity)
					{
						return true;
					}
				}
				return false;
			}
			else
			{
				return false;
			}
		}


		public void PlaceWall(int player, int wn, int x, int y, Wall.Orientations Orient)
		{
			Player P;

			if(player == 1)
			{
				P = Player1;
			}
			else if (player == 2)
			{
				P = Player2;
			}
			else
			{
				throw new Exception("Placing wall: Invalid player");
			}

			if(!(wn > 0 && wn <= 10))
			{
				throw new Exception("Placing wall: Invalid wall");
			}

			if(P.WallsRemaining > 0 == false)
			{
				throw new Exception("Player has not enough walls!");
			}

			Wall W = (Wall)P.WallList[wn-1];

			Tile a, b;

			if (Orient == Wall.Orientations.Vertical)
			{
				if(y > 7 || x == 8)
				{
					throw new Exception("Placing wall: Invalid location, would put wall off the board");
				}
				a = GetTile(x, y);
				b = GetTile(x, y+1);

				if((a.WallUp && !a.AnnexUp) || a.WallRight || b.WallRight)
				{
					throw new Exception("Placing wall: Invalid location, would collide with other wall");
				}
				
				try
				{
					W.MoveTo(Orient, new Geometry.Point3Df(x * 1.25f + 1.0f, y * 1.25f, 0.01f));
					//W.MoveTowardDestination(0.999f);
				}
				catch (Exception e)
				{
					throw new Exception("Placing wall: Exception: " + e);
				}

				Tile t;
				t = GetTile(x, y);
				t.WallRight = true;
				t.AnnexRight = false;
				
				t = GetTile(x, y+1);
				t.WallRight = true;
				t.AnnexRight = true;

				W.Moved = true;
				P.WallsRemaining--;
			}
			else if (Orient == Wall.Orientations.Horizontal)
			{
				if(x > 7 || y == 8)
				{
					throw new Exception("Placing wall: Invalid location, would put wall off the board");
				}
				a = GetTile(x, y);
				b = GetTile(x+1, y);
				
				if ((a.WallRight && !a.AnnexRight) || a.WallUp || b.WallUp)
				{
					throw new Exception("Placing wall: Invalid location, would put wall off the board");
				}

				try
				{
					W.MoveTo(Orient, new Geometry.Point3Df(x * 1.25f, y * 1.25f + 1.0f, 0.01f));
					//W.MoveTowardDestination(0.999f);
				}
				catch (Exception e)
				{
					throw new Exception("Placing wall: Exception: " + e);
				}

				Tile t;
				t = GetTile(x, y);
				t.WallUp = true;
				t.AnnexUp = false;

				t = GetTile(x+1, y);
				t.WallUp = true;
				t.AnnexUp = true;
				
				W.Moved = true;
				P.WallsRemaining--;
			}
		}

		#endregion

		public Player GetPlayerFromNumber(int pn)
		{
			switch(pn)
			{
				case 1:
					return this.Player1;
				case 2:
					return this.Player2;
				default:
					throw new Exception("Invalid player number: " + pn);
			}
		}

		public void RemoveHighlights()
		{
			foreach(Tile t in this.MyTiles)
			{
				t.Color = Geometry.Colors.Tiles;
			}
		}

		#endregion

		#region IDrawable Members

		public void Draw()
		{
			int i = Geometry.Namestack.al.IndexOf(this);
			Gl.glPushName( i ); // picking
			Colors.SetGlColor(Colors.Board);
			Shapes.DrawCube(new Point3Df(-1.0f, -1.0f, 0.0f - 0.26f), new Point3Df(13.25f, 13.25f, 0.25f));
			Gl.glPopName();

			foreach(Tile T in MyTiles)
			{
				T.Draw();
			}

			foreach(Player P in MyPlayers)
			{
				P.Pawn.Draw();

				foreach(Wall W in P.WallList)
				{
					W.Draw();
				}
			}
		}

		#endregion
	}

	public enum Direction { Up, Down, Left, Right };
	public enum PlayerContents { Empty, Player1, Player2 };
	public enum WallContents { Empty, Right, Bottom, RightAndBottom };

	/// <summary>
	/// Graph nodes for the board contain NodeObjects. This object and its descendents contain information to draw
	/// the tile, and whatnot.
	/// </summary>
	public interface IDrawable
	{
		void Draw();
	}

}
