using CommonAI.Zone;
using CommonAI.Zone.ZoneEditor;
using CommonLang;
using FairyGUI;
using Sirenix.Utilities;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;

namespace ET.Client
{
    [FriendOf(typeof(GameObjectPool))]
    public static class GameObjectPoolSystem
    {
        [ObjectSystem]
        public class GameObjectPoolAwakeSystem : AwakeSystem<GameObjectPool>
        {
            protected override void Awake(GameObjectPool self)
            {
                GameObjectPool.Instance = self;
            }
        }

        [ObjectSystem]
        public class GameObjectPoolDestroySystem : DestroySystem<GameObjectPool>
        {
            protected override void Destroy(GameObjectPool self)
            {
                self.ClearCache();
                GameObjectPool.Instance = null;
            }
        }
    }

    [ComponentOf(typeof(Scene))]
    public class GameObjectPool : Entity, IAwake, IDestroy
    {
        [StaticField]
        public static GameObjectPool Instance;

        private readonly HashMap<string, List<GameObject>> goPool = new();
        private readonly HashMap<string, AudioClip> audioPool = new();
        private readonly List<GComponent>headBarPool = new();

        //TODO: 选取当前场景的单位、技能(事件触发等等)
        //TODO: BUFF
        public async ETTask CacheSceneObject(int scnId)
        {
            var mgr = BattleResourceMgr.Instance.GameEditorTemplates;
            var templates = mgr.Templates;

            SceneData sceneData = mgr.LoadScene(scnId, false, true);
            BattleMgr.Instance.Layer.Data = sceneData;

            List<string> effectlist = new();
            List<string> soundlist = new();

            if(!sceneData.BGM.IsNullOrWhitespace())
            {
                soundlist.Add(sceneData.BGM);
            }

            //遍历模型
            var units = templates.getUnits();
            foreach(var unit in units.Values)
            {
                var name = $"Unit_{unit.FileName}";
                var handle = await YooAssetProxy.LoadAssetAsync<GameObject>(name);
                var prefab = handle.GetAssetObject<GameObject>();
                var gameobj = UnityEngine.Object.Instantiate(prefab, GlobalViewComponent.Instance.RecycleNode, true);
                gameobj.name = name;
                RecycleObject(gameobj);

                if(unit.FootCircleEffect != null) CacheLaunchEffect(unit.FootCircleEffect, ref effectlist, ref soundlist);
                if (unit.SpawnEffect != null) CacheLaunchEffect(unit.SpawnEffect, ref effectlist, ref soundlist);
                if (unit.DeadActionEffect != null) CacheLaunchEffect(unit.DeadActionEffect, ref effectlist, ref soundlist);
            }

            var skills = templates.getAllSkillData();
            foreach (var skt in skills.Values)
            {
                foreach (var act in skt.ActionQueue)
                {
                    if (!act.ActionEffectFileName.IsNullOrWhitespace())
                    {
                        effectlist.Add(act.ActionEffectFileName);
                    }
                    foreach (var kf in act.KeyFrames)
                    {
                        if (kf.Effect != null)
                        {
                            CacheLaunchEffect(kf.Effect, ref effectlist, ref soundlist);
                        }
                        if (kf.Spell != null)
                        {
                            CacheSpellEffect(templates, kf.Spell.SpellID, ref effectlist, ref soundlist);
                        }
                    }
                }
            }

            foreach (var ef in effectlist)
            {
                var name = $"Effect_{ef}";
                var handle = await YooAssetProxy.LoadAssetAsync<GameObject>(name);
                var prefab = handle.GetAssetObject<GameObject>();
                var gameobj = UnityEngine.Object.Instantiate(prefab, GlobalViewComponent.Instance.RecycleNode, true);
                gameobj.name = name;
                RecycleObject(gameobj);
            }

            foreach(var sound in soundlist)
            {
                var key = sound;
                var m = Regex.Match(key, "/?res/sound/.*/([\\w_\\d]+)\\.assetbundles$");
                if (m.Success)
                {
                    key = m.Groups[1].Value.ToLower();
                }
                if(audioPool.ContainsKey(key))
                {
                    continue;
                }
                //Log.Debug($"cache audio: {key}");
                var handle = await YooAssetProxy.LoadAssetAsync<AudioClip>($"Sound_{key}");
                var ac = handle.GetAssetObject<AudioClip>();
                ac.LoadAudioData();
                audioPool.Add(key, ac);
            }
        }

        private void CacheLaunchEffect(LaunchEffect effect, ref List<string>effectlist, ref List<string> soundlist)
        {
            if(!effect.Name.IsNullOrWhitespace())
            {
                effectlist.Add(effect.Name);
            }
            if(!effect.SoundName.IsNullOrWhitespace())
            {
                soundlist.Add(effect.SoundName);
            }
        }

