using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace CommonUnity3D.XMUnity.LoadUtil
{
    /// <summary>
    /// XMUnityAssetBundle加载管理器.
    /// </summary>
    public class XMUnityAssetBundleManager : MonoBehaviour
    {
        private static XMUnityAssetBundleManager mInstance = null;
        private Dictionary<string, XMUnityAssetBundle> mABMap = null;
        private Dictionary<string, XMUnityABLoadAdapter> mLoadTaskMap = null;
        public AssetBundleManifest Manifest { get; private set; }
        public List<Shader> ShaderList { get; private set; }
        private Dictionary<string, List<string>> mDepsList = new Dictionary<string, List<string>>();
        public int PrefabCapacity = 50;

        private bool mNeedCleanUp = false;

        private List<XMUnityABLoadAdapter> mTempLoadTask = null;
        private AsyncOperation mUnloadOp = null;
        private bool mNeedUnload = false;
        void Awake()
        {
            mInstance = this;
            Init();
        }

        void Start()
        {

        }

        void Update()
        {
            //检查加载任务
            mTempLoadTask = new List<XMUnityABLoadAdapter>(mLoadTaskMap.Values);
            var iter = mTempLoadTask.GetEnumerator();
            while (iter.MoveNext())
            {
                if(iter.Current.OnAdapterUpdate())
                {
                    iter.Current.Dispose();
                    mLoadTaskMap.Remove(iter.Current.GetURL());

                }
            }
            mTempLoadTask.Clear();

            //是否存在加载任务
            bool isLoading = false;
            if (mLoadTaskMap.Count == 0)
            {
                var item = mABMap.GetEnumerator();
                while (item.MoveNext())
                {
                    if (item.Current.Value.BundleStatus == XMUnityAssetBundle.Status.LoadAsset || item.Current.Value.BundleStatus == XMUnityAssetBundle.Status.LoadDep)
                    {
                        isLoading = true;
                        break;
                    }
                }
            }
            else
            {
                isLoading = true;
            }
                //在合适的时候执行Cleanup
            if (!isLoading && mNeedCleanUp)
            {
                CleanAssetBundleMap(false);
                //Resources.UnloadUnusedAssets ();
                System.GC.Collect();
                mNeedCleanUp = false;
            }

            //检查unload进度
            if(mUnloadOp != null && mUnloadOp.isDone)
            {
                mUnloadOp = null; 
            }
            //在合适的时候执行unload
            if (!isLoading  && mNeedUnload)
            {
                mUnloadOp = Resources.UnloadUnusedAssets();
                mNeedUnload = false;
            }
        }

        private void Init()
        {
            mABMap = new Dictionary<string, XMUnityAssetBundle>();
            mLoadTaskMap = new Dictionary<string, XMUnityABLoadAdapter>();
            mTempLoadTask = new List<XMUnityABLoadAdapter>();
            ShaderList = new List<Shader>();
            StartLoadManifest();
            StartLoadShaderList();
        }
        void StartLoadManifest()
        {
            XMUnityABLoadAdapter load = new XMUnityABLoadAdapter();
            load.LoadAsync = false;
            load.SetFinishCallBack(OnManifestFinish);
            load.Load("/res/" + GetPlatformForAssetBundles(), null);
            StartCoroutine(CheckLoadManifest(load));
        }
        IEnumerator CheckLoadManifest(XMUnityABLoadAdapter load)
        {
            while (!load.HasDone)
            {
                load.OnAdapterUpdate();
                yield return null;
            }
        }
        private void OnManifestFinish(XMUnityABLoadAdapter adapter)
        {
            if (adapter.GetAssetBundle() != null)
            {

                Manifest = adapter.GetAssetBundle().LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }
            else
            {
                Debug.LogError("XMUnityAssetBundleManager can not find Manifest");
            }

        }
        void StartLoadShaderList()
        {
            XMUnityABLoadAdapter load = new XMUnityABLoadAdapter();
            load.LoadAsync = false;
            load.SetFinishCallBack(OnShaderListFinish);
            load.Load("/res/shaderslist.assetbundles", null);
            StartCoroutine(CheckLoadShaderList(load));
        }
        IEnumerator CheckLoadShaderList(XMUnityABLoadAdapter load)
        {
            while (!load.HasDone)
            {
                load.OnAdapterUpdate();
                yield return null;
            }
        }
        private void OnShaderListFinish(XMUnityABLoadAdapter adapter)
        {
            if (adapter.GetAssetBundle() != null)
            {

                Object[] shaderobject = adapter.GetAssetBundle().LoadAllAssets();
                foreach (Object sb in shaderobject)
                {
                    if (sb is Shader)
                    {
                        ShaderList.Add((Shader)sb);
                    }
                    else
                    {
                        Debug.Log("ShaderList add dif type=" + sb.GetType());
                    }

                }
                Shader.WarmupAllShaders();
            }
            else
            {
                Debug.LogWarning("XMUnityAssetBundleManager can not find ShaderList");
            }

        }
        public void Dispose()
        {
            CleanAssetBundleMap(true);
            mInstance = null;
        }
        public static XMUnityAssetBundleManager GetInstance()
        {
            if (mInstance == null)
            {
                Debug.LogError("XMUnityAssetBundleManager must create before use");
            }

            return mInstance;
        }
        public void UnloadUnusedAssets()
        {
            if(mUnloadOp == null)
            {
                mNeedUnload = true;
            }
        }
        public List<string> GetDepList(string name)
        {
            name = name.ToLower();
            string dep_key = name.ToLower().Replace("/res/", "");
            if (!mDepsList.ContainsKey(name))
            {
                string[] deps = Manifest.GetAllDependencies(dep_key);
                List<string> ds = new List<string>(deps.Length);
                for (int i = 0; i < deps.Length; i++)
                {
                    if(deps[i] != "shaderslist.assetbundles")
                    {
                        string key = "/res/" + deps[i];
                        //if (name != key)
                        //{
                        ds.Add(key);
                        //}
                        //else
                        //{
                        //    Debug.LogError("what the fuck");
                        //}
                    }
                }
                mDepsList[name] = (ds);
            }
            return mDepsList[name];
        }
        public void GetAssetBundle(string name, XMUnityABLoadAdapter.XMUnityLoadAdapterCallBack callBack, bool async = true)
        {
            name = name.ToLower();
            XMUnityAssetBundle ab = null;
            XMUnityABLoadAdapter adapter = null;
            if (mABMap.TryGetValue(name, out ab))   //从AB中寻找AssetBundle.
            {
                if (callBack != null)
                {
                    callBack.Invoke(ab);
                    //return null;
                }
            }
            else if (mLoadTaskMap.TryGetValue(name, out adapter)) //从正在加载的map中寻找.
            {
                if (callBack != null)
                {
                    adapter.AddCallBack(callBack);
                }
                //return null;
            }
            else //创建加载器.
            {
                XMUnityABLoadAdapter load = new XMUnityABLoadAdapter();
				load.LoadAsync = async;
                mLoadTaskMap.Add(name, load);
                load.SetFinishCallBack(OnAdapterFinish);
                load.Load(name, callBack);
                //return load;
            }

            //return null;
        }
        public AssetBundle GetAssetBundle(string name)
        {
            name = name.ToLower();
            XMUnityAssetBundle ret = null;
            mABMap.TryGetValue(name, out ret);
            return ret.AssetBundle;
        }
        public XMUnityAssetBundle GetXMUnityAssetBundle(string name)
        {
            name = name.ToLower();
            XMUnityAssetBundle ret = null;
            mABMap.TryGetValue(name, out ret);
            if (ret == null)
            {
                return null;
            }
            return ret;
        }
        public bool AddAssetBundle(string name, XMUnityAssetBundle ab)
        {
            if (!string.IsNullOrEmpty(name) && ab != null)
            {
                mABMap.Add(name, ab);
                return true;
            }

            Debug.Log("XMUnityAssetBundleManager AddAssetBundle Error: Invaild Data");

            return false;
        }
        /// <summary>
        /// 将remove操作push到一个队列中执行,只有当当前没有加载任务时才会触发.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="isUnloadAll"></param>
        public void UnloadAssetBundle(string name, bool isUnloadAll = false, bool force = false)
        {
            XMUnityAssetBundle ab = null;
            if (mABMap.TryGetValue(name, out ab))
            {
                if (ab != null)
                {
                    if(ab.BundleType == XMUnityAssetBundle.Type.DEP_ASSET)
                    {
                        Debug.LogError("Cant unload a dep assetbundle: " + name);
                    }
                    List<string> deps = GetDepList(name);
                    foreach (var item in deps)
                    {
                        XMUnityAssetBundle mfab = null;
                        if (mABMap.TryGetValue(item, out mfab))
                        {
                            if (mfab != null)
                            {
                                if (mfab.TryUnloadDep(name, isUnloadAll, force))
                                {
                                    mABMap.Remove(item);
                                }
                            }
                        }
                    }
                    UnloadAssetBundleImmediate(name, isUnloadAll, force);
                }
            }
            else
            {
                //通常只有切换场景时没有清除资源的情况下强制卸载了所有ab才会出,可以用来监控逻辑加载卸载成对的情况
                Debug.LogWarning("[策划,测试请无视]UnloadAssetBundle Error, bundle not exists: " + name);
            }
        }
        public void UnloadAssetBundleImmediate(string name, bool isUnloadAll, bool force = false)
        {
            // 内部自动处理
            XMUnityAssetBundle ab = null;
            if (mABMap.TryGetValue(name, out ab))
            {
                if (ab != null)
                {
                    //TODO 销毁XMUnityAssetBundle,主要是缓存和prefab
                    if (ab.Unload(isUnloadAll, force))
                    {
                        mABMap.Remove(name);
                    }
                }
            }
            else
            {
                Debug.LogWarning("[策划,测试请无视]UnloadAssetBundleImmediate Error, bundle not exists: " + name);
            }
        }
        public void LoadDep(string name, bool loadABASync, bool loadAssetASync, XMUnityAssetBundle.LoadResCallBack callback, object userdata, string childAB)
        {
            XMUnityAssetBundleManager.GetInstance().GetAssetBundle(name, (XMUnityAssetBundle mfab) =>
            {
                if (mfab != null)
                {
                    mfab.MarkAsDeps(childAB);
                }
                //else
                {
                    callback(null, null, userdata, false);
                }
            }, loadABASync);
        }
        public void LoadAsset(string name, string asset, bool loadABASync, bool loadAssetASync, XMUnityAssetBundle.LoadResCallBack callback, object userdata, System.Type type = null)
        {
            GetAssetBundle(name, (XMUnityAssetBundle mfab) =>
            {
                if (mfab != null)
                {
                    mfab.GetAsset(asset, loadAssetASync, callback, userdata, type);
                }
                else
                {
                    callback(asset, null, userdata, false);
                }
            }, loadABASync);
        }
        public void LoadAsset(string name, bool loadABASync, bool loadAssetASync, XMUnityAssetBundle.LoadResCallBack callback, object userdata, System.Type type = null)
        {
            int starIndex = name.LastIndexOf('/') + 1;
            int length = name.Length - starIndex - ".assetbundles".Length;
            string objectName = name.Substring(starIndex, length);
            LoadAsset(name.ToLower(), objectName, loadABASync, loadAssetASync, callback, userdata, type);
        }
        public void CleanAssetBundleMap(bool isUnloadAll)
        {
            var temp = new Dictionary<string, XMUnityAssetBundle>();
            foreach (KeyValuePair<string, XMUnityAssetBundle> kvp in mABMap)
            {
                if (kvp.Value != null)
                {
                    if(!kvp.Value.Unload(isUnloadAll, true))
                    {
                        temp.Add(kvp.Key, kvp.Value);
                    }
                }
            }
            mABMap.Clear();
            mABMap = temp;
            AssetObject.ClearRefs();
        }
        public void CleanAssetBundleMapInQueue()
        {
            mNeedCleanUp = true;
        }
        private void OnAdapterFinish(XMUnityABLoadAdapter adapter)
        {
            XMUnityAssetBundle mfab = adapter.GetXMUnityAssetBundle();
            if (mfab != null)
            {
                mABMap.Add(adapter.GetURL(), mfab);
            }

            if (mLoadTaskMap.ContainsKey(adapter.GetURL()))
            {
                mLoadTaskMap.Remove(adapter.GetURL());
            }
            else
            {
                Debug.Log("XMUnityAssetBundleManager can not find Adapter");
            }

            adapter.DispatchCallBack();
        }
        public bool ContainsBundle(string name) { return mABMap.ContainsKey(name); }

        public static string GetPlatformForAssetBundles()
        {
            RuntimePlatform platform = Application.platform;
            switch (platform)
            {
                case RuntimePlatform.Android:
                    return "android";
                case RuntimePlatform.IPhonePlayer:
                    return "ios";
                case RuntimePlatform.WebGLPlayer:
                    return "webgl";
                case RuntimePlatform.OSXWebPlayer:
                case RuntimePlatform.WindowsWebPlayer:
                    return "standalonewindows";
                case RuntimePlatform.WindowsEditor:
#if UNITY_ANDROID
                return "android";
#else
                    return "standalonewindows";
#endif
                case RuntimePlatform.WindowsPlayer:
                    return "standalonewindows";
                case RuntimePlatform.OSXPlayer:
                    return "osx";
                default:
                    return null;
            }
        }
    }
}