using System;
using System.Collections.Generic;
using System.Linq;
using Actions;
using UnityEngine;
using UnityEngine.Serialization;

namespace Grid {
    public class GridSystemVisual : MonoBehaviour {
        public enum GridVisualType {
            White,
            Red,
            Blue,
            Yellow,
            RedSoft
        }

        [FormerlySerializedAs("GridSystemVisualSinglePrefab")] [SerializeField]
        private Transform gridSystemVisualSinglePrefab;

        [FormerlySerializedAs("gridVisualTypeMaterials")] [SerializeField]
        private List<GridVisualTypeMaterial> gridVisualTypeMaterialList;

        private GridSystemVisualSingle[,] gridSystemVisualSingleArray;
        private GridSystemVisualSingle lastSelectedGridSystemVisualSingle;
        private static GridSystemVisual Instance { get; set; }

        private void Awake() {
            if (Instance is not null) {
                Debug.LogError($"There is more than one GridSystemVisual! {transform} - {Instance}");
                Destroy(gameObject);
                return;
            }

            Instance = this;
        }

        // private void Update() {
        //     if (lastSelectedGridSystemVisualSingle is not null) lastSelectedGridSystemVisualSingle.SetSelected(false); //Deselected last one
        //     GridPosition gridPosition = LevelGrid.Instance.GetGridPosition(MouseWorld.GetPosition());
        //     if (LevelGrid.Instance.IsValidGridPosition(gridPosition)) {
        //         lastSelectedGridSystemVisualSingle = gridSystemVisualSingleArray[gridPosition.X, gridPosition.Z];
        //     }
        //     if (lastSelectedGridSystemVisualSingle is not null) lastSelectedGridSystemVisualSingle.SetSelected(true); //Select current one
        // }

        private void Start() {
            int width = LevelGrid.Instance.GetWidth();
            int height = LevelGrid.Instance.GetHeight();
            gridSystemVisualSingleArray = new GridSystemVisualSingle[width, height];

            for (int x = 0; x < width; x++) {
                for (int z = 0; z < height; z++) {
                    Transform gridSystemVisualSingleTransform = Instantiate(gridSystemVisualSinglePrefab, LevelGrid.Instance.GetWorldPosition(new(x, z)), Quaternion.identity);
                    gridSystemVisualSingleArray[x, z] = gridSystemVisualSingleTransform.GetComponent<GridSystemVisualSingle>();
                }
            }

            UnitActionSystem.Instance.OnSelectedActionChanged += UnitActionSystem_OnSelectedActionChanged;
            LevelGrid.Instance.OnAnyUnitMovedGridPosition += LevelGrid_OnAnyUnitMovedGridPosition;
            UpdateGridVisual();

            // for (int x = 0; x < LevelGrid.Instance.GetWidth(); x++) {
            //     for (int z = 0; z < LevelGrid.Instance.GetHeight(); z++) {
            //         gridSystemVisualSingleArray[x, z].Show(GetGridVisualTypeMaterial(GridVisualType.White));
            //     }
            // }
        }

        private void LevelGrid_OnAnyUnitMovedGridPosition(object sender, EventArgs e) => UpdateGridVisual();

        private void UnitActionSystem_OnSelectedActionChanged(object sender, EventArgs e) => UpdateGridVisual();

        private void HideAllGridPosition() {
            foreach (GridSystemVisualSingle gridSystemVisualSingle in gridSystemVisualSingleArray)
                gridSystemVisualSingle.Hide();
        }

        private void ShowGridPositionList(List<GridPosition> gridPositionList, GridVisualType gridVisualType) {
            foreach (GridPosition gridPosition in gridPositionList)
                gridSystemVisualSingleArray[gridPosition.X, gridPosition.Z].Show(GetGridVisualTypeMaterial(gridVisualType));
        }

        private void ShowGridPositionRange(GridPosition gridPosition, int range, GridVisualType gridVisualType) {
            List<GridPosition> gridPositionList = new();
            for (int x = -range; x <= range; x++) {
                for (int z = -range; z <= range; z++) {
                    GridPosition testGridPosition = gridPosition + new GridPosition(x, z);
                    if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition)) continue; //Only return valid grid positions
                    if (Mathf.Abs(x) + Mathf.Abs(z) > range) continue; //Out of Range
                    gridPositionList.Add(testGridPosition);
                }
            }

            ShowGridPositionList(gridPositionList, gridVisualType);
        }

        private void ShowGridPositionRangeSquare(GridPosition gridPosition, int range, GridVisualType gridVisualType) {
            List<GridPosition> gridPositionList = new();
            for (int x = -range; x <= range; x++) {
                for (int z = -range; z <= range; z++) {
                    GridPosition testGridPosition = gridPosition + new GridPosition(x, z);
                    if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition)) continue; //Only return valid grid positions
                    gridPositionList.Add(testGridPosition);
                }
            }

            ShowGridPositionList(gridPositionList, gridVisualType);
        }

        private void UpdateGridVisual() {
            HideAllGridPosition();
            BaseAction selectedAction = UnitActionSystem.Instance.SelectedAction;
            Unit selectedUnit = UnitActionSystem.Instance.SelectedUnit;
            GridVisualType gridVisualType;
            switch (selectedAction) {
                case SpinAction:
                    gridVisualType = GridVisualType.Blue;
                    break;
                case GrenadeAction:
                    gridVisualType = GridVisualType.Yellow;
                    break;
                case ShootAction:
                    gridVisualType = GridVisualType.Red;
                    ShowGridPositionRange(selectedUnit.GridPosition, selectedUnit.GetAction<ShootAction>().MaxShootDistance, GridVisualType.RedSoft);
                    break;
                case SwordAction:
                    gridVisualType = GridVisualType.Red;
                    ShowGridPositionRangeSquare(selectedUnit.GridPosition, selectedUnit.GetAction<SwordAction>().MaxSwordDistance, GridVisualType.RedSoft);
                    break;
                case InteractAction:
                    gridVisualType = GridVisualType.Blue;
                    break;
                default:
                    gridVisualType = GridVisualType.White;
                    break;
            }

            ShowGridPositionList(selectedAction.GetValidActionGridPositionList(), gridVisualType);
        }

        private Material GetGridVisualTypeMaterial(GridVisualType gridVisualType) {
            return (from gridVisualTypeMaterial in gridVisualTypeMaterialList where gridVisualTypeMaterial.gridVisualType == gridVisualType select gridVisualTypeMaterial.material).FirstOrDefault();
        }

        [Serializable]
        public struct GridVisualTypeMaterial {
            public GridVisualType gridVisualType;
            public Material material;
        }
    }
}