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 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 Start() {
            int width = LevelGrid.Instance.GetWidth();
            int height = LevelGrid.Instance.GetHeight();
            int floorAmount = LevelGrid.Instance.FloorAmount;
            gridSystemVisualSingleArray = new GridSystemVisualSingle[width, height, floorAmount];

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

            UnitActionSystem.Instance.OnSelectedActionChanged += UnitActionSystem_OnSelectedActionChanged;
            UnitActionSystem.Instance.OnBusyChanged += UnitActionSystem_OnOnBusyChanged;
            // LevelGrid.Instance.OnAnyUnitMovedGridPosition += LevelGrid_OnAnyUnitMovedGridPosition;
            UpdateGridVisual();
        }

        private void UnitActionSystem_OnOnBusyChanged(object sender, bool e) => UpdateGridVisual();

        // 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.XPosition, gridPosition.ZPosition, gridPosition.Floor].Show(GetGridVisualTypeMaterial(gridVisualType));
        }

        private void ShowGridPositionRange(GridPosition gridPosition, int range, GridVisualType gridVisualType) {
            List<GridPosition> gridPositionList = new();
            for (int xPosition = -range; xPosition <= range; xPosition++) {
                for (int zPosition = -range; zPosition <= range; zPosition++) {
                    GridPosition testGridPosition = gridPosition + new GridPosition(xPosition, zPosition, 0);
                    if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition)) continue; //Only return valid grid positions
                    if (Mathf.Abs(xPosition) + Mathf.Abs(zPosition) > 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 xPosition = -range; xPosition <= range; xPosition++) {
                for (int zPosition = -range; zPosition <= range; zPosition++) {
                    GridPosition testGridPosition = gridPosition + new GridPosition(xPosition, zPosition, 0);
                    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;
        }
    }
}