using System;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Grid {
    public class GridSystem<TGridObject> {
        private readonly TGridObject[,] gridObjectArray;

        public GridSystem(int width, int height, float cellSize, int floor, float floarHeight, Func<GridSystem<TGridObject>, GridPosition, TGridObject> createGridObject) {
            Width = width;
            Height = height;
            CellSize = cellSize;
            Floor = floor;
            FloorHeight = floarHeight;

            gridObjectArray = new TGridObject[width, height];
            for (int xPosition = 0; xPosition < width; xPosition++) {
                for (int zPosition = 0; zPosition < height; zPosition++) {
                    gridObjectArray[xPosition, zPosition] = createGridObject(this, new(xPosition, zPosition, floor));
                }
            }
        }

        public int Height { get; }
        public int Width { get; }
        private float CellSize { get; }
        private int Floor { get; }
        private float FloorHeight { get; }

        public Vector3 GetWorldPosition(GridPosition gridPosition) => new Vector3(gridPosition.XPosition, 0, gridPosition.ZPosition) * CellSize + new Vector3(0, gridPosition.Floor, 0) * FloorHeight;

        public GridPosition GetGridPosition(Vector3 worldPosition) => new(Mathf.RoundToInt(worldPosition.x / CellSize), Mathf.RoundToInt(worldPosition.z / CellSize), Floor);

        public void CreateDebugObjects(Transform debugPrefab) {
            for (int xPosition = 0; xPosition < Width; xPosition++) {
                for (int zPosition = 0; zPosition < Height; zPosition++) {
                    GridPosition gridPosition = new(xPosition, zPosition, Floor);
                    Transform debugTransform = Object.Instantiate(debugPrefab, GetWorldPosition(gridPosition), Quaternion.identity);
                    GridDebugObject gridDebugObject = debugTransform.GetComponent<GridDebugObject>();
                    gridDebugObject.SetGridObject(GetGridObject(gridPosition));
                }
            }
        }

        public TGridObject GetGridObject(GridPosition gridPosition) => gridObjectArray[gridPosition.XPosition, gridPosition.ZPosition];

        public bool IsValidGridPosition(GridPosition gridPosition) => gridPosition is { XPosition: >= 0, ZPosition: >= 0 } && gridPosition.XPosition < Width && gridPosition.ZPosition < Height && gridPosition.Floor == Floor;
    }
}