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

public class BoardManager : MonoBehaviour
{
	[SerializeField] private int tileSize = 2;
	private Tile[] tiles;
	private Tile[,] grid;
	private int smallestX;
	private int biggestX;
	private int smallestZ;
	private int biggestZ;
	private List<Vector2Int> currentlyOccupied = new List<Vector2Int>();

	private void Awake()
	{
		tiles = GetComponentsInChildren<Tile>();

		smallestX = int.MaxValue;
		biggestX = int.MinValue;
		smallestZ = int.MaxValue;
		biggestZ = int.MinValue;

		foreach (Tile t in tiles)
		{
			int roundedX = Mathf.RoundToInt(t.transform.position.x);
			if (roundedX < smallestX)
			{
				smallestX = roundedX;
			}

			if (roundedX > biggestX)
			{
				biggestX = roundedX;
			}

			int roundedZ = Mathf.RoundToInt(t.transform.position.z);
			if (roundedZ < smallestZ)
			{
				smallestZ = roundedZ;
			}

			if (roundedZ > biggestZ)
			{
				biggestZ = roundedZ;
			}
		}

		int width = Math.Abs(biggestX - smallestX);
		int height = Math.Abs(biggestZ - smallestZ);
		int widthBounds = width / tileSize + 1;
		int heightBounds = height / tileSize + 1;

		grid = new Tile[widthBounds, heightBounds];

		foreach (Tile t in tiles)
		{
			int x = (Mathf.RoundToInt(t.transform.position.x) + tileSize - smallestX) / tileSize - 1;
			int y = (Mathf.RoundToInt(t.transform.position.z) + tileSize - smallestZ) / tileSize - 1;
			grid[x, y] = t;
		}

		int rows = grid.GetLength(0);
		int columns = grid.GetLength(1);
		string arrayString = string.Empty;
		for (int row = 0; row < rows; row++)
		{
			for (int column = 0; column < columns; column++)
			{
				arrayString += grid[row, column] ? "1" : "0";
			}

			arrayString += Environment.NewLine;
		}

		Debug.Log(arrayString);
	}

	private Vector2Int GridPosition(Vector3 worldPosition)
	{
		return new Vector2Int(
			(Mathf.RoundToInt(worldPosition.x) + tileSize - smallestX) / tileSize - 1,
			(Mathf.RoundToInt(worldPosition.z) + tileSize - smallestZ) / tileSize - 1);
	}

	public List<Vector2Int> PlayerHasMoved(Vector3 position, Vector3 size, bool actuallyDoMovement, GameObject player = null)
	{
		if (actuallyDoMovement)
		{
			LeaveOccupied(player);
		}

		Vector3Int tileAmount = new Vector3Int(
			Mathf.RoundToInt(size.x / tileSize),
			Mathf.RoundToInt(size.y / tileSize),
			Mathf.RoundToInt(size.z / tileSize));

		var occupied = new List<Vector2Int>();

		// Standing straight up.
		if (tileAmount.x == 1 && tileAmount.z == 1)
		{
			occupied.Add(GridPosition(position));

			if (actuallyDoMovement)
			{
				currentlyOccupied = occupied;
				EnterOccupied(player);
			}

			return occupied;
		}

		float centerX = tileAmount.x / 2f * tileSize;
		float beginX = position.x - centerX;
		float centerZ = tileAmount.z / 2f * tileSize;
		float beginZ = position.z - centerZ;

		for (int x = 0; x < tileAmount.x; x++)
		{
			int xComponent = Mathf.RoundToInt(beginX + x * tileSize + tileSize / 2f);
			for (int y = 0; y < tileAmount.z; y++)
			{
				occupied.Add(GridPosition(new Vector3(xComponent, 0,
					Mathf.RoundToInt(beginZ + y * tileSize + tileSize / 2f))));
			}
		}

		if (actuallyDoMovement)
		{
			currentlyOccupied = occupied;
			EnterOccupied(player);
		}

		return occupied;
	}

	private void LeaveOccupied(GameObject player)
	{
		if (grid == null)
		{
			return;
		}

		foreach (Vector2Int o in currentlyOccupied)
		{
			grid[o.x, o.y].OnLeave(player);
		}
	}

	private void EnterOccupied(GameObject player)
	{
		if (grid == null)
		{
			return;
		}

		foreach (Vector2Int o in currentlyOccupied)
		{
			grid[o.x, o.y].OnEnter(player);
		}
	}

	public bool CanMoveInDirection(Vector3 comingFrom, Vector3 steppingInto, Vector3 size)
	{
		var potentialTiles = PlayerHasMoved(steppingInto, size, false);
		var orientation = potentialTiles.Count == 1 ? Orientation.Standing : Orientation.Laying; 
		foreach (Vector2Int tilePosition in potentialTiles)
		{
			if (tilePosition.x < 0 || tilePosition.y < 0 ||
			    tilePosition.x >= grid.GetLength(0) ||
			    tilePosition.y >= grid.GetLength(1))
			{
				return false;
			}

			var currentTile = grid[tilePosition.x, tilePosition.y];
			if (!currentTile || !currentTile.CanEnter(GridPosition(comingFrom), tilePosition, orientation))
			{
				return false;
			}
		}

		return true;
	}
}