using CommonAI.ZoneClient;
using CommonAIClient.Client;
using CommonAIClient.Unity.Battle;
using CommonAIClient.Unity.Utils;
using CommonLang;
using CommonUnity3D.MFUnity.LoadUtil;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;

namespace CommonAIClient.Unity
{
    public class DefaultBattleScene : BattleScene
    {
        public DefaultBattleScene(AbstractBattle client) : base(client)
        {
        }
    }

    public class DefaultBattleFactroy : BattleFactroy
    {
        public enum LayerSetting
        {
            UI = 5,
            IconGenerator = 9,
            ColliderObject = 10, //用于隐藏遮挡玩家的场景物体
            STAGE_NAV = 13,
            ColliderValkyrie = 16, //女武神整体
            ColliderValkyriePart = 17, //女武神身体部位
        }

        private DefaultGameObjectAdapter mGameObjectAdapter;
        private DefaultTerrainAdapter mTerrainAdapater;
        private DefaultSoundAdapter mSoundAdapter;


        public DefaultBattleFactroy() : base()
        {
            mGameObjectAdapter = new DefaultGameObjectAdapter(20);
            mTerrainAdapater = new DefaultTerrainAdapter();
            mSoundAdapter = new DefaultSoundAdapter(8);
        }

        public override BattleScene CreateBattleScene(AbstractBattle battle)
        {
            return new DefaultBattleScene(battle);
        }

        public override GameObjectAdapter GameObjectAdapter
        {
            get
            {
                return mGameObjectAdapter;
            }
        }

        public override SoundAdapter SoundAdapter
        {
            get
            {
                return mSoundAdapter;
            }
        }

        public override TerrainAdapter TerrainAdapter
        {
            get
            {
                return mTerrainAdapater;
            }
        }

        public override int StageNavLay
        {
            get
            {
                return (int)LayerSetting.STAGE_NAV;
            }
        }

        public override DisplayCell CreateDisplayCell(GameObject root, string name)
        {
            return new DisplayCell(root, name);
        }

        public override ComAICell CreateComAICell(BattleScene battleScene, CommonAI.ZoneClient.ZoneObject obj)
        {
            if (obj is ZoneActor)
            {
                return new ComAIActor(battleScene, obj as ZoneActor);
            }
            else if (obj is ZoneUnit)
            {
                return new ComAIUnit(battleScene, obj as ZoneUnit);
            }
            else if (obj is ZoneItem)
            {
                return new ComAIItem(battleScene, obj as ZoneItem);
            }
            else if (obj is ZoneSpell)
            {
                return new ComAISpell(battleScene, obj as ZoneSpell);
            }
            return null;
        }
        
        public override void OnError(string msg)
        {

        }

        public override void MakeDamplingJoint(GameObject body, GameObject from, GameObject to)
        {
            //DampingJoint dj = body.transform.GetComponentInChildren<DampingJoint>();
            //if (dj != null)
            //{
            //    dj.Hook(from.transform, to.transform);
            //}
        }
    }
    public class DefaultGameObjectAdapter : GameObjectAdapter
    {
        private LRUCache<string, AssetObjectExt> gCache;
        private GameObject mCacheNode;


        public DefaultGameObjectAdapter(int cacheCount) : base()
        {
            gCache = new LRUCache<string, AssetObjectExt>(cacheCount, OnCacheRemoveObject);
        }

        private GameObject CacheNode
        {
            get
            {
                if (mCacheNode == null)
                {
                    mCacheNode = new GameObject("GameObjectLoaderCache");
                    mCacheNode.SetActive(false);
                }
                return mCacheNode;
            }
        }

