using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace WorldOfPeacecraft
{
	public class MapPanel : Panel
	{
		private const int TileSize = 32;
		private const int EntitySize = 10;
		private IBackend Backend;

		private string ImagesFolder = "textures/";
		private Image Dragon1;
		private Image Dragon2;
		private Image Dragon3;
		private Image Pferd1;
		private Image Pferd2;
		private Image Forest;
		private Image ForestStag;
		private Image ForestLeft;
		private Image ForestRight;
		private Image ForestTop;
		private Image ForestBottom;
		private Image Knight;
		private Image Walkable;
		private Image Flowers;
		private Image Water;
		private Image WaterInnerTopLeft;
		private Image WaterInnerTopRight;
		private Image WaterInnerBotLeft;
		private Image WaterInnerBotRight;
		private Image WallBase;
		private Image WallEdgeTopLeft;
		private Image WallEdgeTopRight;
		private Image WallEdgeBotRight;
		private Image WallEdgeBotLeft;
		private Image WallCornerTopLeft;
		private Image WallCornerTopRight;
		private Image WallCornerBotRight;
		private Image WallCornerBotLeft;
		private Image WallInnerTopLeft;
		private Image WallInnerTopRight;
		private Image WallInnerBotRight;
		private Image WallInnerBotLeft;
		private Image BeachTop;
		private Image BeachBottom;
		private Image BeachLeft;
		private Image BeachRight;
		private Image BeachTopLeft;
		private Image BeachTopRight;
		private Image BeachBottomLeft;
		private Image BeachBottomRight;
		private Image BeachInnerTopLeft;
		private Image BeachInnerTopRight;
		private Image BeachInnerBotLeft;
		private Image BeachInnerBotRight;
		private int FlowerCounter;

		public MapPanel (IBackend backend)
		{
			Backend = backend;
		}

		private Image RotateFlipImage(Image image, RotateFlipType type)
		{
			Image rotated = new Bitmap(image);
			rotated.RotateFlip (type);
			return rotated;
		}

		protected override void OnLayout (LayoutEventArgs levent)
		{
			ITile[,] map = Backend.GetMap ();
			if (map != null) {
				int mapWidth = (map.GetLength(0)) * TileSize;
				int mapHeight = (map.GetLength(1)) * TileSize;
				this.SetClientSizeCore(mapWidth, mapHeight);
			}
			base.OnLayout (levent);
		}

        private void board_KeyPress(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.A:
                    this.Backend.moveLeft();
                    break;
                case Keys.D:
                    this.Backend.moveRight();
                    break;
                case Keys.W:
                    this.Backend.moveUp();
                    break;
                case Keys.S:
                    this.Backend.moveDown();
                    break;
            }
        }

		protected override void OnMouseClick (MouseEventArgs e)
		{
			base.OnMouseClick (e);
			int targetX = e.X / TileSize;
			int targetY = e.Y / TileSize;
			Backend.MoveTo (targetX, targetY);
		}

		protected override void OnPaint (PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			lock (Backend) {
				PaintMap (g);
				PaintEntities (g);
			}
		}

		public void PaintMap (Graphics g)
		{
			ITile[,] map = Backend.GetMap ();
			if (map != null) {
				FlowerCounter = 0;
				for (int y = 0; y < map.GetLength(1); y++) {
					for (int x = 0; x < map.GetLength(0); x++) {
						PaintTile (g, map, x, y);
					}
				}
			}
			else {
				g.FillRectangle(new SolidBrush(Color.White), this.DisplayRectangle);
			}
		}

		public void PaintTile (Graphics g, ITile[,] map, int locx, int locy)
		{
			ITile tile = map [locx, locy];
			int x = locx * TileSize;
			int y = locy * TileSize;
			if (tile.IsWall ()) {
				PaintImage(g, x, y, WallBase);
				ITile top = GetTile(locx, locy - 1, map);
				ITile topright = GetTile(locx + 1, locy - 1, map);
				ITile right = GetTile(locx + 1, locy, map);
				ITile botright = GetTile(locx + 1, locy + 1, map);
				ITile bot = GetTile(locx, locy + 1, map);
				ITile botleft = GetTile(locx - 1, locy + 1, map);
				ITile left = GetTile(locx - 1, locy, map);
				ITile topleft = GetTile(locx - 1, locy - 1, map);
				int offset = TileSize - 12;
				DrawWallEdge(g, CountWater (left, topleft, top), x, y, 0, 0, WallEdgeTopLeft, WallCornerTopLeft, (left.IsWater() ? BeachLeft : BeachTop));
				DrawWallEdge(g, CountWater (top, topright, right), x, y, offset, 0, WallEdgeTopRight, WallCornerTopRight, (top.IsWater() ? BeachTop : BeachRight));
				DrawWallEdge(g, CountWater (right, botright, bot), x, y, offset, offset, WallEdgeBotRight, WallCornerBotRight, (right.IsWater() ? BeachRight : BeachBottom));
				DrawWallEdge(g, CountWater (bot, botleft, left), x, y, 0, offset, WallEdgeBotLeft, WallCornerBotLeft, (bot.IsWater () ? BeachBottom : BeachLeft));
			} else if (tile.IsWater ()) {
				PaintImage(g, x, y, Water);
				PaintBeachInnerEdge(g, locx, locy, x, y, map);
				PaintWallInnerEdge(g, locx, locy, x, y, map);
			}
			else {
				PaintImage (g, x, y, Walkable);
				bool beachPlaced = false;
				if (NullsafeIsWater(locx - 1, locy, map)) {
					PaintImage(g, x, y, BeachLeft);
					beachPlaced = true;
				}
				if (NullsafeIsWater(locx + 1, locy, map)) {
					PaintImage (g, x, y, BeachRight);
					beachPlaced = true;
				}
				if (NullsafeIsWater(locx, locy - 1, map)) {
					PaintImage (g, x, y, BeachTop);
					beachPlaced = true;
				}
				if (NullsafeIsWater(locx, locy + 1, map)) {
					PaintImage (g, x, y, BeachBottom);
					beachPlaced = true;
				}
				if (!beachPlaced)
				{
					if (locx > 0)
					{
						if (locy > 0 && map[locx - 1, locy - 1].IsWater())
						{
							PaintImage (g, x, y, BeachTopLeft);
						}
						else if (locy < map.GetLength(1) - 1 && map[locx - 1, locy + 1].IsWater())
						{
							PaintImage (g, x, y, BeachBottomLeft);
						}
					}
					if (locx < map.GetLength(0) - 1)
					{
						if (locy > 0 && map[locx + 1, locy - 1].IsWater())
						{
							PaintImage (g, x, y, BeachTopRight);
						}
						else if (locy < map.GetLength(1) - 1 && map[locx + 1, locy + 1].IsWater())
						{
							PaintImage (g, x, y, BeachBottomRight);
						}
					}

				}
				PaintWaterInnerEdge(g, locx, locy, x, y, map);
				PaintWallInnerEdge(g, locx, locy, x, y, map);
			}
			if (tile.IsForest ()) {
				if (tile.IsHuntable ()) {
					PaintImage (g, x, y, ForestStag);
				} else {
					PaintImage (g, x, y, Forest);
				}
				if (NullsafeIsForrest(locx - 1, locy, map)) {
					PaintImage(g, x, y, ForestLeft);
				}
				if (NullsafeIsForrest(locx + 1, locy, map)) {
					PaintImage (g, x, y, ForestRight);
				}
				if (NullsafeIsForrest(locx, locy - 1, map)) {
					PaintImage (g, x, y, ForestTop);
				}
				if (NullsafeIsForrest(locx, locy + 1, map)) {
					PaintImage (g, x, y, ForestBottom);
				}
			} else if (tile.IsWalkable()) {
				if (FlowerCounter == 4) {
					PaintImage (g, x, y, Flowers);
					FlowerCounter = 0;
				}
				else {
					FlowerCounter++;
				}
			}
		}

		private void PaintBeachInnerEdge (Graphics g, int locx, int locy, int x, int y, ITile[,] map)
		{
			bool topDry = GetTile (locx, locy - 1, map).IsWalkable() || GetTile (locx, locy - 1, map).IsWall();
			bool rightDry = GetTile (locx + 1, locy, map).IsWalkable() || GetTile (locx + 1, locy, map).IsWall();
			bool botDry = GetTile (locx, locy + 1, map).IsWalkable() || GetTile (locx, locy + 1, map).IsWall();
			bool leftDry = GetTile (locx - 1, locy, map).IsWalkable() || GetTile (locx - 1, locy, map).IsWall();
			if (leftDry) {
				if (topDry) 
					if (GetTile (locx - 1, locy - 1, map).IsWalkable() || GetTile (locx - 1, locy - 1, map).IsWall())
						PaintImage (g, x, y, BeachInnerTopLeft);
				if (botDry)
					if (GetTile (locx - 1, locy + 1, map).IsWalkable()|| GetTile (locx - 1, locy + 1, map).IsWall())
						PaintImage (g, x, y, BeachInnerBotLeft);
			}
			if (rightDry) {
				if (topDry)
					if (GetTile (locx + 1, locy - 1, map).IsWalkable()|| GetTile (locx + 1, locy - 1, map).IsWall())
						PaintImage (g, x, y, BeachInnerTopRight);
				if (botDry)
					if (GetTile (locx + 1, locy + 1, map).IsWalkable()|| GetTile (locx + 1, locy + 1, map).IsWall())
						PaintImage (g, x, y, BeachInnerBotRight);
			}
		}

		private void PaintWallInnerEdge (Graphics g, int locx, int locy, int x, int y, ITile[,] map)
		{
			bool topWall = NullsafeIsWall (locx, locy - 1, map);
			bool rightWall = NullsafeIsWall (locx + 1, locy, map);
			bool botWall = NullsafeIsWall (locx, locy + 1, map);
			bool leftWall = NullsafeIsWall (locx - 1, locy, map);
			if (leftWall) {
				if (topWall) 
					PaintImage (g, x, y, WallInnerTopLeft);
				if (botWall)
					PaintImage (g, x, y, WallInnerBotLeft);
			}
			if (rightWall) {
				if (topWall)
					PaintImage (g, x, y, WallInnerTopRight);
				if (botWall)
					PaintImage (g, x, y, WallInnerBotRight);
			}
		}

		private void PaintWaterInnerEdge (Graphics g, int locx, int locy, int x, int y, ITile[,] map)
		{
			bool topWater = NullsafeIsWater (locx, locy - 1, map);
			bool rightWater = NullsafeIsWater (locx + 1, locy, map);
			bool botWater = NullsafeIsWater (locx, locy + 1, map);
			bool leftWater = NullsafeIsWater (locx - 1, locy, map);
			if (leftWater) {
				if (topWater) 
					PaintImage (g, x, y, WaterInnerTopLeft);
				if (botWater)
					PaintImage (g, x, y, WaterInnerBotLeft);
			}
			if (rightWater) {
				if (topWater)
					PaintImage (g, x, y, WaterInnerTopRight);
				if (botWater)
					PaintImage (g, x, y, WaterInnerBotRight);
			}
		}

		private void DrawWallEdge (Graphics g, int water, int x, int y, int offsetx, int offsety, Image edgeImage, Image cornerImage, Image beachImage)
		{
			if (water == -1)
				PaintImage (g, x, y, edgeImage);
			else {
				Rectangle src = new Rectangle(0 + offsetx, 0 + offsety, 12, 12);
				Rectangle dst = new Rectangle(x + offsetx, y + offsety, 12, 12);
				if (water == 3) {
					g.DrawImage(Water, dst, src, GraphicsUnit.Pixel);
					PaintImage(g, x, y, cornerImage);
				}
				else {
					g.DrawImage(Walkable, dst, src, GraphicsUnit.Pixel);
					if (water > 0)
						g.DrawImage(beachImage, dst, src, GraphicsUnit.Pixel);
					PaintImage(g, x, y, cornerImage);
				}
			}
		}

		private static int CountWater (params ITile[] tiles)
		{
			int water = 0;
			foreach (ITile tile in tiles) {
				if (tile.IsWall())
					return -1;
				if (tile.IsWater())
					water++;
			}
			return water;
		}

		private bool NullsafeIsWater (int x, int y, ITile[,] map)
		{
			ITile tile = GetTile (x, y, map);
			if (tile == null)
				return false;
			return tile.IsWater();
		}

		private bool NullsafeIsWall (int x, int y, ITile[,] map)
		{
			ITile tile = GetTile (x, y, map);
			if (tile == null)
				return true;
			return tile.IsWall();
		}

		private bool NullsafeIsForrest (int x, int y, ITile[,] map)
		{
			ITile tile = GetTile (x, y, map);
			if (tile == null)
				return false;
			return tile.IsForest();
		}

		private static ITile GetTile (int x, int y, ITile[,] map)
		{
			if (x >= 0 && x < map.GetLength (0) && y >= 0 && y < map.GetLength (1)) {
				return map[x,y];
			}
			return Dummytile.GetInstance();
		}

		public void PaintEntities (Graphics g)
		{
			IEnumerable<IEntity> dragons = Backend.GetDragons ();
			IEnumerable<IEntity> players = Backend.GetPlayers ();
			foreach (IEntity dragon in dragons) {
				int id = dragon.GetId();
				Image image;
				if (Math.Abs (id % 50) == 0)
				{
					if (Math.Abs (id % 100) == 0) {
						image = Pferd1;
					}
					else {
						image = Pferd2;
					}

				}
				else
				{
					switch (Math.Abs(id % 3))
					{
					case 0:
						image = Dragon1;
						break;
					case 1:
						image = Dragon2;
						break;
					case 2:
						image = Dragon3;
						break;
					default:
						throw new Exception("There is no image for dragon id '" + id + "' (this should never happen)");
					}
				}
				PaintImage(g, dragon.GetX() * TileSize, dragon.GetY() * TileSize, image);
			}
			foreach (IEntity player in players) {
				PaintImage(g, player.GetX() * TileSize, player.GetY () * TileSize, Knight);
			}
		}

		public void PaintImage (Graphics g, int posx, int posy, Image image)
		{
			int x = posx + (TileSize - image.Width) / 2;
			int y = posy + (TileSize - image.Height) / 2;
			g.DrawImage(image, x, y, image.Width, image.Height);
		}
			
		public void LoadResources (){
			Dragon1 = Image.FromFile (ImagesFolder + "dragon1.png");
			Dragon2 = Image.FromFile (ImagesFolder + "dragon2.png");
			Dragon3 = Image.FromFile (ImagesFolder + "dragon3.png");
			Pferd1 = Image.FromFile (ImagesFolder + "pferd1.png");
			Pferd2 = Image.FromFile (ImagesFolder + "pferd2.png");
			Forest = Image.FromFile (ImagesFolder + "forest.png");
			ForestStag = Image.FromFile (ImagesFolder + "stag.png");
			ForestLeft = Image.FromFile (ImagesFolder + "forest_left.png");
			ForestRight = Image.FromFile (ImagesFolder + "forest_right.png");
			ForestTop = Image.FromFile (ImagesFolder + "forest_top.png");
			ForestBottom = Image.FromFile (ImagesFolder + "forest_bottom.png");
			Knight = Image.FromFile (ImagesFolder + "knight.png");
			Walkable = Image.FromFile (ImagesFolder + "walkable.jpg");
			Flowers = Image.FromFile(ImagesFolder + "flowers.png");
			BeachBottom = Image.FromFile (ImagesFolder + "beach.png");
			BeachLeft = RotateFlipImage (BeachBottom, RotateFlipType.Rotate90FlipNone);
			BeachTop = RotateFlipImage (BeachBottom, RotateFlipType.Rotate180FlipNone);
			BeachRight = RotateFlipImage (BeachBottom, RotateFlipType.Rotate270FlipNone);
			BeachBottomRight = Image.FromFile (ImagesFolder + "beachcorner.png");
			BeachBottomLeft = RotateFlipImage (BeachBottomRight, RotateFlipType.Rotate90FlipNone);
			BeachTopLeft = RotateFlipImage (BeachBottomRight, RotateFlipType.Rotate180FlipNone);
			BeachTopRight = RotateFlipImage (BeachBottomRight, RotateFlipType.Rotate270FlipNone);
			BeachInnerBotRight = Image.FromFile (ImagesFolder + "beachcorner_inner.png");
			BeachInnerBotLeft = RotateFlipImage (BeachInnerBotRight, RotateFlipType.Rotate90FlipNone);
			BeachInnerTopLeft = RotateFlipImage (BeachInnerBotRight, RotateFlipType.Rotate180FlipNone);
			BeachInnerTopRight = RotateFlipImage (BeachInnerBotRight, RotateFlipType.Rotate270FlipNone);
			WallBase = Image.FromFile(ImagesFolder + "wall_base.png");
			WallEdgeBotLeft = Image.FromFile (ImagesFolder + "wall_edge_botleft.png");
			WallEdgeBotRight = Image.FromFile (ImagesFolder + "wall_edge_botright.png");
			WallEdgeTopLeft = Image.FromFile (ImagesFolder + "wall_edge_topleft.png");
			WallEdgeTopRight = Image.FromFile (ImagesFolder + "wall_edge_topright.png");
			WallCornerBotLeft = Image.FromFile (ImagesFolder + "wall_corner_botleft.png");
			WallCornerBotRight = Image.FromFile (ImagesFolder + "wall_corner_botright.png");
			WallCornerTopLeft = Image.FromFile (ImagesFolder + "wall_corner_topleft.png");
			WallCornerTopRight = Image.FromFile (ImagesFolder + "wall_corner_topright.png");
			WallInnerBotLeft = Image.FromFile (ImagesFolder + "wall_inner_botleft.png");
			WallInnerBotRight = Image.FromFile (ImagesFolder + "wall_inner_botright.png");
			WallInnerTopLeft = Image.FromFile (ImagesFolder + "wall_inner_topleft.png");
			WallInnerTopRight = Image.FromFile (ImagesFolder + "wall_inner_topright.png");
			Water = Image.FromFile (ImagesFolder + "water.jpg");
			WaterInnerTopLeft = Image.FromFile (ImagesFolder + "water_topleft.png");
			WaterInnerTopRight = Image.FromFile (ImagesFolder + "water_topright.png");
			WaterInnerBotLeft = Image.FromFile (ImagesFolder + "water_botleft.png");
			WaterInnerBotRight = Image.FromFile (ImagesFolder + "water_botright.png");

		}

		public void InitializeComponents(){
			SetStyle (ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
			this.PreviewKeyDown += board_KeyPress;
		}
	}
}

