using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Giant.Common;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Giant.Resource {

    public class LoadedAssetBundle
    {
        public AssetBundle Bundle;
        public int HashCode;
        public int ReferencedCount;

        public LoadedAssetBundle(AssetBundle _assetBundle, int _hashCode)
        {
            Bundle = _assetBundle;
            HashCode = _hashCode;
            ReferencedCount = 1;
        }
    }

    ///  for loading asset bundle's dependency
    /*public class LoadedDependencyAssetBundle
    {
        public AssetBundle Bundle;
        public int Key;

        public LoadedDependencyAssetBundle(AssetBundle assetBundle)
        {
            Bundle = assetBundle;
            Key = 0;
        }
    }*/

    public class AssetBundleManager
    {
#if UNITY_EDITOR
        static int m_bUseAssetBundle = -1;
        const string SimulateAssetBundles = "SimulateAssetBundles";
        static public bool UseAssetBundle
        {
            get
            {
#if UNITY_EDITOR
                if (m_bUseAssetBundle == -1)
                    m_bUseAssetBundle = EditorPrefs.GetBool(SimulateAssetBundles, false) ? 1 : 0;
#endif
                return m_bUseAssetBundle != 0;
            }
            set
            {
                int newValue = value ? 1 : 0;
                if (newValue != m_bUseAssetBundle)
                {
                    m_bUseAssetBundle = newValue;
#if UNITY_EDITOR
                    EditorPrefs.SetBool(SimulateAssetBundles, value);
#endif
                }

            }
        }
#endif

        static AssetBundleManager m_sInstance = null;


        public static AssetBundleManager Instance
        {
            get
            {
                if (m_sInstance == null)
                {
                    m_sInstance = new AssetBundleManager();
                }
                return m_sInstance;
            }
        }

        /// internal cache for all loaded assetbundles, <path name hashID, all assetbundles under this path>.
        Dictionary<Int32, LoadedAssetBundle> m_loadedAB = new Dictionary<Int32, LoadedAssetBundle>(100);

        /// internal cache for all loaded assetbundles dependency, <path name hashID, all assetbundles dependency under this path>.
        Dictionary<Int32, List<LoadedAssetBundle>> m_loadedABDependency = new Dictionary<Int32, List<LoadedAssetBundle>>(100);

        public Dictionary<Int32, LoadedAssetBundle> LoadedAssetBundles
        {
            get
            {
                return m_loadedAB;
            }
        }

        public AssetBundle GetShaderBundle()
        {
            return AssetBundleResource.Instance.ShaderBundle;
        }

        public AssetBundle GetAssetbundle(uint hashCode)
        {
            uint PackCount = ResourceloadConfig.PackageNum;
            int index = (int)(hashCode % PackCount);
            if (index < PackCount && AssetBundleResource.Instance.AllBundles.Count > 0)
            {
                if (AssetBundleResource.Instance.AllBundles.ContainsKey(index)) {
                    return AssetBundleResource.Instance.AllBundles[index];
                }
                
            }
            return null;
        }

        /// Add the asset bundle resource.
        /// <param name="hashCode"> The  name hashcode.</param>
        /// <param name="bundle">   Reference to bundle resource.</param>
        /// <param name="ownerKey"> The owner's name hashcode, if have. </param>
        public void AddAssetBundle(int hashCode, AssetBundle bundle, int ownerKey)
        {
            LoadedAssetBundle loadedAssetBundle;
            if (AssetBundleManager.Instance.LoadedAssetBundles.TryGetValue(hashCode, out loadedAssetBundle))
            {
                loadedAssetBundle.ReferencedCount++;
            }
            else
            {
                loadedAssetBundle = new LoadedAssetBundle(bundle, hashCode);
                AssetBundleManager.Instance.LoadedAssetBundles.Add(hashCode, loadedAssetBundle);
            }
            if (ownerKey != 0)
                AssetBundleManager.Instance.AddDenpency(ownerKey, loadedAssetBundle);
        }

        /// Add the depency resource.
        /// <param name="ownerKey"> The owner's name hashcode.</param>
        /// <param name="bundle">   Reference to bundle resource.</param>
        public void AddDenpency(int ownerKey, LoadedAssetBundle bundle)
        {
            if (bundle == null)
                return;
            List<LoadedAssetBundle> depencyList;
            if (m_loadedABDependency.TryGetValue(ownerKey, out depencyList))
            {
                depencyList.Add(bundle);
            }
            else
            {
                depencyList = new List<LoadedAssetBundle>(10);
                depencyList.Add(bundle);
                m_loadedABDependency[ownerKey] = depencyList;
            }
        }

        public void AddDepencyRef(int ownerKey)
        {
            List<LoadedAssetBundle> depencyList;
            if (m_loadedABDependency.TryGetValue(ownerKey, out depencyList))
            {
                for (int i = 0; i < depencyList.Count; i++)
                {
                    depencyList[i].ReferencedCount++;
                }
            }
        }

        public float m_nextUnloadUnusedAssetTime = float.MinValue;
        private bool m_unloadUnusedAssetsCompleted = true;
        public void DoUpdate()
        {
            AssetBundleResource.Instance.DoUpdate();
            ResourcesFolderResource.Instance.DoUpdate();

            if (
                m_unloadUnusedAssetsCompleted &&
                //!IGame.Instance.IsInLoading() &&
                m_nextUnloadUnusedAssetTime < Time.realtimeSinceStartup// &&
                                                                       // !IGame.Instance.IsOutGame()
                )
            {

                m_unloadUnusedAssetsCompleted = false;
                AsyncOperation callback = Resources.UnloadUnusedAssets();
                callback.completed += ResourcesUnloadDone;
                m_nextUnloadUnusedAssetTime = Time.realtimeSinceStartup + 60.0f;
            }
        }

        void ResourcesUnloadDone(AsyncOperation _)
        {
            m_unloadUnusedAssetsCompleted = true;
        }

        /// load a resource from the "Resources" folder, using synchronous mode.
        /// <param name="strResourcePath"> The relative path of the resource.</param>
        public UnityEngine.Object LoadResourceSync(VString strResourcePath, Type t = null)
        {
            return ResourcesFolderResource.Instance.LoadResourceSync(strResourcePath, t);
        }


        /// load a resource from the "Resources" folder, using asynchronous mode.
        /// <param name="strResourcePath"> The relative path of the resource.</param>
        /// <param name="callback">        Callback when the resource is loaded.</param>
        /// <param name="strResourcePath"> Type of the resource to load.</param>
        public void LoadResourceAsync(VString strResourcePath, LoadCallback callback, Type t = null)
        {
            ResourcesFolderResource.Instance.LoadResourceAsync(strResourcePath, callback, t);
        }



        /// Loads all assets in a folder or file at path in a Resources folder.
        /// <param name="strResourcePath">Pathname of the target folder.</param>
        public UnityEngine.Object[] LoadResourceAll(VString strResourcePath, Type t = null)
        {
            return ResourcesFolderResource.Instance.LoadResourceAll(strResourcePath, t);
        }



        /// Unload a resource from memory.
        /// <param name="strResourcePath">The relative path of the resource.</param>
        public void UnLoadResource(VString strResourcePath)
        {
            ResourcesFolderResource.Instance.UnLoadResource(strResourcePath);
        }

        /// Unload an assetbundle.
        /// <param name="strAssetBundleName">Name of the assetbundle.</param>
        public void UnLoadAssetBundle(VString strAssetBundleName)
        {
            LoadedAssetBundle bundle;
            Int32 hashCode = strAssetBundleName.GetHashCode();
            List<LoadedAssetBundle> dependeneyList;


            if (m_loadedAB.TryGetValue(hashCode, out bundle))
            {
                bundle.ReferencedCount--;
                if (m_loadedABDependency.TryGetValue(hashCode, out dependeneyList))
                {
                    for (int i = 0; i < dependeneyList.Count; ++i)
                    {
                        dependeneyList[i].ReferencedCount--;
                        if (dependeneyList[i].ReferencedCount <= 0)
                        {
                            dependeneyList[i].Bundle.Unload(false);
                            if (m_loadedAB.ContainsKey(dependeneyList[i].HashCode))
                                m_loadedAB.Remove(dependeneyList[i].HashCode);

                        }
                    }

                }
                if (bundle.ReferencedCount <= 0)
                {
                    bundle.Bundle.Unload(false);
                    m_loadedAB.Remove(hashCode);
                    if (dependeneyList != null)
                        m_loadedABDependency.Remove(hashCode);
                }
            }
        }

        /// Load an object asset from assetbundle.
        /// <param name="ab">        The assetbundle pointer.</param>
        /// <param name="objectname">The object name witch need to loading.</param>
        /// <param name="callback">  The callback function will do when load asset finished.</param>
        public UnityEngine.Object LoadAssetSync(AssetBundle ab, string objectname, Type t = null)
        {
            UnityEngine.Object o = null;
            if (t != null)
                o = ab.LoadAsset(objectname, t);
            else
                o = ab.LoadAsset(objectname);

            return o;
        }



        /// Load an assetbundle resouce with sync.
        /// <param name="manifest">  The manifest pointer.</param>
        /// <param name="strFolder"> The resource folder name. Like: "ui/"</param>
        /// <param name="strAssetBundleName"> The assetbundle name. </param>
        /// <param name="callback">  The callback function will do when load asset finished.</param>
        public void LoadAssetBundleSync(VString strFolder, VString strAssetBundleName, LoadCallback callback)
        {
            AssetBundleResource.Instance.LoadAssetBundleSync(AssetBundleResource.Instance.DataManifest, strFolder, strAssetBundleName, callback);
        }

        /// Load an assetbundle in asynchronous mode.
        /// <param name="manifest"> The manifest pointer.</param>
        /// <param name="strFolder">Relative path under the root assetbundle path, to load the assetbundle, like "ui/".</param>
        /// <param name="strAssetBundleName"> The assetbundle name. </param>
        /// <param name="callback"> The callback function will do when load asset finished.</param>
        public void LoadAssetBundleAsync(VString strFolder, VString strAssetBundleName, LoadCallback callback)
        {
            AssetBundleResource.Instance.LoadAssetBundleAsync(AssetBundleResource.Instance.DataManifest, strFolder, strAssetBundleName, callback);
        }

        /// Called when the instance will be destroyed.
        public void DoDestroy()
        {
            m_sInstance = null;
            ResourcesFolderResource.Instance.DoDestroy();
            AssetBundleResource.Instance.DoDestroy();
            m_loadedAB.Clear();
        }

        public void DoStart()
        {
            AssetBundleResource.Instance.DoStart();
        }

        public static void ReStart() {
            AssetBundleManager.Instance.DoDestroy();
            AssetBundleManager.Instance.DoStart();
        }
    }

}