﻿using System.Collections.Generic;
using System.Linq;
using Gamelogic.AbstractStrategy.Grids;
using Gamelogic.Diagnostics;
using Gamelogic.Grids;
using UnityEngine;

namespace Gamelogic.AbstractStrategy.Examples
{
	public enum CaptureType
	{
		HopCapture,
		RelativeCapture,
		SurroundCapture
	}

	public class CaptureGame : GLMonoBehaviour
	{
		public CaptureType captureType;

		private GridGameManager manager;
		private Player<RectPoint, GridGamePieceSettings> redPlayer, bluePlayer;
		private IPieceProperties selectedPiece;

		public void Start()
		{
			InitializeGameManager();
			InitializePlayers();

			manager.StartGame();
		}

		public void Update()
		{
			if (Input.GetKeyDown(KeyCode.Space))
			{
				manager.StartGame(); //Reset Game
			}
		}

		public void OnClick(RectPoint point)
		{
			var currentPlayer = manager.CurrentPlayer;

			if (currentPlayer is GridHumanPlayer &&
				manager.MoveManager.Done)
			{
				// If a block is occupied, try to select
				var state = manager.State;
				var piece = state.TestPosition(point);

				if (piece != null)
				{
					if (piece.playerID == currentPlayer.PlayerID)
					{
						SelectPiece(piece);
					}
				}

				if (selectedPiece != null)
				{
					// Try to commit move
					var selectedPos = state.FindPiece(selectedPiece);
					var move = state.CreateMovePieceMove(selectedPiece, selectedPos, point);

					if (manager.CommitMove(move))
					{
						TransformEnemyPieces(point);

						DeselectPiece();
					}
					else
					{
						CapturePiece(selectedPos, point);
					}
				}
			}
		}

		private void TransformEnemyPieces(RectPoint point)
		{
			if (captureType != CaptureType.SurroundCapture)
				return;

			var rules = manager.Rules;
			var currentPlayer = manager.CurrentPlayer;
			var positionsToReplace = GetPositionsToReplace(manager, point);

			//replace all the enemy cells between current player pieces
			foreach (var rectPoint in positionsToReplace)
			{
				var newMove = manager.State.CreateReplacePieceMove(manager.GameGrid[rectPoint][0],
					rules.CreatePieceProperties(rules.GetPieceSettings("piece", currentPlayer.PlayerID), currentPlayer),
					rectPoint);

				manager.CommitMove(newMove, false);
			}
		}

		private void InitializeGameManager()
		{
			manager = GetComponent<GridGameManager>();

			if (manager == null)
			{
				GLDebug.LogError("You must have a GridGameManager attached to the same game object as this component");
			}
		}

		private void InitializePlayers()
		{
			redPlayer = new GridHumanPlayer("red");
			bluePlayer = new GridHumanPlayer("blue");

			manager.RegisterPlayer(redPlayer);
			manager.RegisterPlayer(bluePlayer);
		}

		private void DeselectPiece()
		{
			if (selectedPiece != null)
			{
				UpdateVisualSelectionState(false);
			}

			selectedPiece = null;
		}

		private void SelectPiece(IPieceProperties piece)
		{
			if (selectedPiece != null)
			{
				DeselectPiece();
			}

			selectedPiece = piece;

			UpdateVisualSelectionState(true);
		}

		private void UpdateVisualSelectionState(bool selectionOn)
		{
			var visualPiece = manager.GetExistingVisualPiece(selectedPiece);
			var sprite = visualPiece.GetComponentInChildren<SpriteRenderer>();

			if (sprite != null)
			{
				var colour = sprite.color;
				colour.a = selectionOn ? 0.8f : 1.0f;
				sprite.color = colour;
			}
		}

		/// <summary>
		/// It tries to capture any piece that is between a movement that has a manhattan distance of 2 or 4.
		/// In other words, any piece in rect or diagonal position when you hop above a piece.
		/// </summary>
		private void CapturePiece(RectPoint actualPiecePosition, RectPoint targetPosition)
		{
			var state = manager.State;

			var moveDist = actualPiecePosition.DistanceFrom(targetPosition);
			
			if (ValidateCapture(moveDist))
				return;

			var centerPos = (targetPosition + actualPiecePosition) / 2;
			var capturePiece = state.TestPosition(centerPos);

			if (capturePiece == null)
				return;

			var move = state.CreateCapturePieceMove(selectedPiece, capturePiece, actualPiecePosition, targetPosition, centerPos);

			if (manager.CommitMove(move))
			{
				DeselectPiece();
			}
		}

		private bool ValidateCapture(int moveDistance)
		{
			switch (captureType)
			{
				case CaptureType.HopCapture:
					if (moveDistance != 2 && moveDistance != 4)
						return true;
					break;
				case CaptureType.RelativeCapture:
					if (moveDistance != 1)
						return true;
					break;
				case CaptureType.SurroundCapture:
					return true;
			}

			return false;
		}

		public static IEnumerable<RectPoint> GetPositionsToReplace(GridGameManager gameManager, RectPoint position)
		{
			var grid = gameManager.GameGrid;
			var currentPlayer = gameManager.CurrentPlayer;
			var positionsToReplace = new List<RectPoint>();

			foreach (var direction in RectPoint.MainAndDiagonalDirections)
			{
				var potentialPositionsToReplace = new List<RectPoint>();

				foreach (var point in grid.LineIterator(position, direction).Skip(1))
				{
					if (grid[point].Any())
					{
						if (grid[point][0].playerID != currentPlayer.PlayerID)
						{
							potentialPositionsToReplace.Add(point);
						}
						else
						{
							positionsToReplace.AddRange(potentialPositionsToReplace);
							break;
						}
					}
					else
					{
						break;
					}
				}
			}

			return positionsToReplace;
		}
	}
}