using System.Collections;
using Gamelogic;
using UnityEngine;

using Gamelogic.Grids;
using System.Collections.Generic;

namespace Gamelogic.Grids.Examples
{
	public class LudoHexGrid : GLMonoBehaviour, IResetable
	{
		private const int GridSize = 10;
		private readonly Vector2 CellDimensions = new Vector2(187, 217)*1.1f;

		public LudoCell cellPrefab;
		public GameObject uiRoot;
		public GameObject gridRoot;

		public PlayerPiece playerPiecePrefab;

		public TextMesh winMessage;
		public TextMesh diceText;

		private PointyHexGrid<LudoCell> grid;
		private IMap<PointyHexPoint> map;

		private Player[] players;
		private int currentPlayer;

		private int rollValue = 0;

		private bool gameOver;

		public static readonly Color[] colors =
		{
			ExampleUtils.Colors[0],
			ExampleUtils.Colors[1],
			ExampleUtils.Colors[2],
			ExampleUtils.ColorFromInt(255, 150, 100),
			ExampleUtils.Colors[3],
			ExampleUtils.ColorFromInt(150, 100, 255)
		};

		private readonly PointyHexPoint[] StartPositions =
		{
			new PointyHexPoint(-8, 8),
			new PointyHexPoint(-9, 8),
			new PointyHexPoint(-8, 9)
		};

		public void Start()
		{
			Reset();
			rollValue = RollDice(6);
		}

		public void Update()
		{
			if (!gameOver)
			{
				CheckGameState();
				if (currentPlayer == 0)
				{
					HumanTurn();
				}
				/*else
			{
				ComputerTurn();
			}*/
			}

			if (Input.GetKeyDown(KeyCode.R))
			{
				Reset();
			}
		}

		public IEnumerator ComputerPlay()
		{
			while (currentPlayer != 0)
			{
				ComputerTurn();
				yield return new WaitForSeconds(0.5f);
			}
		}

		public void Reset()
		{
			gridRoot.transform.DestroyChildren();
			BuildGrid();
			BuildMap();
			CreateCells();
			InitializeGame();
		}

		private void BuildGrid()
		{
			grid = PointyHexGrid<LudoCell>.Hexagon(GridSize);
		}

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

		private void CreateCells()
		{
			foreach (PointyHexPoint point in grid)
			{
				LudoCell cell = Instantiate(cellPrefab);

				Vector3 worldPosition = map[point];

				cell.transform.parent = gridRoot.transform;
				cell.transform.localScale = Vector3.one;
				cell.transform.localPosition = worldPosition;

				grid[point] = cell;
			}
		}

		private void InitializeGame()
		{
			gameOver = false;

			SetupPlayerHexes();
			SetupMovementHexes();
			InitPlayers();
			InitPlayerPositions();
			InitWinMessage();
		}

		private void SetupPlayerHexes()
		{
			var playerPoints = new List<PointyHexPoint>
			{
				new PointyHexPoint(-1, 1),
				new PointyHexPoint(-2, 1),
				new PointyHexPoint(-2, 2),
				new PointyHexPoint(-3, 3),
				new PointyHexPoint(-4, 4),
				new PointyHexPoint(-5, 5),
				new PointyHexPoint(-6, 6),
				new PointyHexPoint(-8, 8),
				new PointyHexPoint(-9, 8),
				new PointyHexPoint(-8, 9)
			};

			grid[playerPoints[0]].IsLast = true;
			grid[playerPoints[0]].IsHome = true;
			grid[playerPoints[1]].IsHome = true;
			grid[playerPoints[2]].IsHome = true;

			grid[playerPoints[0]].Direction = PointyHexPoint.East;
			grid[playerPoints[1]].Direction = PointyHexPoint.East;
			grid[playerPoints[2]].Direction = PointyHexPoint.SouthWest;
			grid[playerPoints[3]].Direction = PointyHexPoint.SouthEast;
			grid[playerPoints[4]].Direction = PointyHexPoint.SouthEast;
			grid[playerPoints[5]].Direction = PointyHexPoint.SouthEast;
			grid[playerPoints[6]].Direction = PointyHexPoint.SouthEast;
			grid[playerPoints[7]].Direction = PointyHexPoint.SouthEast;
			grid[playerPoints[8]].Direction = PointyHexPoint.East;
			grid[playerPoints[9]].Direction = PointyHexPoint.SouthWest;

			int playerIndex = 0;

			foreach (PointyHexPoint playerPoint in playerPoints)
			{
				grid[playerPoint].Color = (colors[playerIndex]);
				grid[playerPoint].HomePlayer = -1;
				grid[playerPoint].HomeDirection = PointyHexPoint.Zero;
			}

			for (playerIndex = 1; playerIndex < 6; playerIndex++)
			{
				for (int i = 0; i < playerPoints.Count; i++)
				{
					grid[playerPoints[i].Rotate60()].Direction = grid[playerPoints[i]].Direction.Rotate60();
					playerPoints[i] = playerPoints[i].Rotate60();
				}

				grid[playerPoints[0]].IsLast = true;
				grid[playerPoints[0]].IsHome = true;
				grid[playerPoints[1]].IsHome = true;
				grid[playerPoints[2]].IsHome = true;

				foreach (PointyHexPoint playerPoint in playerPoints)
				{
					grid[playerPoint].Color = colors[playerIndex];
				}
			}
		}

