using System.Collections.Generic;
using UnityEngine;
using Sirenix.OdinInspector;
using DG.Tweening;

namespace mGame
{
    public class MGameManager : MonoBehaviour
    {
        #region 单例
        public static MGameManager Instance { get; private set; }
        public void SetInstance()
        {
            if (Instance == null) { Instance = this; }
            else { Destroy(gameObject); }
        }

        private void Awake()
        {
            SetInstance();
        }
        #endregion
        #region 地图
        [FoldoutGroup("Debug"),Button()]
        public void DoBubbleBlow(Vector3 pos,Block oldB, Block newB,int radiu = 1)
        {
            List<Vector2Int> toChange = radiu switch
            {
                0 => r0,
                1=>r1,
                2=>r2,
                3=>r3,  
                _ => r0
            };
            Vector2Int center = Convert(pos);
            for(int i = 0; i < toChange.Count; i++)
            {
                ChangeBlockByType(center + toChange[i],oldB, newB);
            }
        }
        List<Vector2Int> r0 = new List<Vector2Int> { Vector2Int.zero };
        List<Vector2Int> r1 = new List<Vector2Int> { Vector2Int.zero, Vector2Int.left, Vector2Int.right, Vector2Int.up, Vector2Int.down };
        List<Vector2Int> r2 = new List<Vector2Int> { Vector2Int.zero, new Vector2Int(-1,-1), new Vector2Int(-1, 0), new Vector2Int(-1, 1)
            , new Vector2Int(0, -1) , new Vector2Int(0,1), new Vector2Int(1,-1), new Vector2Int(1,0), new Vector2Int(1,1)
        }; 
        List<Vector2Int> r3 = new List<Vector2Int> { 
                                    new Vector2Int(-2,-1),      new Vector2Int(-2,0),   new Vector2Int(-2,1)
        ,new Vector2Int(-1,-2)      ,new Vector2Int(-1,-1),     new Vector2Int(-1, 0),  new Vector2Int(-1, 1),  new Vector2Int(-1,2)
        ,new Vector2Int(0,-2)       ,new Vector2Int(0, -1),     Vector2Int.zero,        new Vector2Int(0,1),    new Vector2Int(0,2)
        ,new Vector2Int(1,-2)       ,new Vector2Int(1,-1),      new Vector2Int(1,0),    new Vector2Int(1,1),    new Vector2Int(1,2)
                                    ,new Vector2Int(2,-1),      new Vector2Int(2,0),    new Vector2Int(2,1)
        };
        public Vector2Int Convert(Vector3 pos) { return new Vector2Int(Mathf.RoundToInt(pos.x), Mathf.RoundToInt(-pos.z)); }
        #endregion
        #region 游戏统计
        public UnityEngine.Rendering.SerializedDictionary<Block, int> blockCount;
        public void ResetCount() { blockCount.Clear(); }


        #endregion
        #region 可建造内容
        [FoldoutGroup("Building")] public int economy;
        public int Economy { get { return economy; } set { economy = value; economyTip.text = economy.ToString(); } }
        [FoldoutGroup("Building")] public int isSelecting;
        [FoldoutGroup("Building")] public Vector3 mousePos;
        [FoldoutGroup("Building")] public int[] costs;
        private Ray mouseRay;
        private RaycastHit mouseHit;
        [FoldoutGroup("Building")] public Dictionary<Vector2Int, BuildSlot> buildings = new();
        public struct BuildSlot
        {
            public GameObject build;
            public int cost;
        }

        private List<Block> bubbleEmpty = new List<Block> { };
        [FoldoutGroup("Building")] public GameObject bubbleGenerater;
        private List<Block> bubbleGenType = new List<Block> { Block.Water };
        [FoldoutGroup("Building")] public GameObject bubbleTranmitter;
        private List<Block> bubbleTransType = new List<Block> { Block.Desert,Block.Grass,Block.Water };
        [FoldoutGroup("Building")] public GameObject bubbleJumper;
        private List<Block> bubbleJumperType = new List<Block> { Block.Desert, Block.Grass, Block.Water };
        [FoldoutGroup("Building")] public GameObject bubbleBurster;
        private List<Block> bubbleBursterType = new List<Block> { Block.Desert, Block.Grass, Block.Water };

