﻿using UnityEngine;
using Debug = UnityEngine.Debug;

namespace Gamelogic.Grids.Examples
{
	public class ScrollingGrid : GLMonoBehaviour
	{
		#region Constants

		private const int ScrollSpeed = 100;

		private static readonly PointyHexPoint GridRightEnd = PointyHexPoint.East*2;
		private static readonly PointyHexPoint GridLeftEnd = PointyHexPoint.West*2;
		private static readonly PointyHexPoint GridTopEnd = PointyHexPoint.NorthEast + PointyHexPoint.NorthWest;
		private static readonly PointyHexPoint GridBottomEnd = PointyHexPoint.SouthEast + PointyHexPoint.SouthWest;

		#endregion

		#region Pseudo Constants

		private Vector3 rightEnd;
		private Vector3 leftEnd;
		private Vector3 topEnd;
		private Vector3 bottomEnd;
		private Vector3 gridRootOriginalPosition;

		#endregion

		#region Public Fields

		public ScrollingGridCell cellPrefab;
		public GameObject gridRoot;

		#endregion

		#region Private Fields

		private PointyHexGrid<int> bigGrid;
		private PointyHexGrid<ScrollingGridCell> gridWindow;
		private IMap3D<PointyHexPoint> map;

		private Vector3 windowOffset;
		private PointyHexPoint gridOffset;

		#endregion

		#region Unity Callbacks

		public void Start()
		{
			InitializeOffsets();
			BuildGrids();
			InitializeGridPointConstants();
		}

		public void Update()
		{
			ProcessKeys();
			ProcessMouse();
			UpdateGridOutOfBounds();
		}

		#endregion

		#region Implementation

		private void InitializeOffsets()
		{
			windowOffset = Vector3.zero;
			gridOffset = PointyHexPoint.Zero;
		}

		private void BuildGrids()
		{
			bigGrid = PointyHexGrid<int>.Hexagon(20);
			gridWindow = PointyHexGrid<ScrollingGridCell>.Rectangle(20, 10);

			map = new PointyHexMap(new Vector2(69, 80)*3)
				.WithWindow(ExampleUtils.ScreenRect)
				.AlignMiddleCenter(gridWindow)
				.To3DXY();

			foreach (var point in bigGrid)
			{
				bigGrid[point] = point.GetColor(9, 3, 1);
			}

			foreach (var point in gridWindow)
			{
				var cell = Instantiate(cellPrefab);

				cell.transform.parent = gridRoot.transform;
				cell.transform.localPosition = map[point];

				cell.x = point.X + gridOffset.X;
				cell.y = point.Y + gridOffset.Y;

				if (bigGrid.Contains(point))
				{
					cell.Color = ExampleUtils.Colors[bigGrid[point + gridOffset]];
				}

				gridWindow[point] = cell;
			}
		}

		private void InitializeGridPointConstants()
		{
			gridRootOriginalPosition = map[PointyHexPoint.Zero];
			rightEnd = map[GridRightEnd] - gridRootOriginalPosition;
			leftEnd = map[GridLeftEnd] - map[PointyHexPoint.Zero];
			topEnd = map[GridTopEnd] - map[PointyHexPoint.Zero];
			bottomEnd = map[GridBottomEnd] - map[PointyHexPoint.Zero];
		}

		private void ProcessMouse()
		{
			if (Input.GetMouseButtonDown(0))
			{
				var gridPoint = map[GridBuilderUtils.ScreenToWorld(gridRoot, Input.mousePosition)];
				bigGrid[gridPoint + gridOffset] = 0;
				UpdateWindowCell(gridPoint);
				gridWindow[gridPoint].HighlightOn = !gridWindow[gridPoint].HighlightOn;
			}
		}

		private void UpdateGridOutOfBounds()
		{
			if (windowOffset.x > rightEnd.x)
			{
				windowOffset.x -= rightEnd.x;
				gridOffset -= GridRightEnd;

				UpdateWindow();
			}
			else if (windowOffset.x < leftEnd.x)
			{
				windowOffset.x -= leftEnd.x;
				gridOffset -= GridLeftEnd;
				UpdateWindow();
			}
			else if (windowOffset.y > topEnd.y)
			{
				windowOffset.y -= topEnd.y;
				gridOffset -= GridTopEnd;

				UpdateWindow();
			}
			else if (windowOffset.y < bottomEnd.y)
			{
				windowOffset.y -= bottomEnd.y;
				gridOffset -= GridBottomEnd;

				UpdateWindow();
			}
		}

		private void ProcessKeys()
		{
			if (Input.GetKey(KeyCode.RightArrow))
			{
				windowOffset += ScrollSpeed*Vector3.right*Time.deltaTime;
				gridRoot.transform.localPosition = windowOffset;
			}

			if (Input.GetKey(KeyCode.LeftArrow))
			{
				windowOffset += ScrollSpeed*Vector3.left*Time.deltaTime;
				gridRoot.transform.localPosition = windowOffset;
			}

			if (Input.GetKey(KeyCode.UpArrow))
			{
				windowOffset += ScrollSpeed*Vector3.up*Time.deltaTime;
				gridRoot.transform.localPosition = windowOffset;
			}

			if (Input.GetKey(KeyCode.DownArrow))
			{
				windowOffset += ScrollSpeed*Vector3.down*Time.deltaTime;
				gridRoot.transform.localPosition = windowOffset;
			}
		}

		private void UpdateWindow()
		{
			gridRoot.transform.localPosition = windowOffset;

			foreach (var point in gridWindow)
			{
				var cell = gridWindow[point];

				cell.x = point.X + gridOffset.X;
				cell.y = point.Y + gridOffset.Y;

				UpdateWindowCell(point);
			}

			Debug.Log(gridWindow[PointyHexPoint.Zero].x);
		}

		private void UpdateWindowCell(PointyHexPoint point)
		{
			var cell = gridWindow[point];

			if (bigGrid.Contains(point))
			{
				cell.Color = ExampleUtils.Colors[bigGrid[point + gridOffset]];
			}
			else
			{
				cell.Color = Color.black;
			}
		}

		#endregion
	}
}