using UnityEngine;
using CommonAIClient.Client;
using System;
using CommonAI.ZoneClient;
using CommonLang;
using CommonLang.Concurrent;
using CommonAI.Zone;
using CommonAIClient.Unity.Utils;
using CommonAI.Zone.ZoneEditor;

namespace CommonAIClient.Unity.Battle
{

    public partial class BattleScene : IDisposable
    {
        #region RefCount

        private static AtomicInteger s_alloc_count = new AtomicInteger(0);
        private static AtomicInteger s_active_count = new AtomicInteger(0);

        /// <summary>
        /// 分配实例数量
        /// </summary>
        public static int AllocCount { get { return s_alloc_count.Value; } }
        /// <summary>
        /// 未释放实例数量
        /// </summary>
        public static int ActiveCount { get { return s_active_count.Value; } }

        #endregion


        private bool mDisposed = false;
        private AbstractBattle mBattle;
        protected GameObject mMapRoot;
        /// <summary>
        /// 不绑定任何东西的特效会存在在这里
        /// </summary>
        private GameObject mEffectRoot;

        public bool IsDisposed { get { return mDisposed; } }
        protected AbstractBattle Battle { get { return mBattle; } }
        protected GameObject MapRoot { get { return mMapRoot; } }
        public GameObject EffectRoot { get { return mEffectRoot; } }
        public EditorTemplates DataRoot { get { return mBattle.DataRoot; } }
        public ZoneInfo Terrain { get { return mBattle.Layer.Terrain; } }
        public SceneData SceneData { get { return this.Battle.Layer.Data; } }
        public int SceneID { get { return this.Battle.Layer.SceneID; } }

        protected BattleScene(AbstractBattle client)
        {
            s_alloc_count++;
            s_active_count++;

            mBattle = client;
            mBattle.Layer.LayerInit += Layer_LayerInit;
            mBattle.Layer.ObjectEnter += Layer_ObjectEnter;
            mBattle.Layer.ObjectLeave += Layer_ObjectLeave;
            mBattle.Layer.MessageReceived += Layer_MessageReceived;
            mBattle.Layer.DecorationChanged += Layer_DecorationChanged;
            mBattle.Layer.OnChangeBGM += Layer_OnChangeBGM;

            mEffectRoot = new GameObject("EffectNode");

            RegistAllZoneEvent();
        }

        ~BattleScene()
        {
            s_alloc_count--;
        }

        protected virtual void OnLoadMapFinish()
        {

        }

        private void OnMapLoadFinish(bool success, GameObject o)
        {
            if (success)
            {
                mMapRoot = o;
                InitDecoration();
            }
        }

        public void Dispose()
        {
            if (!mDisposed)
            {
                OnDispose();
                OnDisposeMapRoot();
                mDisposed = true;
                s_active_count--;
            }
        }
        protected virtual void OnDisposeMapRoot()
        {
            GameObject.DestroyObject(mMapRoot);
        }
        protected virtual void OnDispose()
        {
            foreach (var elem in mBattleObjects)
            {
                elem.Value.Dispose();
            }
            mBattleObjects.Clear();

            mBattle.Layer.LayerInit -= Layer_LayerInit;
            mBattle.Layer.ObjectEnter -= Layer_ObjectEnter;
            mBattle.Layer.ObjectLeave -= Layer_ObjectLeave;
            mBattle.Layer.DecorationChanged -= Layer_DecorationChanged;

            AssetObjectExt[] aoes = mEffectRoot.GetComponentsInChildren<AssetObjectExt>();
            foreach (var elem in aoes)
            {
                EffectAutoDestroy[] scripts = elem.gameObject.GetComponents<EffectAutoDestroy>();
                if (scripts.Length > 0)
                {
                    foreach (var script in scripts)
                    {
                        script.DoDestroy();
                    }
                }
                else
                {
                    BattleFactroy.Instance.GameObjectAdapter.Unload(elem);
                }
            }
            GameObject.DestroyObject(mEffectRoot);
        }

        public void Update(float deltaTime)
        {
            if (!mDisposed)
            {
                OnUpdate(deltaTime);
            }
        }

        protected virtual void OnUpdate(float deltaTime)
        {
            foreach (var elem in mBattleObjects)
            {
                elem.Value.Update(deltaTime);
            }
        }

        #region Objects

        private HashMap<uint, ComAICell> mBattleObjects = new HashMap<uint, ComAICell>();
        private IComAIActor mActor;


        public ComAICell GetBattleObject(uint id)
        {
            ComAICell outVal = null;
            mBattleObjects.TryGetValue(id, out outVal);
            return outVal;
        }

        public IComAIActor GetActor()
        {
            return mActor;
        }
        
        private void AddBattleObject(ComAICell obj)
        {
            if (obj != null)
            {
                mBattleObjects.Add(obj.ObjectID, obj);
                if (obj is ComAIActor)
                {
                    mActor = obj as ComAIActor;
                }
                OnComAICellAdded(obj);
            }
        }