        [FoldoutGroup("Building")] public UnityEngine.Rendering.SerializedDictionary<Block, int> transAward;
        [FoldoutGroup("Building")] public TMPro.TextMeshProUGUI selectedTip;
        [FoldoutGroup("Building")] public TMPro.TextMeshProUGUI selectedCost;
        [FoldoutGroup("Building")] public TMPro.TextMeshProUGUI canBuildTip;
        [FoldoutGroup("Building")] public TMPro.TextMeshProUGUI economyTip;
        public void SetSelect(int idx) 
        { 
            isSelecting = idx;
            selectedTip.text = isSelecting switch
            {
                1=>"Bubble Generator",
                2=>"Bubble Accelerator",
                3=>"Bubble Jumper",
                4=>"Bubble Burster",
                _=>"None"
            };

            List<Block> checkType = isSelecting switch
            {
                1 => bubbleGenType,
                2 => bubbleTransType,
                3 => bubbleJumperType,
                4 => bubbleBursterType,
                _ => bubbleEmpty,
            };
            string canBuild = "Valid On\n";
            for(int i = 0; i < checkType.Count; i++)
            {
                canBuild += checkType[i].ToString() + "\n";
            }
            canBuildTip.text = canBuild;

            int cost = GetCost(idx);
            selectedCost.text = idx > 0 ?
                "Cost:\t" + cost.ToString()
                : string.Empty
                ;
        }
        public int GetCost(int idx)
        {
            if (idx < 0 || idx >= costs.Length) return 0;
            return costs[idx];
        }
        protected void UpdateMouse()
        {
            mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
            Physics.Raycast(mouseRay
                , out mouseHit
                , 300
                , 1 << LayerMask.NameToLayer("Mouse")
                , QueryTriggerInteraction.Collide
                );
            mousePos = mouseHit.point;
            mousePos.y = 0;
        }
        protected void DoAward(Block bt)
        {
            if (!transAward.TryGetValue(bt, out int val)) return;

            Economy += val;
            mAudio.PlayOneShot(onChange);
        }
        public void DoBuild(Vector3 pos)
        {
            pos.x = Mathf.RoundToInt(pos.x);
            pos.y = 0;
            pos.z = Mathf.RoundToInt(pos.z);

            if (NotInMap(Convert(pos))) return;

            GameObject toBuild = isSelecting switch
            {
                1=>bubbleGenerater,
                2=>bubbleTranmitter,
                3=>bubbleJumper,
                4=>bubbleBurster,
                _=>null
            };
            List<Block> checkType = isSelecting switch
            {
                1=>bubbleGenType,
                2=>bubbleTransType,
                3=>bubbleJumperType,
                4=>bubbleBursterType,
                _ => bubbleEmpty,
            };

            if (toBuild == null) return;
            Vector2Int atPos = Convert(pos);
            if (!checkType.Contains(GetBlock(atPos))) return;
            if (buildings.ContainsKey(atPos)) return;
            int cost = GetCost(isSelecting);
            if (economy < cost) return;
            Economy -= cost;

            toBuild = ObjectPool.Instance.GetObject(toBuild,pos);
            buildings.Add(atPos, new BuildSlot() 
            {
                build = toBuild,
                cost = cost,
            }
            );
        }
        public void DoRemove(Vector3 pos)
        {
            pos.x = Mathf.RoundToInt(pos.x);
            pos.y = 0;
            pos.z = Mathf.RoundToInt(pos.z);
            Vector2Int atPos = Convert(pos);
            if (NotInMap(atPos)) return;
            if (!buildings.TryGetValue(atPos, out BuildSlot bl)) return;


            Economy += bl.cost;
            bl.build.GetComponent<BlockBase>().OnDisable();
            buildings.Remove(atPos);
        }
        #endregion
        [FoldoutGroup("Component")] public Transform mapRoot;

        [FoldoutGroup("RunTime")] public float runTime;
        [FoldoutGroup("RunTime")] public List<RecycleSlot> recycles = new();