        public override void Load(string assetBundleName, string assetName
            , Action<bool, AssetObjectExt> callback
            , bool bASyncLoadAB = true, bool bASyncLoadAsset = true)
        {
            if (callback == null)
            {
                Debug.LogError("callback == null");
                return;
            }
            if (string.IsNullOrEmpty(assetBundleName) || string.IsNullOrEmpty(assetName))
            {
                Debug.LogError("string.IsNullOrEmpty(assetBundleName) || string.IsNullOrEmpty(assetName)");
                callback(false, null);
                return;
            }

            if (gCache.ContainsKey(assetName.ToLower()))
            {
                AssetObjectExt tmp = gCache.Get(assetName.ToLower());
                tmp.transform.parent = null;
                tmp.transform.position = Vector3.zero;
                tmp.transform.rotation = Quaternion.identity;
                tmp.transform.localScale = Vector3.one;
                tmp.gameObject.SetActive(true);
                callback(true, tmp);
            }
            else
            {
                AssetLoader loader = new AssetLoader(assetBundleName.ToLower(), assetName.ToLower()
                    , (succ_, res_, mfab_) =>
                    {
                        if (callback == null)
                            return;

                        if (!succ_)
                        {
                            callback(false, null);
                            return;
                        }

                        UnityEngine.Object tmp = GameObject.Instantiate(res_);
                        if (tmp is GameObject)
                        {
                            AssetObjectExt aoe = (tmp as GameObject).AddComponent<AssetObjectExt>();
                            aoe.name = assetName.ToLower();
                            aoe.AddRef(assetBundleName.ToLower(), mfab_.Version);
                            callback(true, aoe);
                        }
                        else
                        {
                            UnityEngine.Object.DestroyImmediate(tmp, true);
                            callback(false, null);
                        }
                    }, bASyncLoadAB, bASyncLoadAsset);
            }
        }

        public override void Unload(AssetObjectExt aoe)
        {
            if (aoe != null)
            {
                var script = aoe.gameObject.GetComponent<EffectAutoDestroy>();
                aoe.gameObject.SetActive(false);
                aoe.transform.parent = CacheNode.transform;
                gCache.Add(aoe.name, aoe);
            }
        }

        public void ClearCache()
        {
            gCache.Clear();
        }

        private void OnCacheRemoveObject(AssetObjectExt aoe)
        {
            if (aoe == null || aoe.gameObject.Equals(null))
            {
                Debug.LogWarning("remove error: null");
            }
            else
            {
                aoe.Destroy();
            }
        }
    }
    public class DefaultAudio : Audio
    {
        public string Name { get; set; }
        public AudioSource AudioSource { get; private set; }

        private static GameObject mSoundRoot;
        private static GameObject SoundRoot
        {
            get
            {
                if (mSoundRoot == null)
                {
                    mSoundRoot = new GameObject("SoundRoot");
                    mSoundRoot.SetActive(false);
                }
                return mSoundRoot;
            }
        }

        public DefaultAudio() : base()
        {
            GameObject sound = new GameObject("Sound3D");
            this.AudioSource = sound.AddComponent<AudioSource>();
        }

        public void Play()
        {
            this.AudioSource.gameObject.name = Name;
            this.AudioSource.gameObject.Parent(null);
            this.AudioSource.gameObject.SetActive(true);
            if (this.AudioSource.clip != null)
            {
                this.AudioSource.Play();
            }
        }

        public void Stop()
        {
            this.AudioSource.Stop();
            this.AudioSource.clip = null;
            this.AudioSource.gameObject.Parent(SoundRoot);
            this.AudioSource.gameObject.SetActive(false);
        }
    }

    public class SoundLoader
    {
        private bool mDisposed;
        private string mAssetBundleName;
        private AudioClip mAudioClip;
        private System.Action<bool, AudioClip> mCallbacks;
        private bool mLoadFinished;

        public string AssetBundleName { get { return mAssetBundleName; } }


        public void Load(System.Action<bool, AudioClip> callback)
        {
            if (mLoadFinished)
            {
                callback(mAudioClip != null, mAudioClip);
                return;
            }

            mCallbacks += callback;
        }

        public void Dispose()
        {
            if (!mDisposed)
            {
                mDisposed = true;
                if (mAudioClip != null)
                {
                    UnityEngine.Object.DestroyImmediate(mAudioClip, true);
                    mAudioClip = null;
                }

                if (mCallbacks != null)
                {
                    mCallbacks(false, null);
                    mCallbacks = null;
                }
            }
        }

        public SoundLoader(string assetBundleName)
        {
            mAssetBundleName = assetBundleName.ToLower();

            AssetLoader loader = new AssetLoader(mAssetBundleName
                , System.IO.Path.GetFileNameWithoutExtension(mAssetBundleName)
                , (succ_, res_, mfab_) =>
                {
                    OnLoadAssetFinish(succ_, res_, mfab_);
                }, true, true);
        }

        private void OnLoadAssetFinish(bool succ, UnityEngine.Object res, MFUnityAssetBundle mfab)
        {
            if (succ)
            {
                mAudioClip = res as AudioClip;
                MFUnityAssetBundleManager.GetInstance().UnloadAssetBundleImmediate(mAssetBundleName, false);
            }

            if (mCallbacks != null)
            {
                mCallbacks(mAudioClip != null, mAudioClip);
                mCallbacks = null;
            }
            mLoadFinished = true;
        }
    }

