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

public class MapManager : MonoBehaviour
{
    [SerializeField] private Tilemap buildingsMap;
    //[SerializeField] private Tilemap resourcesMap;
    [SerializeField] private Tilemap availableBuildingAreaMap;
    [SerializeField] private List<TileTypePair> tileDatas;

    [Serializable]
    class TileTypePair
    {
        public TileBase tile;
        public TileType tileType;
    }

    private readonly Dictionary<TileBase, TileType> tileBaseToData = new();
    private readonly Dictionary<TileType, TileBase> tileTypeToBase = new();

    public static MapManager Instance;
    private void Awake()
    {
        if (Instance == null) Instance = this;
        else Destroy(gameObject);

        DisplayBuildArea(false);

        foreach (var tileData in tileDatas)
        {
            tileBaseToData.Add(tileData.tile, tileData.tileType);
            tileTypeToBase.Add(tileData.tileType, tileData.tile);
        }
    }

    [ContextMenu("RecalculateBounds")]
    private void RecalculateBounds()
    {
        buildingsMap.CompressBounds();
    }

    public Vector2 CalcCorner(Vector2 pos, Vector2 size) =>
        new(pos.x - (size.x - 1) / 2, pos.y - (size.y - 1) / 2);

    public void DisplayBuildArea(bool isOn)
    {
        availableBuildingAreaMap.gameObject.SetActive(isOn);
    }

    public bool CheckIfAvailableForBuilding(Vector2 buildingPos, BuildingData buildingData)
    {
        Vector2 corner = CalcCorner(buildingPos, buildingData.Size);

        for (int y = 0; y < buildingData.Size.y; y++)
        {
            for (int x = 0; x < buildingData.Size.x; x++)
            {
                Vector3 tilePos = new(corner.x + x, corner.y + y, 0);
                Vector3Int gridPosition = buildingsMap.WorldToCell(tilePos);
                TileBase tile = buildingsMap.GetTile(gridPosition);
                if (tile == null) return false;
                if (y == 0)
                {
                    if (tileBaseToData[tile] != TileType.Available) return false;
                    //if (!buildingData.BuildingGround.HasFlag(tileBaseToData[tile])) return false;
                }
                else if (tileBaseToData[tile] != TileType.Empty) return false;
            }
        }
        
        return true;
    }

    public void NewBuildingPlaced(Vector2 buildingPos, BuildingData buildingData)
    {
        Vector2 corner = CalcCorner(buildingPos, buildingData.Size);

        for (int y = 0; y < buildingData.Size.y + 1; y++)
        {
            for (int x = 0; x < buildingData.Size.x; x++)
            {
                Vector3 tilePos = new(corner.x + x, corner.y + y, 0);
                Vector3Int gridPosition = buildingsMap.WorldToCell(tilePos);

                if (y == buildingData.Size.y)
                {
                    //TileBase tile = resourcesMap.GetTile(gridPosition);
                    //if (tile != null) buildingsMap.SetTile(gridPosition, tile);
                    buildingsMap.SetTile(gridPosition, tileTypeToBase[TileType.Unavailable]);
                    //else buildingsMap.SetTile(gridPosition, tileTypeToBase[buildingData.BuildingTop]);
                }
                else
                {
                    buildingsMap.SetTile(gridPosition, tileTypeToBase[TileType.Occupied]);
                    availableBuildingAreaMap.SetTile(gridPosition, null);
                }
                
            }
        }
    }

    public void BuildingBuilt(Vector2 buildingPos, BuildingData buildingData)
    {
        if (!buildingData.CanPlaceOnTop) return;
        ChangeBuildingTop(buildingPos, buildingData, TileType.Available);
    }

    public void ChangeBuildingTop(Vector2 buildingPos, BuildingData buildingData, TileType tileType)
    {
        Vector2 corner = CalcCorner(buildingPos, buildingData.Size);

        for (int x = 0; x < buildingData.Size.x; x++)
        {
            Vector3 tilePos = new(corner.x + x, corner.y + buildingData.Size.y, 0);
            Vector3Int gridPosition = buildingsMap.WorldToCell(tilePos);

            buildingsMap.SetTile(gridPosition, tileTypeToBase[tileType]);
            availableBuildingAreaMap.SetTile(gridPosition, tileTypeToBase[tileType]);
        }
    }

    public bool CheckIfBuildingTopIsOpen(Vector2 buildingPos, BuildingData buildingData)
    {
        Vector2 corner = CalcCorner(buildingPos, buildingData.Size);
        
        for (int x = 0; x < buildingData.Size.x; x++)
        {
            Vector3 tilePos = new(corner.x + x, corner.y + buildingData.Size.y, 0);
            Vector3Int gridPosition = buildingsMap.WorldToCell(tilePos);
            TileBase tile = buildingsMap.GetTile(gridPosition);
            if (tileBaseToData[tile] == TileType.Occupied) return false;
        }
        
        return true;
    }

    public void RemoveBuilding(Vector2 buildingPos, BuildingData buildingData)
    {
        Vector2 corner = CalcCorner(buildingPos, buildingData.Size);

        for (int y = 0; y < buildingData.Size.y + 1; y++)
        {
            for (int x = 0; x < buildingData.Size.x; x++)
            {
                Vector3 tilePos = new(corner.x + x, corner.y + y, 0);
                Vector3Int gridPosition = buildingsMap.WorldToCell(tilePos);

                if (y == 0)
                {
                    //TileBase tile = resourcesMap.GetTile(gridPosition);
                    //if (tile != null) buildingsMap.SetTile(gridPosition, tile);
                    buildingsMap.SetTile(gridPosition, tileTypeToBase[TileType.Available]);
                    availableBuildingAreaMap.SetTile(gridPosition, tileTypeToBase[TileType.Available]);
                }
                else
                {
                    buildingsMap.SetTile(gridPosition, tileTypeToBase[TileType.Empty]);
                    availableBuildingAreaMap.SetTile(gridPosition, tileTypeToBase[TileType.Empty]);
                }
            }
        }
    }
}
