﻿using UnityEngine;
using System.Linq;
using Gamelogic.Grids;
using System.Collections;

namespace Gamelogic.Grids.Examples
{
	public class MagicTriangle : GLMonoBehaviour, IResetable
	{
		private const float TileMoveTime = 0.15f;
		private const int AmountOfRandomStartingMoves = 50; 
		
		public GameObject gridRoot;
		public MagicTriangleCell cellPrefab;
		public GameObject overlayPrefab;
		public GameObject winMessageLabel;
		public int gridSize;
		public Vector2 cellDimensions;

		private PointyHexGrid<MagicTriangleCell> grid;
		private IMap<PointyHexPoint> map;
		private bool gameOver;
		private bool pointSelected;
		private PointyHexPoint selectedPoint;
		private GameObject overlay;

		public void Start()
		{
			Debug.Log("Start");
			Reset();
		}

		public void Update()
		{
			if (Input.GetKeyDown(KeyCode.N))
			{
				Reset();
			}

			if (!gameOver)
			{
				HandlePlayerInput();
			}
		}

		public void Reset()
		{
			gridSize++;

			if (gridSize > 10)
			{
				gridSize = 3;
			}

			Debug.Log("Reset");
			gridRoot.transform.DestroyChildren();

			CreateOverlay();
			DeselectPoint();
			BuildGrid();
			BuildMap();
			CreateCells();
			InitGame();
		}

		private void CreateOverlay()
		{
			overlay = Instantiate(overlayPrefab);
			overlay.transform.parent = gridRoot.transform;
			overlay.transform.localScale = Vector3.one;
		}

		private void BuildGrid()
		{
			Debug.Log("BuildGrid()");
			grid = PointyHexGrid<MagicTriangleCell>
				.BeginShape()
				.DownTriangle(gridSize)
				.EndShape();

			/*grid.SetGridPointTransforms( 
			p => p + new PointyHexPoint(1 - gridSize, gridSize - 1),
			p => p - new PointyHexPoint(1 - gridSize, gridSize - 1)
			);*/
		}

		private void BuildMap()
		{
			Debug.Log("BuildMap");
			map = new PointyBrickMap(cellDimensions)
				.AnchorCellMiddleCenter()
				.WithWindow(ExampleUtils.ScreenRect)
				.AlignMiddleCenter(grid)

				.Scale(1.1f);
		}

		private void CreateCells()
		{
			Debug.Log("CreateCells");
			foreach (var gridPoint in grid)
			{
				MagicTriangleCell cell = Instantiate(cellPrefab);
				cell.transform.parent = gridRoot.transform;
				cell.transform.localScale = Vector3.one;
				Vector3 newPoint = map[gridPoint];

				cell.transform.localPosition = newPoint;
				cell.name = gridPoint.ToString();
				grid[gridPoint] = cell;
			}
		}

		private void InitGame()
		{
			Debug.Log("InitGame");
			InitTopRow();
			CalculateOtherNumbers();

			while (EvaluateCorrectness())
			{
				JumbleBoard();
			}

			winMessageLabel.SetActive(false);
			gameOver = false;
		}

		private void InitTopRow()
		{
			Debug.Log("InitTopRow");
			const int numberMinimum = 1;
			const int numberMaximum = 10;
			foreach (var topRowPoint in grid.Where(p => p.Y == 0))
			{
				grid[topRowPoint].Number = Random.Range(numberMinimum, numberMaximum);
			}
		}

		private void CalculateOtherNumbers()
		{
			Debug.Log("CalculateOTherNumbers()");
			grid[PointyHexPoint.Zero].Color = Color.red;

			for (int currentRow = -1; currentRow >= 1 - gridSize; currentRow--)
			{
				int row = currentRow;

				foreach (var gridPoint in grid.Where(p => p.Y == row))
				{
					Debug.Log(gridPoint);
					int northWestParentNumber = grid[gridPoint + PointyHexPoint.NorthWest].Number;
					int northEastParentNumber = grid[gridPoint + PointyHexPoint.NorthEast].Number;
					int subtractionOfParents = northWestParentNumber - northEastParentNumber;
					grid[gridPoint].Number = Mathf.Abs(subtractionOfParents);
				}
			}
		}

		private void JumbleBoard()
		{
			Debug.Log("JumbleBoard");
			for (int i = 0; i < AmountOfRandomStartingMoves; i++)
			{
				SwapPoints(grid.RandomItem(), grid.RandomItem());
			}
		}

		private void SwapPoints(PointyHexPoint p1, PointyHexPoint p2)
		{
			Debug.Log("Swapping points");
			StartCoroutine(MoveTile(map[p1], map[p2], grid[p1].gameObject));
			StartCoroutine(MoveTile(map[p2], map[p1], grid[p2].gameObject));
			MagicTriangleCell tempCell = grid[p1];
			grid[p1] = grid[p2];
			grid[p2] = tempCell;
		}

		private IEnumerator MoveTile(Vector3 from, Vector3 to, GameObject tileToMove)
		{
			float time = 0;

			while (time < TileMoveTime)
			{
				float x = Mathf.Lerp(@from.x, to.x, time/TileMoveTime);
				float y = Mathf.Lerp(@from.y, to.y, time/TileMoveTime);
				tileToMove.transform.localPosition = new Vector3(x, y);
				yield return null;
				time += Time.deltaTime;
			}
			tileToMove.transform.localPosition = to;
		}

		private void HandlePlayerInput()
		{
			if (Input.GetMouseButtonDown(0))
			{
				PointyHexPoint clickedPoint = map[GridBuilderUtils.ScreenToWorld(gridRoot, Input.mousePosition)];
				Debug.Log("Clicked Point: " + clickedPoint.ToString());

				if (grid.Contains(clickedPoint))
				{
					if (pointSelected)
					{
						if (clickedPoint == selectedPoint)
						{
							DeselectPoint();
						}
						else
						{
							SwapPoints(clickedPoint, selectedPoint);
							DeselectPoint();
						}
					}
					else
					{
						SelectPointAt(clickedPoint);
					}
				}

				if (EvaluateCorrectness())
				{
					GameOver();
				}
			}

			
		}

		private void SelectPointAt(PointyHexPoint clickedPoint)
		{
			overlay.SetActive(true);
			pointSelected = true;
			selectedPoint = clickedPoint;
			overlay.transform.localPosition = map[selectedPoint];
		}

		private void DeselectPoint()
		{
			overlay.SetActive(false);
			pointSelected = false;
		}

		private bool EvaluateCorrectness()
		{
			Debug.Log("Evaluating Correctness");
			bool gameCompleted = true;

			foreach (var gridPoint in grid)
			{
				if (gridPoint.Y != 0)
				{
					//check to see if value is correct
					int northWestParentNumber = grid[gridPoint + PointyHexPoint.NorthWest].Number;
					int northEastParentNumber = grid[gridPoint + PointyHexPoint.NorthEast].Number;
					int subtractionOfParents = northWestParentNumber - northEastParentNumber;

					if (grid[gridPoint].Number != Mathf.Abs(subtractionOfParents))
					{
						grid[gridPoint].Color = ExampleUtils.Colors[3];
						gameCompleted = false;
					}
					else
					{
						grid[gridPoint].Color = ExampleUtils.Colors[1];
					}
				}
				else
				{
					grid[gridPoint].Color = ExampleUtils.Colors[1];
				}
			}

			return gameCompleted;
		}

		private void GameOver()
		{
			winMessageLabel.SetActive(true);
			gameOver = true;
		}
	}
}