    public class DefaultSoundAdapter : SoundAdapter
    {
        private ObjectPool<DefaultAudio> mAudioPoool = new ObjectPool<DefaultAudio>();
        private List<DefaultAudio> mLoopAudios = new List<DefaultAudio>();
        private Queue<DefaultAudio> mNormalAudios = new Queue<DefaultAudio>();
        private int mMaxNormalAudioNum;
        private HashMap<string, SoundLoader> mAudioLoaders = new HashMap<string, SoundLoader>();
        private DefaultAudio mBGM;
        private SoundLoader mBGMLoader;


        public override void Clear()
        {
            foreach (var elem in mAudioLoaders)
            {
                elem.Value.Dispose();
            }
            mAudioLoaders.Clear();
        }

        private void RemoveLoader(string name)
        {
            SoundLoader loader = mAudioLoaders.RemoveByKey(name.ToLower());
            if (loader != null)
            {
                loader.Dispose();
            }
        }

        private DefaultAudio GetNormalAudio()
        {
            DefaultAudio audio = null;
            if (mNormalAudios.Count >= mMaxNormalAudioNum)
            {
                audio = mNormalAudios.Dequeue();
                audio.Stop();
                mAudioPoool.Release(audio);
            }

            audio = mAudioPoool.Get();
            mNormalAudios.Enqueue(audio);
            return audio;
        }

        public DefaultSoundAdapter(int maxNormalAudioNum) : base()
        {
            mMaxNormalAudioNum = maxNormalAudioNum;
        }

        private void LoadBGM(string name, System.Action<bool, AudioClip> callback)
        {
            if (mBGMLoader != null)
            {
                if (mBGMLoader.AssetBundleName == name.ToLower())
                {
                    mBGMLoader.Load(callback);
                    return;
                }
                mBGMLoader.Dispose();
            }

            mBGMLoader = new SoundLoader(name);
            mBGMLoader.Load(callback);
        }

        private void LoadAudio(string name, System.Action<bool, AudioClip> callback)
        {
            SoundLoader loader = null;
            if (mAudioLoaders.TryGetValue(name.ToLower(), out loader))
            {
                if (callback != null)
                {
                    loader.Load(callback);
                }
                return;
            }

            loader = new SoundLoader(name);
            loader.Load(callback);
            mAudioLoaders.Add(name.ToLower(), loader);
        }

        public override void PlayBGM(string name)
        {
            if (!string.IsNullOrEmpty(name))
            {
                LoadBGM(name, (succ_, res_) =>
                {
                    if (succ_)
                    {
                        if (mBGM == null)
                        {
                            mBGM = mAudioPoool.Get();
                            GameObject.DontDestroyOnLoad(mBGM.AudioSource.gameObject);
                        }

                        mBGM.Stop();
                        RemoveLoader(mBGM.Name);

                        mBGM.Name = name;
                        mBGM.AudioSource.clip = res_;
                        mBGM.AudioSource.loop = true;
                        mBGM.AudioSource.volume = 1f;
                        mBGM.AudioSource.spatialBlend = 0f;

                        mBGM.Play();
                    }
                });
            }
        }

        public override void PlaySound(string name, Vector3 pos, float distance = 20f)
        {
            LoadAudio(name, (succ_, res_) =>
            {
                if (succ_)
                {
                    DefaultAudio audio = GetNormalAudio();
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(pos);
                    audio.AudioSource.clip = res_;
                    audio.AudioSource.loop = false;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script == null)
                    {
                        script = audio.AudioSource.gameObject.AddComponent<AudioAutoStop>();
                    }
                    script.Auido = audio;
                    script.Duration = audio.AudioSource.clip.length;
                    script.TraceTarget = null;

                    audio.Play();
                }
            });
        }