        private void CacheSpellEffect(TemplateManager templates, int spellid, ref List<string> effectlist, ref List<string> soundlist)
        {
            var spell = templates.getSpell(spellid);
            if (spell == null)
            {
                Log.Error($"spell({spellid}) not exist");
                return;
            }
            if (!spell.FileName.IsNullOrWhitespace())
            {
                effectlist.Add(spell.FileName);
            }

            var frame = spell.HitIntervalKeyFrame;
            if (frame != null)
            {
                if (frame.Effect != null)
                {
                    CacheLaunchEffect(frame.Effect, ref effectlist, ref soundlist);
                }
                if (frame.Spell != null && frame.Spell.SpellID != 0)
                {
                    CacheSpellEffect(templates, frame.Spell.SpellID, ref effectlist, ref soundlist);
                }
                if (frame.Attack != null)
                {
                    var attack = frame.Attack;
                    if (attack.Effect != null)
                    {
                        CacheLaunchEffect(attack.Effect, ref effectlist, ref soundlist);
                    }
                    if (attack.Spell != null && attack.Spell.SpellID != 0)
                    {
                        CacheSpellEffect(templates, attack.Spell.SpellID, ref effectlist, ref soundlist);
                    }
                }
            }

            frame = spell.HitOnExplosionKeyFrame;
            if (spell.HitOnExplosion && frame != null)
            {
                if (frame.Effect != null)
                {
                    CacheLaunchEffect(frame.Effect, ref effectlist, ref soundlist);
                }
                if (frame.Spell != null && frame.Spell.SpellID != 0)
                {
                    CacheSpellEffect(templates, frame.Spell.SpellID, ref effectlist, ref soundlist);
                }
                if (frame.Attack != null)
                {
                    var attack = frame.Attack;
                    if (attack.Effect != null)
                    {
                        CacheLaunchEffect(attack.Effect, ref effectlist, ref soundlist);
                    }
                    if (attack.Spell != null && attack.Spell.SpellID != 0)
                    {
                        CacheSpellEffect(templates, attack.Spell.SpellID, ref effectlist, ref soundlist);
                    }
                }
            }

            foreach (var frm in spell.KeyFrames)
            {
                if (frm.Effect != null)
                {
                    CacheLaunchEffect(frm.Effect, ref effectlist, ref soundlist);
                }
                if (frm.Spell != null && frm.Spell.SpellID != 0)
                {
                    CacheSpellEffect(templates, frm.Spell.SpellID, ref effectlist, ref soundlist);
                }
                if (frm.Attack != null)
                {
                    var attack = frm.Attack;
                    if (attack.Effect != null)
                    {
                        CacheLaunchEffect(attack.Effect, ref effectlist, ref soundlist);
                    }
                    if (attack.Spell != null && attack.Spell.SpellID != 0)
                    {
                        CacheSpellEffect(templates, attack.Spell.SpellID, ref effectlist, ref soundlist);
                    }
                }
            }
        }

        public void ClearCache()
        {
            foreach (var list in goPool.Values)
            {
                foreach (var go in list)
                {
                    GameObject.Destroy(go);
                }
            }
            goPool.Clear();

            foreach(var ac in audioPool.Values)
            {
                ac.UnloadAudioData();
            }
            audioPool.Clear();
        }

        public void RecycleObject(GameObject go, string key = "")
        {
            if(key.IsNullOrWhitespace())
            {
                key = go.name;
            }
            //Log.Debug($"recycle gameobject: {key}");
            go.SetActive(false);
            go.transform.SetParent(GlobalViewComponent.Instance.RecycleNode, false);
            if (goPool.TryGetValue(key, out var golist))
            {
                golist.Add(go);
            }
            else
            {
                goPool.Add(key, new List<GameObject>() { go });
            }
        }

        public async ETTask<GameObject> Acquire(string key)
        {
            if (goPool.TryGetValue(key, out var golist))
            {
                if (golist.Count > 0)
                {
                    var gobj = golist[0];
                    golist.RemoveRange(0, 1);
                    return gobj;
                }
            }
            else
            {
                goPool.Add(key, new List<GameObject>());
            }
            Log.Warning($"not cache gameobject: ({key})");

            var handle = await YooAssetProxy.LoadAssetAsync<GameObject>(key);
            var prefab = handle.GetAssetObject<GameObject>();
            var gameobj = UnityEngine.Object.Instantiate(prefab, GlobalViewComponent.Instance.RecycleNode, true);
            gameobj.name = key;
            return gameobj;
        }

        public async ETTask<AudioClip> AcquireSound(string key)
        {
            if(audioPool.TryGetValue(key, out var ac))
            {
                return ac;
            }
            Log.Warning($"not cache audio: ({key})");

            var handle = await YooAssetProxy.LoadAssetAsync<AudioClip>($"Sound_{key}");
            var aac = handle.GetAssetObject<AudioClip>();
            aac.LoadAudioData();
            audioPool.Add(key, aac);
            return aac;
        }

        public async ETTask<GComponent> AcquireHeadBar()
        {
            if(headBarPool.Count > 0)
            {
                var ret = headBarPool[0];
                headBarPool.RemoveAt(0);
                return ret;
            }

            var root = GlobalViewComponent.Instance.HeadbarView;
            if (root == null)
            {
                //加入所有headbar的parent view
                root = await UIHelper.Create("HeadBar", "HeadBarRoot", 0);
                GlobalViewComponent.Instance.HeadbarView = root;
                root.visible = false;
            }

            var view = await UIHelper.Create("HeadBar", "normalbar", -1, root);
            return view;
        }

        public void RecycleHeadBar(GComponent view)
        {
            headBarPool.Add(view);
        }
    }
}