		private void SetupMovementHexes()
		{
			var movePositions = new List<PointyHexPoint>
			{
				new PointyHexPoint(-7, 0),
				new PointyHexPoint(-7, 1),
				new PointyHexPoint(-6, 1),
				new PointyHexPoint(-6, 2),
				new PointyHexPoint(-6, 3),
				new PointyHexPoint(-6, 4),
				new PointyHexPoint(-6, 5),
				new PointyHexPoint(-7, 6)
			};

			grid[movePositions[0]].Direction = PointyHexPoint.NorthEast;
			grid[movePositions[1]].Direction = PointyHexPoint.East;
			grid[movePositions[2]].Direction = PointyHexPoint.NorthEast;
			grid[movePositions[3]].Direction = PointyHexPoint.NorthEast;
			grid[movePositions[4]].Direction = PointyHexPoint.NorthEast;
			grid[movePositions[5]].Direction = PointyHexPoint.NorthEast;
			grid[movePositions[6]].Direction = PointyHexPoint.NorthWest;
			grid[movePositions[7]].Direction = PointyHexPoint.NorthEast;

			for (int playerIndex = 0; playerIndex < 6; playerIndex++)
			{
				foreach (PointyHexPoint movePoint in movePositions)
				{
					grid[movePoint].HomePlayer = -1;
					grid[movePoint].HomeDirection = PointyHexPoint.Zero;
					grid[movePoint].Color = new Color(0.95f, 0.95f, 0.95f);
				}

				grid[movePositions[7]].HomePlayer = playerIndex;
				grid[movePositions[7]].HomeDirection = grid[movePositions[7]].Direction.Rotate300();

				for (int j = 0; j < movePositions.Count; j++)
				{
					grid[movePositions[j].Rotate60()].Direction = grid[movePositions[j]].Direction.Rotate60();
					movePositions[j] = movePositions[j].Rotate60();
				}
			}
		}

		private void InitPlayers()
		{
			players = new Player[6];
			for (int i = 0; i < 6; i++)
			{
				players[i] = new Player {HomeCount = 0};
			}
		}

		private void KickPlayer(PointyHexPoint point)
		{
			var occupant = grid[point].Occupant;
			occupant.CurrentPosition = occupant.StartPosition;
			grid[occupant.StartPosition].Occupant = occupant;
			grid[point].Occupant = null;
			occupant.transform.localPosition = map[occupant.StartPosition];
		}

		private void InitPlayerPositions()
		{
			for (var playerIndex = 0; playerIndex < 6; playerIndex++)
			{
				foreach (var startPosition in StartPositions)
				{
					PointyHexPoint playerStartPosition = startPosition;
					for (var i = 0; i < playerIndex; i++)
					{
						playerStartPosition = playerStartPosition.Rotate60();
					}

					var playerPiece = Instantiate<PlayerPiece>(playerPiecePrefab);

					playerPiece.transform.parent = gridRoot.transform;
					playerPiece.transform.localScale = Vector3.one;
					playerPiece.transform.localPosition = map[playerStartPosition];

					playerPiece.Color = playerIndex;
					playerPiece.Owner = playerIndex;
					playerPiece.CurrentPosition = playerStartPosition;
					playerPiece.StartPosition = playerStartPosition;
					playerPiece.Movable = true;

					grid[playerStartPosition].Occupant = playerPiece;
					players[playerIndex].AddPlayerPiece(playerPiece);
				}
			}
		}

