using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Gamelogic.Grids.Examples
{
	public class MonsterMansionHex : GLMonoBehaviour, IResetable
	{
		private const int GridStartSize = 3;
		private const int GridMaxSize = 9;
		private const float TickTime = .33f;
		private const float ResetDelay = 3f;

		private readonly Vector2 CellDimensions = new Vector2(74, 84) * 2;

		public MovementCell movementCellPrefab;
		public Player playerPrefab;
		public Monster monsterPrefab;
		public TextMesh endMessage;
		public GameObject gridRoot;
		public GameObject uiRoot;

		private int gridSize = 3;
		private bool canTick;
		private List<Monster> monsters;
		private PointyHexGrid<MovementCell> grid;
		private IMap<PointyHexPoint> map;
		private PointyHexPoint startPoint;
		private Player player;
		private bool gameOver;

		public void Reset()
		{
			endMessage.gameObject.SetActive(false);
			gridRoot.transform.DestroyChildren();
			monsters = new List<Monster>();
			canTick = true;

			BuildGrid();
			BuildMap();
			CreateCells();
			InitGame();

			gameOver = false;
		}

		public void Start()
		{
			Reset();
		}

		public void Update()
		{
			//CheckDebugInput();
			if (!gameOver)
			{
				if (canTick)
				{
					UpdateTick();
				}
				CheckPlayerInput();
			}
		}

		public IEnumerator Move(PointyHexPoint start, int spacesToMove)
		{
			grid[start].Occupant.Moving = true;

			const float moveDelay = 0.5f;
			int spacesMoved = 0;
			PointyHexPoint currentPoint = start;

			while (spacesMoved < spacesToMove)
			{
				MoveInWorld(grid[currentPoint].Occupant.gameObject, currentPoint + grid[currentPoint].MoveDirection);
				MoveInGrid(currentPoint, currentPoint + grid[currentPoint].MoveDirection);

				currentPoint = currentPoint + grid[currentPoint].MoveDirection;

				if (!PlayerSafeFrom(currentPoint))
				{
					PlayerLose();
					break;
				}
				spacesMoved++;
				yield return new WaitForSeconds(moveDelay);
			}

			grid[currentPoint].Occupant.Moving = false;
		}

		private void MoveInGrid(PointyHexPoint start, PointyHexPoint destination)
		{
			grid[destination].Occupant = grid[start].Occupant;
			grid[start].Occupant.CurrentPointLocation = destination;
			grid[start].Occupant = null;
		}

		private void MoveInWorld(GameObject objectToMove, PointyHexPoint destinationPoint)
		{
			objectToMove.transform.localPosition = map[destinationPoint];
		}

		private void BuildGrid()
		{
			grid = PointyHexGrid<MovementCell>.Hexagon(gridSize);
		}

		private void BuildMap()
		{
			map = new PointyHexMap(CellDimensions)
				.WithWindow(ExampleUtils.ScreenRect)
				.AlignMiddleCenter(grid)
				.AnchorCellMiddleCenter();
		}

		private void CreateCells()
		{
			foreach (PointyHexPoint point in grid)
			{
				MovementCell movementCell = Instantiate(movementCellPrefab);
				movementCell.transform.parent = gridRoot.transform;

				movementCell.transform.localScale = Vector3.one;
				movementCell.transform.localPosition = map[point];

				movementCell.MoveDirection = PointyHexPoint.Zero;
				movementCell.Color = ExampleUtils.Colors[point.GetColor1_3()*4 + 3];

				grid[point] = movementCell;
			}
		}

		private void InitGame()
		{
			SetupMoveDirections();
			InitMonsters();
			SetGoal();
			SetStart();
			InitPlayer();
		}

		private void SetupMoveDirections()
		{
			for (int k = 1; k < gridSize; k++)
			{
				PointyHexPoint currentDirection = PointyHexPoint.East; 
				
				var hexPoint = PointyHexPoint.Zero + GridExtensions.directions[4]*(k);

				for (int i = 0; i < 6; i++)
				{
					for (int j = 0; j < k; j++)
					{
						if (grid.Contains(hexPoint))
						{
							grid[hexPoint].MoveDirection = currentDirection;
						}

						hexPoint = hexPoint + GridExtensions.directions[i];
					}

					currentDirection = currentDirection.Rotate60();
				}
			}
		}

		private void InitMonsters()
		{
			for (int i = 1; i < gridSize - 1; i++)
			{
				foreach (PointyHexPoint spawnPoint in grid.GetPointsInRing(i))
				{
					if (spawnPoint.Y == 0)
						CreateMonsterAt(spawnPoint, 1);
				}
			}
		}

		private void SetGoal()
		{
			grid[new PointyHexPoint(0, 0)].MakeGoal();
		}

		private void SetStart()
		{
			var outerRing = grid.GetPointsInRing(gridSize - 1)
				.Where(possibleStart => possibleStart.Y != 0)
				.ToList();

			startPoint = outerRing[Random.Range(0, outerRing.Count)];

			while (startPoint.Y == 0)
			{
				startPoint = outerRing[Random.Range(0, outerRing.Count)];
			}

			grid[startPoint].MakeStart();
		}

		private void InitPlayer()
		{
			player = Instantiate(playerPrefab);
			player.transform.parent = gridRoot.transform;

			player.transform.localScale = Vector3.one;
			player.transform.localPosition = map[startPoint];
			player.CurrentPointLocation = startPoint;
		}

		private void CreateMonsterAt(PointyHexPoint point, int moveSpeed)
		{
			if (grid.Contains(point))
			{
				Monster monster = Instantiate(monsterPrefab);
				monster.CurrentPointLocation = point;

				monster.transform.parent = gridRoot.transform;
				monster.transform.localScale = new Vector3(0.55f, 0.55f, 1);
				monster.transform.localPosition = map[point];
				monster.MoveSpeed = moveSpeed;
				monster.Moving = false;

				grid[point].Occupant = monster;

				monsters.Add(monster);
			}
		}

		private void CheckPlayerInput()
		{
			if (Input.GetMouseButtonDown(0))
			{
				Vector3 worldPosition = GridBuilderUtils.ScreenToWorld(gridRoot, Input.mousePosition);
				PointyHexPoint clickedPoint = map[worldPosition];

				if (grid.Contains(clickedPoint))
				{
					foreach (var neighbor in player.CurrentPointLocation.GetNeighbors())
					{
						if (clickedPoint == neighbor)
						{
							player.CurrentPointLocation = clickedPoint;
							player.transform.localPosition = map[clickedPoint];
							
							if (grid[clickedPoint].IsGoal)
							{
								PlayerWin();
							}

							if (!PlayerSafeFrom(player.CurrentPointLocation))
							{
								PlayerLose();
							}
						}
					}
				}
			}
		}

		private bool PlayerSafeFrom(PointyHexPoint location)
		{
			if ((grid[location].Occupant != null) && (player.CurrentPointLocation == location))
			{
				return false;
			}
			return true;
		}

		private void PlayerWin()
		{
			StartCoroutine(DelayedReset(ResetDelay));
			endMessage.gameObject.SetActive(true);
			endMessage.text = "Good job! Prepare for the next level!";
			gameOver = true;

			if (gridSize < GridMaxSize)
			{
				//uiRoot.GetComponentInChildren<Camera>().orthographicSize += 0.1f;
				gridSize++;
			}
		}

		private void PlayerLose()
		{
			//uiRoot.GetComponentInChildren<Camera>().orthographicSize = 1;
			StartCoroutine(DelayedReset(ResetDelay));
			endMessage.gameObject.SetActive(true);
			endMessage.text = "Bad luck. Try again!";
			gameOver = true;
			gridSize = GridStartSize;
		}

		private IEnumerator DelayedReset(float delay)
		{
			yield return new WaitForSeconds(delay);
			Reset();
		}

		private void UpdateTick()
		{
			StartCoroutine(TickOver());
			foreach (var monster in monsters)
			{
				if (!monster.Moving)
				{
					StartCoroutine(Move(monster.CurrentPointLocation, monster.MoveSpeed));
				}
			}
		}

		private IEnumerator TickOver()
		{
			canTick = false;
			yield return new WaitForSeconds(TickTime);
			canTick = true;

		}

		private void CheckDebugInput()
		{
			if (Input.GetMouseButtonDown(0))
			{
				Vector3 worldPosition = GridBuilderUtils.ScreenToWorld(gridRoot, Input.mousePosition);
				PointyHexPoint clickedPoint = map[worldPosition];

				if (grid.Contains(clickedPoint))
				{
					Debug.Log("Point Clicked : " + clickedPoint.ToString());
					Debug.Log("MoveDirection : " + grid[clickedPoint].MoveDirection.ToString());

					if (grid[clickedPoint].Occupant != null)
					{
						StartCoroutine(Move(clickedPoint, grid[clickedPoint].Occupant.MoveSpeed));
					}
				}
			}
		}

	}
}