        private void RemoveBattleObject(uint id)
        {
            ComAICell outVal = mBattleObjects.RemoveByKey(id);
            if (outVal != null)
            {
                OnComAICellRemoved(outVal);
                outVal.Dispose();
            }
        }

        protected virtual void OnComAICellAdded(ComAICell o) { }

        protected virtual void OnComAICellRemoved(ComAICell o) { }

        protected virtual void Layer_LayerInit(ZoneLayer layer)
        {
            BattleFactroy.Instance.TerrainAdapter.Load(layer.Data.FileName, OnMapLoadFinish);
            BattleFactroy.Instance.SoundAdapter.PlayBGM(layer.Data.BGM);
        }

        private void Layer_ObjectEnter(CommonAI.ZoneClient.ZoneLayer layer, CommonAI.ZoneClient.ZoneObject obj)
        {
            AddBattleObject(BattleFactroy.Instance.CreateComAICell(this, obj));
        }

        private void Layer_ObjectLeave(CommonAI.ZoneClient.ZoneLayer layer, CommonAI.ZoneClient.ZoneObject obj)
        {
            RemoveBattleObject(obj.ObjectID);
        }

        #endregion

        private void Layer_MessageReceived(ZoneLayer layer, CommonLang.Protocol.IMessage msg)
        {
            if (msg is CommonAI.Zone.ZoneEvent)
            {
                Action<CommonAI.Zone.ZoneEvent> action = null;
                if (mZoneEvens.TryGetValue(msg.GetType(), out action))
                {
                    action(msg as CommonAI.Zone.ZoneEvent);
                }
            }
            else if (msg is CommonAI.Zone.ObjectEvent)
            {
                var obj = GetBattleObject((msg as CommonAI.Zone.ObjectEvent).object_id);
                if (obj != null)
                {
                    obj.DoObjectEvent(msg as CommonAI.Zone.ObjectEvent);
                }
            }
        }

        private void Layer_OnChangeBGM(ZoneLayer layer, string filename)
        {
            BattleFactroy.Instance.SoundAdapter.PlayBGM(filename);
        }

        public virtual void PlayEffectWithZoneCoord(LaunchEffect eff
            , float x, float y, float direct)
        {
            Vector3 pos = Extensions.ZonePos2NavPos(mBattle.Layer.Terrain.TotalHeight
                , x, y, 0);
            Quaternion rot = Extensions.ZoneRot2UnityRot(direct);
            if (eff != null)
            {
                if (!string.IsNullOrEmpty(eff.SoundName))
                {
                    if (eff.IsLoop)
                    {
                        BattleFactroy.Instance.SoundAdapter.PlaySound(eff.SoundName, eff.EffectTimeMS, pos);
                    }
                    else
                    {
                        BattleFactroy.Instance.SoundAdapter.PlaySound(eff.SoundName, pos);
                    }
                }
                //特效
                if (!string.IsNullOrEmpty(eff.Name))
                {
                    BattleFactroy.Instance.GameObjectAdapter.Load(eff.Name
                        , System.IO.Path.GetFileNameWithoutExtension(eff.Name)
                        , (succ, aoe) =>
                        {
                            if (succ)
                            {
                                if (IsDisposed && eff.BindBody)
                                {
                                    BattleFactroy.Instance.GameObjectAdapter.Unload(aoe);
                                    return;
                                }
                                OnLoadEffectSuccess(aoe, eff, pos, rot);
                            }
                        });
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="eff"></param>
        /// <param name="pos"> unity pos</param>
        /// <param name="rot"> unity rot</param>
        public virtual void PlayEffect(LaunchEffect eff, Vector3 pos, Quaternion rot)
        {
            if (eff != null)
            {
                //特效
                if (!string.IsNullOrEmpty(eff.Name))
                {
                    BattleFactroy.Instance.GameObjectAdapter.Load(eff.Name
                        , System.IO.Path.GetFileNameWithoutExtension(eff.Name)
                        , (succ, aoe) =>
                        {
                            if (succ)
                            {
                                if (IsDisposed && eff.BindBody)
                                {
                                    BattleFactroy.Instance.GameObjectAdapter.Unload(aoe);
                                    return;
                                }

                                OnLoadEffectSuccess(aoe, eff, pos, rot);
                            }
                        });
                }
            }
        }

        protected virtual void OnLoadEffectSuccess(AssetObjectExt aoe
            , LaunchEffect eff, Vector3 pos, Quaternion rot)
        {
            aoe.gameObject.Parent(mEffectRoot);
            aoe.gameObject.Position(pos);
            aoe.gameObject.Rotation(rot);

            var script = aoe.gameObject.AddComponent<EffectAutoDestroy>();
            script.aoeHandler = aoe;
            script.duration = eff.IsLoop ? eff.EffectTimeMS / 1000f : 0f;
        }
    }
}