        public override void PlaySound(string name, int timeMS, Vector3 pos, float distance = 20f)
        {
            LoadAudio(name, (succ_, res_) =>
            {
                if (succ_)
                {
                    DefaultAudio audio = GetNormalAudio();
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(pos);
                    audio.AudioSource.clip = res_ as AudioClip;
                    audio.AudioSource.loop = true;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script == null)
                    {
                        script = audio.AudioSource.gameObject.AddComponent<AudioAutoStop>();
                    }
                    script.Auido = audio;
                    script.Duration = timeMS / 1000f;
                    script.TraceTarget = null;

                    audio.Play();
                }
            });
        }

        public override Audio PlaySoundLoop(string name, Vector3 pos, float distance = 20f)
        {
            DefaultAudio audio = mAudioPoool.Get();
            LoadAudio(name, (succ_, res_) =>
            {
                int index = mLoopAudios.IndexOf(audio);
                if (index == -1)
                {
                    return;
                }

                if (succ_)
                {
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(pos);
                    audio.AudioSource.clip = res_ as AudioClip;
                    audio.AudioSource.loop = true;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script != null)
                    {
                        GameObject.Destroy(script);
                    }

                    audio.Play();
                }
            });
            return audio;
        }

        public override void PlaySound(string name, GameObject obj, float distance = 20f)
        {
            LoadAudio(name, (succ_, res_) =>
            {
                if (succ_)
                {
                    DefaultAudio audio = GetNormalAudio();
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(obj.Position());
                    audio.AudioSource.clip = res_ as AudioClip;
                    audio.AudioSource.loop = false;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script == null)
                    {
                        script = audio.AudioSource.gameObject.AddComponent<AudioAutoStop>();
                    }
                    script.Auido = audio;
                    script.Duration = audio.AudioSource.clip.length;
                    script.TraceTarget = obj;

                    audio.Play();
                }
            });
        }

        public override void PlaySound(string name, int timeMS, GameObject obj, float distance = 20f)
        {
            LoadAudio(name, (succ_, res_) =>
            {
                if (succ_)
                {
                    DefaultAudio audio = GetNormalAudio();
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(obj.Position());
                    audio.AudioSource.clip = res_ as AudioClip;
                    audio.AudioSource.loop = true;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script == null)
                    {
                        script = audio.AudioSource.gameObject.AddComponent<AudioAutoStop>();
                    }
                    script.Auido = audio;
                    script.Duration = timeMS / 1000f;
                    script.TraceTarget = obj;

                    audio.Play();
                }
            });
        }

        public override Audio PlaySoundLoop(string name, GameObject obj, float distance = 20f)
        {
            DefaultAudio audio = GetNormalAudio();
            LoadAudio(name, (succ_, res_) =>
            {
                int index = mLoopAudios.IndexOf(audio);
                if (index == -1)
                {
                    return;
                }

                if (succ_)
                {
                    audio.Name = name;
                    audio.AudioSource.gameObject.Position(obj.Position());
                    audio.AudioSource.clip = res_ as AudioClip;
                    audio.AudioSource.loop = true;
                    audio.AudioSource.volume = 1f;
                    audio.AudioSource.spatialBlend = 0.9f;
                    audio.AudioSource.maxDistance = distance;

                    AudioAutoStop script = audio.AudioSource.gameObject.GetComponent<AudioAutoStop>();
                    if (script != null)
                    {
                        GameObject.Destroy(script);
                    }

                    audio.Play();
                }
            });
            return audio;
        }

        public override void StopSoundLoop(Audio sound)
        {
            DefaultAudio audio = sound as DefaultAudio;
            if (audio != null)
            {
                int index = mLoopAudios.IndexOf(sound as DefaultAudio);
                if (index != -1)
                {
                    mLoopAudios.Remove(audio);
                }
                audio.Stop();
                mAudioPoool.Release(audio);
            }
        }
    }
    public class DefaultTerrainAdapter : TerrainAdapter
    {
        private string mAssetBundleName;
        private string mAsssetName;
        private AssetLoader mAssetLoader;
        private System.Action<bool, GameObject> mCallback;
        private GameObject mMapRoot;


        public override void Load(string assetBundleName, System.Action<bool, GameObject> callback)
        {
            if (!string.IsNullOrEmpty(assetBundleName) && callback != null)
            {
                mMapRoot = new GameObject("MapNode");
                mAssetBundleName = assetBundleName;
                mAsssetName = Path.GetFileNameWithoutExtension(mAssetBundleName);
                mCallback = callback;
                mAssetLoader = new AssetLoader(mAssetBundleName, mAsssetName, OnLoadFinish, false, false);
            }
        }

        protected virtual void OnLoadFinish(bool succ, UnityEngine.Object res, MFUnityAssetBundle ab)
        {
            if (ab == null)
            {
                mCallback(false, null);
                return;
            }

            GameObject map = GameObject.Instantiate(res) as GameObject;
            map.transform.parent = mMapRoot.transform;

            LoadLightmap(ab);
            LoadNav(ab);

            Transform statics = mMapRoot.transform.Find("static");
            if (statics != null)
            {
                StaticBatchingUtility.Combine(statics.gameObject);
            }
            // fog settings
            InitFogParam(mMapRoot);

            // camera effect?
            GameObject objA = GameObject.Find("filter_a");
            GameObject objB = GameObject.Find("filter_b");
            if (objA != null && objB != null)
            {
                objA.transform.parent = Camera.main.gameObject.transform;
                objB.transform.parent = Camera.main.gameObject.transform;
                //position
                objA.transform.localPosition = new Vector3(0, 0, 0.24f);
                objB.transform.localPosition = new Vector3(0, 0, 0.24f);
                //rotation
                objA.transform.localRotation = Quaternion.Euler(270, 0, 0);
                objB.transform.localRotation = Quaternion.Euler(270, 0, 0);
                //scale
                float scaleW = 0.04f;
                float scaleH = 0.0225f;
                objA.transform.localScale = new Vector3(scaleW, 1, scaleH);
                objB.transform.localScale = new Vector3(scaleW, 1, scaleH);
            }

            mAssetLoader.ForeachDepFile((file) =>
            {
                if (file.IndexOf("shaderslist") == -1)
                {
                    MFUnityAssetBundleManager.GetInstance().UnloadAssetBundleImmediate(file, false, true);
                }
            });
            MFUnityAssetBundleManager.GetInstance().UnloadAssetBundleImmediate(mAssetBundleName, false, true);

            mCallback(true, mMapRoot);
        }

        protected virtual bool InitFogParam(GameObject mMapRoot)
        {
            //             FogAmbientColorSetting fs = mMapRoot.GetComponent<FogAmbientColorSetting>();
            //             if (null != fs && fs.fog)
            //             {
            //                 RenderSettings.fog = true;
            //                 RenderSettings.fogColor = fs.fogColor;
            //                 RenderSettings.fogDensity = fs.fogDensity;
            //                 RenderSettings.fogMode = fs.fogMode;
            //             }
            //             else
            //             {
            //                 RenderSettings.fog = false;
            //             }
            return false;
        }

        private void LoadLightmap(MFUnityAssetBundle ab)
        {
            List<UnityEngine.Object> lms = new List<UnityEngine.Object>();
            int count = 0;
            UnityEngine.Object lightMapObj;
            do
            {
                string reslight = "Lightmap-" + count + "_comp_light";
                lightMapObj = ab.AssetBundle.LoadAsset(reslight);
                if (lightMapObj != null)
                {
                    lms.Add(lightMapObj);
                    ++count;
                }
                else
                {
                    break;
                }
            }
            while (true);

            if (lms.Count > 0)
            {
                LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
                UnityEngine.LightmapData[] lmDataSet = new UnityEngine.LightmapData[lms.Count];

                for (int i = 0; i < lms.Count; i++)
                {
                    Texture2D tex = lms[i] as Texture2D;
                    if (tex != null)
                    {
                        UnityEngine.LightmapData lmData = new UnityEngine.LightmapData();
                        lmData.lightmapFar = (Texture2D)tex;
                        lmDataSet[i] = lmData;
                    }
                }
                LightmapSettings.lightmaps = lmDataSet;
            }
            InitLightMapParam(mMapRoot);
        }

        protected virtual bool InitLightMapParam(GameObject mMapRoot)
        {
            //             LightmapParam[] lmd = mMapRoot.GetComponentsInChildren<LightmapParam>(true);
            //             for (int i = 0; i < lmd.Length; i++)
            //             {
            //                 Renderer r = lmd[i].gameObject.GetComponent<Renderer>();
            //                 r.gameObject.isStatic = true;
            //                 r.lightmapIndex = lmd[i].lightmapIndex;
            //                 r.lightmapScaleOffset = lmd[i].lightmapScaleOffset;
            //             }
            return false;
        }

        private void LoadNav(MFUnityAssetBundle ab)
        {
            string resnav = mAsssetName + "_nav";
            UnityEngine.Object navObj = ab.AssetBundle.LoadAsset(resnav);
            if (navObj != null)
            {
                GameObject nav = (GameObject)GameObject.Instantiate(navObj);
                nav.layer = BattleFactroy.Instance.StageNavLay;
                nav.transform.parent = mMapRoot.transform;

                foreach (Transform e in nav.GetComponentInChildren<Transform>())
                {
                    MeshCollider bc = e.gameObject.GetComponent<MeshCollider>();
                    if (bc == null)
                    {
                        bc = e.gameObject.AddComponent<MeshCollider>();
                    }
                    bc.gameObject.layer = nav.layer;
                }
            }
        }

    }


}