using CommonAI.Zone;
using CommonAI.Zone.ZoneEditor;
using CommonLang;
using FairyGUI;
using Sirenix.Utilities;
using System;
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();

        public async ETTask CachePrefab(string name)
        {
            var handle = await YooAssetProxy.LoadAssetAsync<GameObject>(name);
            var prefab = handle.GetAssetObject<GameObject>();
            var gameobj = UnityEngine.Object.Instantiate(prefab, GlobalViewMgr.Instance.RecycleNode, true);
            gameobj.name = name;
            RecycleObject(gameobj);
        }

        //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)
            {
                await CachePrefab($"Unit_{unit.FileName}");

                if(unit.FootCircleEffect != null) CacheLaunchEffect(unit.FootCircleEffect, ref effectlist, ref soundlist);
                if (unit.SpawnEffect != null) CacheLaunchEffect(unit.SpawnEffect, ref effectlist, ref soundlist);
                if (unit.DeadAnimationEndEffect != null) CacheLaunchEffect(unit.DeadAnimationEndEffect, ref effectlist, ref soundlist);
                if (unit.DeadEffect != null) CacheLaunchEffect(unit.DeadEffect, ref effectlist, ref soundlist);
                if (unit.HitBreakEffect != null) CacheLaunchEffect(unit.HitBreakEffect, ref effectlist, ref soundlist);
                if (unit.ChangeForceEffect != null) CacheLaunchEffect(unit.ChangeForceEffect, ref effectlist, ref soundlist);
                if (unit.CrushEffect != null) CacheLaunchEffect(unit.CrushEffect, ref effectlist, ref soundlist);
                if (unit.DamageEffect != null) CacheLaunchEffect(unit.DamageEffect, ref effectlist, ref soundlist);
                if (unit.RemovedEffect != null) CacheLaunchEffect(unit.RemovedEffect, ref effectlist, ref soundlist);

                if (!unit.PreloadResources.IsNullOrWhitespace() )
                {
                    var split = unit.PreloadResources.Split(';');
                    foreach(var res in split)
                    {
                        var realres = res;
                        var cns = res.Split('|');
                        var cnt = 1;
                        if (cns.Length > 1)
                        {
                            realres = cns[0];
                            try
                            {
                                cnt = Convert.ToInt32(cns[1]);
                            }
                            catch
                            {
                                Log.Error($"PreloadResources illegal: {unit.PreloadResources}");
                                break;
                            }
                        }

                        for (var i = 0; i < cnt; i++)
                        {
                            await CachePrefab(realres);
                        }
                    }
                }
            }

            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)
                        {
                            for (var i = 0; i < kf.Spell.Count; i++)
                            {
                                CacheSpellEffect(templates, kf.Spell.SpellID, ref effectlist, ref soundlist);
                            }
                        }
                    }
                }
            }

            foreach (var ef in effectlist)
            {
                await CachePrefab($"Effect_{ef}");
            }

            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, int loopbackCheckid = 0)
        {
            if(spellid == loopbackCheckid)
            {
                Log.Error($"found spell loopback: {loopbackCheckid}");
                return;
            }

            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, spellid);
                }
                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, spellid);
                    }
                }
            }

            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, spellid);
                }
                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, spellid);
                    }
                }
            }

            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, spellid);
                }
                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, spellid);
                    }
                }
            }
        }

        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(GlobalViewMgr.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, GlobalViewMgr.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;
            }

            return await UIHelper.Create("HeadBar", "normalbar", -1, GlobalViewMgr.Instance.HeadbarView);
        }

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