using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace GraveyardGraph
{
	public class Map
	{
		public static readonly Vector2 MAP_START = new Vector2(64, 64);
		public static readonly int WAVEGEN_OFFSET = 2;

		private Player _player;
		public Player Player
		{
			get { return _player; }
			set { _player = value; }
		}

		private Goal _goal;
		public Goal Goal
		{
			get { return _goal; }
			set { _goal = value; }
		}

		private List<TileWall> _tileWalls = new List<TileWall>();
		public List<TileWall> TileWalls
		{
			get { return _tileWalls; }
		}

		private List<ColliderWall> _colliderWalls = new List<ColliderWall>();
		public List<ColliderWall> ColliderWalls
		{
			get { return _colliderWalls; }
		}

		public int height;
		public int width;

		//q
		public WaveGenerator TopLWaveGen;
		//p
		public WaveGenerator TopRWaveGen;
		//z
		public WaveGenerator BotLWaveGen;
		//m
		public WaveGenerator BotRWaveGen;

		private Map() { }

		public static Map LoadMap(String filename)
		{
			Map map = new Map();

			List<char[]> charList = new List<char[]>();
			StreamReader mapStream = new StreamReader(TitleContainer.OpenStream("Content/Maps/" + filename));

			while (!mapStream.EndOfStream)
			{
				string line = mapStream.ReadLine();
				if (line == "")
				{
					continue;
				}
				charList.Add(line.ToCharArray());
			}

			char[][] chars = charList.ToArray();

			for (int row = 0; row < chars.Length; row++)
			{
				int wallDrawingStartsAt = -1;
				for (int col = 0; col < chars[row].Length; col++)
				{
					char cur = chars[row][col];
					switch (cur)
					{
						case ' ':
							break;
						case 'p':
							map.Player = new Player(MAP_START + (Game1.TILE_SIZE * new Vector2(col + 0.5f, row + 0.5f)));
							break;
						case 'g':
							map.Goal = new Goal(MAP_START + (Game1.TILE_SIZE * new Vector2(col + 0.5f, row + 0.5f)));
							break;
						case '#':
							char above = ' ', below = ' ', left = ' ', right = ' ';
							if (row > 0)
							{
								above = chars[row - 1][col];
							}
							if (row < chars.Length - 1)
							{
								below = chars[row + 1][col];
							}
							if (col > 0)
							{
								left = chars[row][col - 1];
							}
							if (col < chars[row].Length - 1)
							{
								right = chars[row][col + 1];
							}
							Vector2 center = MAP_START + (Game1.TILE_SIZE * new Vector2(col + 0.5f, row + 0.5f));
							TileWall tile = new TileWall(center, above, below, left, right);
							map.TileWalls.Add(tile);
							break;
					}

					//also handle the first pass of creating the wall colliders
					if (row < chars.Length - 1)
					{
						bool hereIsWall = cur == '#';
						bool belowMeIsWall = chars[row + 1][col] == '#';
						if (hereIsWall != belowMeIsWall)
						{
							if (wallDrawingStartsAt == -1)
							{
								wallDrawingStartsAt = col;
							}
						}
						else {
							if (wallDrawingStartsAt != -1)
							{
								Vector2 a = MAP_START + (Game1.TILE_SIZE * new Vector2(wallDrawingStartsAt, row + 1));
								Vector2 b = MAP_START + (Game1.TILE_SIZE * new Vector2(col, row + 1));
								//TODO this might draw walls backwards, might have to flip A and B
								if (chars[row + 1][col - 1] == '#')
								{
									Vector2 tmp = b;
									b = a;
									a = tmp;
								}
								ColliderWall wall = new ColliderWall(a, b);
								map.ColliderWalls.Add(wall);
								wallDrawingStartsAt = -1;
							}
						}
					}
				}
				if (wallDrawingStartsAt != -1)
				{
					Vector2 a = MAP_START + (Game1.TILE_SIZE * new Vector2(wallDrawingStartsAt, row + 1));
					Vector2 b = MAP_START + (Game1.TILE_SIZE * new Vector2(chars[row].Length, row + 1));
					//TODO this might draw walls backwards, might have to flip A and B
					ColliderWall wall = new ColliderWall(a, b);
					map.ColliderWalls.Add(wall);
					wallDrawingStartsAt = -1;
				}
			}

			//generate the rest of the ColliderWalls
			for (int col = 0; col < chars[0].Length - 1; col++)
			{
				int wallDrawingStartsAt = -1;
				for (int row = 0; row < chars.Length; row++)
				{
					bool hereIsWall = chars[row][col] == '#';
					bool belowMeIsWall = chars[row][col+1] == '#';
					if (hereIsWall != belowMeIsWall)
					{
						if (wallDrawingStartsAt == -1)
						{
							wallDrawingStartsAt = row;
						}
					}
					else {
						if (wallDrawingStartsAt != -1)
						{
							Vector2 a = MAP_START + (Game1.TILE_SIZE * new Vector2(col + 1, wallDrawingStartsAt));
							Vector2 b = MAP_START + (Game1.TILE_SIZE * new Vector2(col + 1, row));
							//TODO this might draw walls backwards, might have to flip A and B
							if (chars[row - 1][col + 1] != '#')
							{
								Vector2 tmp = b;
								b = a;
								a = tmp;
							}
							ColliderWall wall = new ColliderWall(a, b);
							map.ColliderWalls.Add(wall);
							wallDrawingStartsAt = -1;
						}
					}
				}
				if (wallDrawingStartsAt != -1)
				{
					Vector2 a = MAP_START + (Game1.TILE_SIZE * new Vector2(col + 1, wallDrawingStartsAt));
					Vector2 b = MAP_START + (Game1.TILE_SIZE * new Vector2(col + 1, chars.Length));
					//TODO this might draw walls backwards, might have to flip A and B
					ColliderWall wall = new ColliderWall(a, b);
					map.ColliderWalls.Add(wall);
					wallDrawingStartsAt = -1;
				}
			}

			//Not drawing outside walls - all map files will have seaweed borders.

			map.height = chars.Length * Game1.TILE_SIZE;
			map.width = chars[0].Length * Game1.TILE_SIZE;

			//Add the wave controllers
			map.TopLWaveGen = new WaveGenerator(MAP_START - new Vector2(WAVEGEN_OFFSET), ArtFetcher.WaveGenerator);
			map.TopRWaveGen = new WaveGenerator(MAP_START + new Vector2(map.width + WAVEGEN_OFFSET, -1 * WAVEGEN_OFFSET), ArtFetcher.WaveGeneratorP);
			map.BotLWaveGen = new WaveGenerator(MAP_START + new Vector2(-1 * WAVEGEN_OFFSET, map.height + WAVEGEN_OFFSET), ArtFetcher.WaveGeneratorZ);
			map.BotRWaveGen = new WaveGenerator(MAP_START + new Vector2(map.width + WAVEGEN_OFFSET, map.height + WAVEGEN_OFFSET), ArtFetcher.WaveGeneratorM);

			return map;
		}
	}
}
