//#define SCENE_EDITOR

using Giant.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using Giant.Res;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Giant.Resource {

    /// <summary>
    /// ab模式加载
    /// </summary>
    public class ABResourceLoader : IResourceLoader
    {
        struct stAssetLoadInfo
        {
            public int assetHashCode;
            public AssetBundleRequest request;
            public List<LoadCallback> callbackList;

            public void Release()
            {
                if (callbackList != null)
                {
                    ListPool<LoadCallback>.Release(callbackList);
                    callbackList = null;
                }
            }
        }

        Dictionary<int, stAssetLoadInfo> m_loadingAssets = new Dictionary<int, stAssetLoadInfo>(50);

        private void OnResourceLoadCompleteCallback(AsyncOperation oper)
        {
            AssetBundleRequest abr = oper as AssetBundleRequest;
            foreach (var info in m_loadingAssets)
            {
                if (info.Value.request == abr)
                {
                    for (int i = 0; i < info.Value.callbackList.Count; ++i)
                    {
                        if (info.Value.callbackList[i] != null)
                        {
                            info.Value.callbackList[i](abr.asset);
                        }
                    }

                    info.Value.Release();
                    m_loadingAssets.Remove(info.Key);
                    return;
                }
            }
        }

        /// <summary>
        /// 异步加载asset
        /// </summary>
        /// <param name="strAssetPath"></param>
        /// <param name="strAssetName"></param>
        /// <param name="strSuffix"></param>
        /// <param name="callback"></param>
        /// <param name="bShader"></param>
        /// <param name="t"></param>
        public override void LoadAssetAsync(string strAssetPath, string strAssetName, string strSuffix, LoadCallback callback = null, bool bShader = false, Type t = null)
        {
            VString abPath;
            if (strAssetPath.IndexOf(AssetPath) < 0)
                abPath = VString.Concat(IResourceLoader.strAssetFolder, strAssetPath, strAssetName, strSuffix, AllocType.Tmp);
            else
                abPath = VString.Concat(strAssetPath, strAssetName, strSuffix, AllocType.Tmp);
            abPath.ToLower();
            uint hashCode = ((uint)abPath.GetHashCode());
            stAssetLoadInfo info;
            if (m_loadingAssets.TryGetValue((int)hashCode, out info))
            {
                info.callbackList.Add(callback);
            }
            else
            {
                info.callbackList = ListPool<LoadCallback>.Get();
                info.assetHashCode = (int)hashCode;
                AssetBundle ab = bShader ? AssetBundleManager.Instance.GetShaderBundle() : AssetBundleManager.Instance.GetAssetbundle(hashCode);
                if (ab)
                {
                    info.request = ab.LoadAssetAsync(abPath.ToString());
                    info.request.completed += OnResourceLoadCompleteCallback;
                    info.callbackList.Add(callback);
                    m_loadingAssets.Add(info.assetHashCode, info);
                }
                else if (callback != null)
                {
                    callback(null);
                }
            }

            abPath.Dispose();
        }

        public override ResourceLoadAsync LoadAssetAsync(string strAssetPath, string strAssetName, string strSuffix, bool bShader = false, System.Type t = null) {
            ResourceLoadAsync resourceLoadAsync = new ResourceLoadAsync();

            VString abPath;
            if (strAssetPath.IndexOf(AssetPath) < 0)
                abPath = VString.Concat(IResourceLoader.strAssetFolder, strAssetPath, strAssetName, strSuffix, AllocType.Tmp);
            else
                abPath = VString.Concat(strAssetPath, strAssetName, strSuffix, AllocType.Tmp);
            abPath.ToLower();
            uint hashCode = ((uint)abPath.GetHashCode());
            stAssetLoadInfo info;
            if (m_loadingAssets.TryGetValue((int)hashCode, out info))
            {
                info.callbackList.Add((obj) => {
                    resourceLoadAsync.asset = obj;
                    resourceLoadAsync.isDone = true;
                });
            }
            else
            {
                info.callbackList = ListPool<LoadCallback>.Get();
                info.assetHashCode = (int)hashCode;
                AssetBundle ab = bShader ? AssetBundleManager.Instance.GetShaderBundle() : AssetBundleManager.Instance.GetAssetbundle(hashCode);
                if (ab)
                {
                    info.request = ab.LoadAssetAsync(abPath.ToString());
                    info.request.completed += OnResourceLoadCompleteCallback;
                    info.callbackList.Add((obj) => {
                        resourceLoadAsync.asset = obj;
                        resourceLoadAsync.isDone = true;
                    });
                    m_loadingAssets.Add(info.assetHashCode, info);
                }
                else
                {
                    resourceLoadAsync.asset = null;
                    resourceLoadAsync.isDone = true;
                }
            }

            abPath.Dispose();
            return resourceLoadAsync;
        }

        /// <summary>
        /// 同步加载Asset
        /// </summary>
        /// <param name="strAssetPath"></param>
        /// <param name="strAssetName"></param>
        /// <param name="strSuffix"></param>
        /// <param name="bShader"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public override UnityEngine.Object LoadAssetSync(string strAssetPath, string strAssetName, string strSuffix, bool bShader = false, Type t = null)
        {
            UnityEngine.Object result = null;

            VString abPath;
            if (strAssetPath.IndexOf(AssetPath, System.StringComparison.OrdinalIgnoreCase) < 0)
                abPath = VString.Concat(IResourceLoader.strAssetFolder, strAssetPath, strAssetName, strSuffix, AllocType.Tmp);
            else
                abPath = VString.Concat(strAssetPath, strAssetName, strSuffix, AllocType.Tmp);

            abPath.ToLower();
            AssetBundle bundle;
            if (bShader)
            {
                bundle = AssetBundleManager.Instance.GetShaderBundle();
            }
            else
            {
                uint hashCode = ((uint)abPath.GetHashCode());
                bundle = AssetBundleManager.Instance.GetAssetbundle(hashCode);
            }

            result = LoadResourceFromSceneFolderCallBack(bundle, abPath.ToString());
            abPath.Dispose();

            return result;
        }

        UnityEngine.Object LoadResourceFromSceneFolderCallBack(UnityEngine.Object o, string strName)
        {
            AssetBundle ab = o as AssetBundle;
            if (ab != null)
                return AssetBundleManager.Instance.LoadAssetSync(ab, strName);

            return null;
        }

        public override ResourceLoadAsync LoadSceneAsync(string scenepath, LoadSceneMode mode)
        {
            string sceneName = System.IO.Path.GetFileName(scenepath);
            Giant.Common.VString folder = Giant.Common.VString.From(Giant.Resource.IResourceLoader.abpath, Giant.Common.AllocType.Tmp);
            Giant.Common.VString sn = Giant.Common.VString.From(scenepath, Giant.Common.AllocType.Tmp);
            sn.ToLower();
            ResourceLoadAsync request = new ResourceLoadAsync();
            // request.sceneName = sceneName;

            Giant.Resource.AssetBundleManager.Instance.LoadAssetBundleAsync(folder, sn, (obj) =>
            {
                if (obj)
                {
                    var aopt = SceneManager.LoadSceneAsync(sceneName, mode);
                    aopt.completed += (sc) =>
                    {
                        //request.asset = sc.;
                        request.isDone = true;
                    };
                }
                else
                {
                    request.asset = null;
                    request.isDone = true;

                }
            });

            return request;
        }

        public override void UnLoadScene(string scenepath)
        {
            string sceneName = System.IO.Path.GetFileName(scenepath);
            var opt = SceneManager.UnloadSceneAsync(sceneName);
            if (opt != null)
            {
                opt.completed += (a) => { UnLoadSceneAB(sceneName); };
            }
            else
            {
                UnLoadSceneAB(sceneName);
            }
        }

        private void UnLoadSceneAB(string scenepath)
        {
            //Giant.Common.VString strPath = Giant.Common.VString.From(scenepath, Giant.Common.AllocType.Tmp);
            //Giant.Resource.AssetBundleManager.Instance.UnLoadAssetBundle(strPath);

            AssetsMgr.UnloadScene(scenepath);
            AssetsMgr.UnloadUnUsedAssetsAndBundle();
        }

        public override SceneLoadAsync LoadOutSceneAsync(string sceneName, LoadSceneMode mode)
        {
            string scenepath = "outscenes/" + sceneName;
            Giant.Common.VString folder = Giant.Common.VString.From(Giant.Resource.IResourceLoader.abpath, Giant.Common.AllocType.Tmp);
            Giant.Common.VString sn = Giant.Common.VString.From(scenepath, Giant.Common.AllocType.Tmp);
            sn.ToLower();
            SceneLoadAsync request = new SceneLoadAsync();
            // request.sceneName = sceneName;
            Giant.Resource.AssetBundleManager.Instance.LoadAssetBundleAsync(folder, sn, (obj) =>
            {
                if (obj)
                {
                    var aopt = SceneManager.LoadSceneAsync(sceneName, mode);
                    request._asyncOperation = aopt;
                    aopt.completed += (sc) =>
                    {
                        request.scene = SceneManager.GetSceneByName(sceneName);
                        request.isDone = true;
                    };
                }
                else
                {
                    request.scene = default(Scene);
                    request.isDone = true;

                }
            });

            return request;
        }

        public override void UnOutLoadScene(string sceneName)
        {
            var opt = SceneManager.UnloadSceneAsync(sceneName);
            opt.completed += (a) =>
            {
                UnLoadOutSceneAB(sceneName);
            };  
        }

        private  void UnLoadOutSceneAB(string sceneName)
        {
            string scenepath = "outscenes/" + sceneName;
            Giant.Common.VString strPath = Giant.Common.VString.From(scenepath, Giant.Common.AllocType.Tmp);
            Giant.Resource.AssetBundleManager.Instance.UnLoadAssetBundle(strPath);
        }

    }

}