        [FoldoutGroup("RunTime_Map")] public Vector3 centerPos;
        [FoldoutGroup("RunTime_Map")] public bool enableCamMove;
        [FoldoutGroup("RunTime_Map")] public float camValidDis;
        [FoldoutGroup("RunTime_Map")] public float camMoveSpeed = 3f;
        [FoldoutGroup("RunTime_Map")] public Transform camTarget;
        [PropertySpace(10)]
        [FoldoutGroup("RunTime_Map")] public LevelListSO levelList;
        [FoldoutGroup("RunTime_Map")] public int curLevIdx;
        [FoldoutGroup("RunTime_Map")] public LevelSO currentLevel;
        [FoldoutGroup("RunTime_Map")] public MapSO mapBuffer;
        [FoldoutGroup("RunTime_Map")] public float levelSwitchTime = 2f;
        [FoldoutGroup("RunTime_Map")] public float levelStartFreeze = 3f;
        [FoldoutGroup("RunTime_Map")] public bool levelEnable;
        [FoldoutGroup("RunTime_Map")] public UnityEngine.VFX.VisualEffect onSuccessVFX;
        [FoldoutGroup("RunTime_Map")] public AudioClip onChange;
        [FoldoutGroup("RunTime_Map")] public AudioClip onSuccess;
        [FoldoutGroup("RunTime_Map")] public AudioSource mAudio;
        [PropertySpace(10)]
        [FoldoutGroup("RunTime_Map")] public List<GameObject> blockBuffer = new();
        [FoldoutGroup("RunTime_Map")] public Dictionary<Vector2Int, GameObject> mapBlocks = new();
        [PropertySpace(10)]
        [FoldoutGroup("RunTime_Map")] public GameObject windHeight;
        [FoldoutGroup("RunTime_Map")] public List<GameObject> winds = new();
        [FoldoutGroup("RunTime_Map")] public List<Vector3> highPre = new();
        [FoldoutGroup("RunTime_Map")] public List<Vector3> lowPre = new();
        [FoldoutGroup("RunTime_Map")] public TMPro.TextMeshProUGUI remainDesertText;
        [FoldoutGroup("RunTime_Map")] public TMPro.TextMeshProUGUI levelName;
        #region 流程控制
        public void CheckSuccess()
        {
            if ((!blockCount.ContainsKey(Block.Desert) || blockCount[Block.Desert] == 0)
                && (!blockCount.ContainsKey(Block.DryTown) || blockCount[Block.DryTown] == 0)
                )
            {
                GoNextLevel();
            }
        }
        public void GoNextLevel()
        {
            curLevIdx++;
            onSuccessVFX.Play();
            mAudio.PlayOneShot(onSuccess);
            if (curLevIdx >= levelList.levelList.Count) return;

            currentLevel = levelList.levelList[curLevIdx];
            transform.DOLocalMove(Vector3.zero, levelSwitchTime)
                .OnComplete(() => { LoadLevel(currentLevel); });
        }
        #endregion
        #region 地块编辑
        [FoldoutGroup("Debug"),Button()] 
        public void LoadLevel(LevelSO level)
        {
#if UNITY_EDITOR
            if (!Application.isPlaying) return;
#endif
            levelEnable = false;

            //加载地图数据
            currentLevel = level;
            Vector2Int mapSize = level.mapSize;
            mapBuffer.mapSize = mapSize;
            mapBuffer.mapData = new Block[mapSize.x, mapSize.y];
            Economy = level.initEconomy;
            levelName.text = level.levelName;

            //数据重置
            ClearMap();

            //清空缓存
            highPre.Clear();
            lowPre.Clear();

            for (int i=0;i<mapSize.x;i++)
                for(int j=0;j<mapSize.y;j++)
                {
                    Block bt = level.map[i, j];
                    if (bt == Block.None) continue;
                    //生成
                    if (!GenBlock(new Vector2Int(i, j),bt)) continue;
                    blockCount.TryAdd(bt, 0);

                    //缓存风
                    if (level.mapWind[i, j] == Wind.Height) highPre.Add(new Vector3(i, 0, -j));
                    if (level.mapWind[i, j] == Wind.Low) lowPre.Add(new Vector3(i, 0, -j));
                }

            for(int i = 0; i < highPre.Count; i++)
            {
                Wind_Height wh = ObjectPool.Instance.GetObject(windHeight,highPre[i]).GetComponent<Wind_Height>();
                wh.DoInit(lowPre);

                winds.Add(wh.gameObject);
            }

            centerPos = new Vector3(mapSize.x / 2f, 0, -mapSize.y / 2f);
            camValidDis = Mathf.Max(mapSize.x, mapSize.y) / 2;
            camTarget.position = centerPos;

            transform.DOLocalMove(Vector3.zero, levelStartFreeze)
                .OnComplete(() => {
                    levelEnable = true;
                    CheckSuccess();
                });
        }
        public void ClearMap()
        {
            foreach(var bu in buildings)
            {
                bu.Value.build.GetComponent<BlockBase>().OnDisable();
            }
            buildings.Clear();
            foreach(var bl in mapBlocks)
            {
                bl.Value.GetComponent<BlockBase>().OnDisable();
            }
            mapBlocks.Clear();
            foreach(var wh in winds)
            {
                wh.GetComponent<BlockBase>().OnDisable();
            }
            ResetCount();
        }
        public Block GetBlock(Vector2Int pos)
        {
            if (pos.x < 0 || pos.x >= mapBuffer.mapSize.x || pos.y < 0 || pos.y >= mapBuffer.mapSize.y)
            {
                return Block.None;
            }
            else return mapBuffer.GetBlock(pos);
        }
        public bool GenBlock(Vector2Int idx, Block type)
        {
            mapBuffer.mapData[idx.x, idx.y] = type;
            GameObject newPrefab = mapBuffer.GetBlockPrefab(type);
            if (newPrefab == null) return false;

            newPrefab = ObjectPool.Instance.GetObject(newPrefab, new Vector3(idx.x, 0, -idx.y));
            blockBuffer.Add(newPrefab);
            newPrefab.transform.SetParent(mapRoot);
            mapBlocks.Add(idx, newPrefab);

            blockCount.TryAdd(type, 0);
            blockCount[type]++;

            ShowLevelInfo();

            return true;
        }
        public bool RemoveBlock(Vector2Int idx)
        {
            if (mapBlocks.ContainsKey(idx))
            {
                mapBlocks[idx].GetComponent<BlockBase>().OnDisable();
                mapBlocks.Remove(idx);

                return true;
            }
            return false;
        }
        public void ChangeBlock(Vector2Int pos,Block newB)
        {
            if (NotInMap(pos)) return;
            RemoveBlock(pos);
            GenBlock(pos, newB);
        }
        public void ChangeBlockByType(Vector3 pos, Block oldB, Block newB)
        {
            Vector2Int p = Convert(pos);
            ChangeBlockByType(p, oldB, newB);
        }
        public void ChangeBlockByType(Vector2Int pos, Block oldB, Block newB)
        {
            if (!levelEnable) return;
            if (GetBlock(pos) == oldB)
            {
                blockCount[oldB]--;
                DoAward(newB);
                ChangeBlock(pos, newB);

                CheckSuccess();
            }
        }
        public bool NotInMap(Vector2Int pos)
        {
            return pos.x < 0 || pos.x >= mapBuffer.mapSize.x || pos.y < 0 || pos.y >= mapBuffer.mapSize.y;

        }
        #endregion
        #region 关卡信息展示
        public void ShowLevelInfo()
        {
            if (!blockCount.TryGetValue(Block.Desert, out int val)) return;
            remainDesertText.text = val.ToString();
        }