		private void MovePiece(PointyHexPoint piecePosition, int diceRoll)
		{
			var newPosition = piecePosition;
			var path = new Stack<PointyHexPoint>();

			for (int i = 0; i < diceRoll; i++)
			{
				if (!grid[newPosition].IsLast)
				{
					if (grid[newPosition].HomePlayer != currentPlayer)
					{
						newPosition += grid[newPosition].Direction;
						path.Push(newPosition);
					}
					else
					{
						newPosition += grid[newPosition].HomeDirection;
						path.Push(newPosition);
					}
				}
			}

			bool suitableSpaceFound = false;
			bool searchForwards = true;

			while (!suitableSpaceFound)
			{
				if (grid[newPosition].Occupant == null)
				{
//empty space found
					suitableSpaceFound = true;
				}
				else
				{
//space with occupant found
					if (grid[newPosition].Occupant.Owner != currentPlayer)
					{
//space with enemy found
						suitableSpaceFound = true;
						KickPlayer(newPosition);
					}
					else
					{
//space with friendly found
						if (grid[newPosition].IsLast)
						{
//last space found
							searchForwards = false;
						}
						if (searchForwards)
						{
							if (grid[newPosition].HomePlayer != currentPlayer)
							{
								newPosition += grid[newPosition].Direction;
								path.Push(newPosition);
							}
							else
							{
								newPosition += grid[newPosition].HomeDirection;
								path.Push(newPosition);
							}
						}
						else
						{
							newPosition = path.Pop();
						}
					}
				}
			}
			if (grid[newPosition].IsHome)
			{
				grid[piecePosition].Occupant.Movable = false;
			}

			MovePiece(piecePosition, newPosition);
		}

		private void MovePiece(PointyHexPoint piecePosition, PointyHexPoint newPosition)
		{
			var occupant = grid[piecePosition].Occupant;

			if (occupant == null)
			{
				Debug.LogError("No piece to move...");
				return;
			}

			grid[piecePosition].Occupant = null;
			occupant.transform.localPosition = map[newPosition];
			grid[newPosition].Occupant = occupant;
			occupant.CurrentPosition = newPosition;
		}

		private int RollDice(int sides)
		{
			int roll = Random.Range(1, sides + 1);
			diceText.text = roll.ToString();
			return roll;
		}

		private void HumanTurn()
		{
			//check for input
			if (Input.GetMouseButtonDown(0))
			{
				Vector3 clickedPosition = GridBuilderUtils.ScreenToWorld(gridRoot, Input.mousePosition);
				PointyHexPoint clickedPoint = map[clickedPosition];

				if (grid.Contains(clickedPoint) && grid[clickedPoint].Occupant != null)
				{
					if (grid[clickedPoint].Occupant.Owner == currentPlayer && grid[clickedPoint].Occupant.Movable)
					{
						MovePiece(clickedPoint, rollValue);
						GoNextPlayer();
						StartCoroutine(ComputerPlay());
					}
				}
			}
		}

		private void ComputerTurn()
		{
			PlayerPiece pieceToMove = players[currentPlayer].GetPlayerPiece();
			MovePiece(pieceToMove.CurrentPosition, rollValue);
			GoNextPlayer();
		}

		private void GoNextPlayer()
		{
			rollValue = RollDice(6);
			currentPlayer++;
			if (currentPlayer >= 6)
			{
				currentPlayer = 0;
			}
		}

		private void CheckGameState()
		{
			for (int playerIndex = 0; playerIndex < players.Length; playerIndex++)
			{
				bool playerWin = true;
				foreach (var piece in players[playerIndex].PlayerPieces)
				{
					if (!grid[piece.CurrentPosition].IsHome)
						playerWin = false;
				}

				if (playerWin)
				{
					GameOver(playerIndex);
				}
			}
		}

		private void InitWinMessage()
		{
			//winMessage.color = ExampleUtils.colors[0];
			winMessage.gameObject.SetActive(false);
		}

		private void GameOver(int winner)
		{
			winMessage.gameObject.SetActive(true);
			winMessage.color = colors[winner];
			winMessage.text = string.Format("Congratulations to player {0} for winning!", winner.ToString());

			gameOver = true;
		}
	}
}