using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CodeMonkey.Utils;
using UnityEngine;

public class GroundGridControllerScript : MonoBehaviour {

    public static GroundGridControllerScript Instance { get; private set; }

    public event EventHandler OnSelectedChanged;
    public event EventHandler OnObjectPlaced;
    private GridXZ<BuildingGridNode> grid;
    [SerializeField] List<PlacedObjectTypeSO> placedObjectTypeSOList;
    PlacedObjectTypeSO activePlacedObjectTypeSO;
    [SerializeField] LayerMask mousePlaneLayerMask;


    PlacedObjectTypeSO.Dir dir = PlacedObjectTypeSO.Dir.Down;
    int gridWidth = 50;
    int gridHeight = 50;
    float cellSize = 10f;
    private void Awake() {
        Instance = this;
        grid = new GridXZ<BuildingGridNode>(gridWidth, gridHeight, cellSize, Vector3.zero, (GridXZ<BuildingGridNode> g, int x, int y) => new BuildingGridNode(g, x, y));
    }

    internal Quaternion GetPlacedObjectRotation() {
        if (activePlacedObjectTypeSO != null) {
            return Quaternion.Euler(0, activePlacedObjectTypeSO.GetRotationAngle(dir), 0);
        } else {
            return Quaternion.identity;
        }
    }

    internal Vector2Int GetPlacedObjectRotationOffset() {
        if (activePlacedObjectTypeSO != null) {
            return activePlacedObjectTypeSO.GetRotationOffset(dir);
        } else {
            return Vector2Int.zero;
        }
    }

    internal PlacedObjectTypeSO GetActiveObjectTypeSO() {
        return activePlacedObjectTypeSO;
    }

    internal Vector3 GetMouseWorldSnappedPosition() {
        Vector3 mousePosition = GetMousePosition3d();
        grid.GetXZ(mousePosition, out int x, out int z);

        if (activePlacedObjectTypeSO != null) {
            Vector2Int rotationOffset = activePlacedObjectTypeSO.GetRotationOffset(dir);
            Vector3 placedObjectWorldPosition = grid.GetWorldPosition(x, z) + new Vector3(rotationOffset.x, 0, rotationOffset.y);
            return placedObjectWorldPosition;
        } else {
            return mousePosition;
        }

    }

    public GridXZ<BuildingGridNode> GetGridXZ() {
        return grid;
    }

    private void Update() {
        if (Input.GetMouseButtonDown(0) && activePlacedObjectTypeSO != null) {
            grid.GetXZ(GetMousePosition3d(), out int x, out int z);
            if (x >= 0 && z >= 0) {
                List<Vector2Int> gridPositionList = activePlacedObjectTypeSO.GetGridPositionList(new Vector2Int(x, z), dir);

                if (gridPositionList.TrueForAll(p => ((BuildingGridNode)grid.GetGridObject(p.x, p.y)).CanBuild(activePlacedObjectTypeSO))) {
                    Vector2Int rotationOffset = activePlacedObjectTypeSO.GetRotationOffset(dir);
                    Vector3 placedObejctWorldPosition = grid.GetWorldPosition(x, z) + new Vector3(rotationOffset.x, 0, rotationOffset.y) * grid.GetCellSize();

                    Transform transform = Instantiate(
                        activePlacedObjectTypeSO.prefab,
                        placedObejctWorldPosition,
                        Quaternion.Euler(0, activePlacedObjectTypeSO.GetRotationAngle(dir), 0));
                    foreach (Vector2Int gridPosition in gridPositionList) {
                        grid.GetGridObject(gridPosition.x, gridPosition.y).SetTransform(transform);
                    }

                    OnObjectPlaced?.Invoke(this, EventArgs.Empty);
                } else {
                    UtilsClass.CreateWorldTextPopup("Connot build here!", GetMousePosition3d());
                }
            }
        } else if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape)) {
            DeselectObjectType();
        }

        if (Input.GetKeyDown(KeyCode.R)) {
            dir = PlacedObjectTypeSO.GetNextDir(dir);
        }
    }

    public void SetActiveObejctToPlace(PlacedObjectTypeSO o) {
        activePlacedObjectTypeSO = o;
        OnSelectedChanged?.Invoke(this, EventArgs.Empty);
    }

    public Vector3 GetMousePosition3d() {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Physics.Raycast(ray, out RaycastHit raycastHit, 999f, mousePlaneLayerMask);

        return raycastHit.point;
    }

    private void DeselectObjectType() {
        activePlacedObjectTypeSO = null;
        RefreshSelectedObjectType();
    }

    private void RefreshSelectedObjectType() {
        OnSelectedChanged?.Invoke(this, EventArgs.Empty);
    }
}