        #endregion
        #region 镜头控制
        protected void DoCamMove()
        {
            if (!enableCamMove) return;

            Vector3 curPos = camTarget.position;
            Vector3 moveDir = (mousePos - curPos).normalized;
            curPos += camMoveSpeed * Time.fixedDeltaTime * moveDir;
            curPos.y = 0;

            float dis = Vector3.Distance(curPos, centerPos);
            if (dis > camValidDis)
            {
                Vector3 curDir = curPos - centerPos;
                curPos = centerPos + curDir.normalized * camValidDis;
            }

            camTarget.position = curPos;
        }
        #endregion
        #region 回收队列
        [System.Serializable]
        public struct RecycleSlot
        {
            public float recycleTime;
            public GameObject go;
        }
        
        public void AddRecycle(GameObject go,float delay = 1f)
        {
            RecycleSlot rs = new()
            {
                recycleTime = runTime + delay,
                go = go
            };
            recycles.Add(rs);
        }
        protected void DoRecycle()
        {
            for(int i = 0; i < recycles.Count;i++)
                if (recycles[0].recycleTime < runTime)
                {
                    ObjectPool.Instance.PushObject(recycles[i].go);
                    recycles.RemoveAt(0);
                }
                else break;
            
        }

        #endregion

        [FoldoutGroup("Start")] public bool startYet;
        [FoldoutGroup("Start")] public Transform UIRoot;
        [FoldoutGroup("Start")] public float uiLerp = 3f;
        private void DoStart()
        {
            if (startYet) return;
            startYet = true;
            UIRoot.DOLocalMoveY(0, uiLerp).SetEase(Ease.InOutCubic);
            currentLevel = levelList.levelList[0];
            LoadLevel(currentLevel);
        }
        private void Update()
        {
            UpdateMouse();
            if (!startYet && Input.anyKeyDown) DoStart();
            if (Input.GetMouseButtonDown(0)) DoBuild(mousePos);
            if (Input.GetMouseButtonDown(1)) DoRemove(mousePos);
            if (Input.GetKeyDown(KeyCode.R) && currentLevel) LoadLevel(currentLevel);
            if (Input.GetKeyDown(KeyCode.Escape)) Application.Quit();
        }
        private void FixedUpdate()
        {
            runTime += Time.fixedDeltaTime;
            DoCamMove();        //移动镜头
            DoRecycle();        //对象回收
        }


    }

    public enum Block
    {
        None,
        Grass,
        Water,
        Dirt,
        Desert,
        Mountain,
        Tree,
        DryTown,
        GreenTown
    }

    public enum Wind
    {
        None,
        Height,
        Low
    }

}



