using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
using UnitySceneManager = UnityEngine.SceneManagement.SceneManager;

namespace AssetManagement
{
    /// <summary>
    ///     管理资源池和资源抽取回调的情况
    /// </summary>
    public class AssetManager
    {
        private readonly List<BundleCallbackData> _bundleCallbacks = new List<BundleCallbackData>();
        private readonly Dictionary<string, BundleHandle> _bundleHandles = new Dictionary<string, BundleHandle>();
        private readonly List<SceneHandle> _sceneHandles = new List<SceneHandle>();
        public BundleManager bundleManager { get; private set; }

        public static AssetManager instance { get; private set; }

        /// <summary>
        ///     构造新的实例
        /// </summary>
        public static void CreateInstance()
        {
            if (instance != null)
                Debug.LogError(
                    "Process Error: AssetManager already has an instance, while trying to create a new one!");
            else
                instance = new AssetManager();
        }

        // /// <summary>
        // ///     移除旧的实例
        // /// </summary>
        // public static void Clear()
        // {
        //     if (instance != null)
        //     {
        //         instance.ClearAllAssets();
        //         instance = null;
        //     }
        // }

        // // 移除非场景等资源,自动走BundleManager予以卸载
        // public void ClearCommonAssets()
        // {
        //     _bundleCallbacks.Clear();
        //     foreach (var bundle in _bundleHandles.Values)
        //     {
        //         bundle.Dispose();
        //         bundleManager.UnloadBundle(bundle.bundleName);
        //     }
        //
        //     _bundleHandles.Clear();
        // }

        // private void ClearAllAssets()
        // {
        //     try
        //     {
        //         _bundleCallbacks.Clear();
        //         foreach (var bundle in _bundleHandles.Values)
        //             bundle.Dispose();
        //         // foreach (var bundle in _spriteHandles.Values)
        //         //     bundle.Dispose();
        //         foreach (var scene in _sceneHandles)
        //             bundleManager.UnloadBundle(scene.bundleName);
        //     }
        //     catch (Exception e)
        //     {
        //         Debug.LogError(e);
        //     }
        //
        //     try
        //     {
        //         AssetBundle.UnloadAllAssetBundles(true);
        //     }
        //     catch (Exception e)
        //     {
        //         Debug.LogError(e);
        //     }
        // }

        public bool IsLoadingScene()
        {
            var result = false;
            if (_sceneHandles != null)
                foreach (var handle in _sceneHandles)
                    if (handle.state == SceneHandle.SceneState.notLoad)
                    {
                        result = true;
                        break;
                    }

            return result;
        }

        public SceneHandle GetCurrentSceneHandle()
        {
            SceneHandle result;
            if (_sceneHandles == null || _sceneHandles.Count == 0)
                result = null;
            else
                result = _sceneHandles[0];
            return result;
        }

        public void Init(BundleManager bundleManager)
        {
            this.bundleManager = bundleManager;
            this.bundleManager.onComplete += OnBundleLoaded;
        }

        private void OnBundleLoaded(BundleRecord record)
        {
            BundleHandle handle;
            if (_bundleHandles.TryGetValue(record.name, out handle))
            {
                handle.OnAssetBundleLoad(record);
                // 如果加载完成时,已经没有资源需求
                CheckBundleUnload(handle);
            }

            // // 注:不可能有其他流程不依赖加载Sprite Bundle,所以主动卸载应该是安全的
            // // 如果项目里面还有作妖,再处理这个流程
            // SpriteBundleHandle spriteHandle;
            // if (_spriteHandles.TryGetValue(record.name, out spriteHandle))
            // {
            //     spriteHandle.OnAssetBundleLoad(record.assetBundle);
            //     if (spriteHandle.callbackList.Count == 0 && spriteHandle.inUseSprites.Count == 0)
            //     {
            //         spriteHandle.Dispose();
            //         _spriteHandles.Remove(record.name);
            //         _bundleManager.UnloadBundle(record.name);
            //     }
            // }
            for (var i = _bundleCallbacks.Count - 1; i >= 0; i--)
            {
                var bundle = _bundleCallbacks[i];
                if (bundle.bundleName == record.name)
                {
                    _bundleCallbacks.RemoveAt(i);
                    bundle.CallBack(record);
                }
            }

            foreach (var scene in _sceneHandles)
                if (scene.bundleName == record.name)
                    scene.OnAssetBundleLoad();
        }

        public SceneHandle LoadScene(string bundleName, string sceneName, LoadSceneMode mode,
            SceneLoadCallback onSceneLoad = null, bool allowReload = false)
        {
            // 注:SceneBundle应该是单一场景,包含多个场景时的情况尚未测试。
            var handle = _sceneHandles.Find(a => a.bundleName == bundleName && a.sceneName == sceneName);
            if (handle != null)
            {
                if (handle.mode != mode)
                {
                    Debug.LogError(string.Format(
                        "Loading or loaded scene cannot switch loading mode! Cannot switch from {0} to {1}!",
                        handle.mode, mode));
                }
                else
                {
                    if (handle.state == SceneHandle.SceneState.notLoad)
                    {
                        if (onSceneLoad != null)
                            handle.AddCallback(onSceneLoad);
                    }
                    else if (allowReload)
                    {
                        if (onSceneLoad != null)
                            handle.AddCallback(onSceneLoad);
                        handle.ReloadScene();
                    }
                    else
                    {
                        if (onSceneLoad != null)
                            handle.CallBackOne(onSceneLoad);
                    }

                    // 清理单一场景可能覆盖的叠加场景
                    // 注:Reload叠加场景本身也是个不正常的操作
                    if (mode == LoadSceneMode.Single)
                        for (var i = _sceneHandles.Count - 1; i >= 0; i--)
                        {
                            var scene = _sceneHandles[i];
                            if (scene != handle)
                            {
                                scene.Dispose();
                                bundleManager.UnloadBundle(scene.bundleName);
                                _sceneHandles.RemoveAt(i);
                            }
                        }
                }
            }
            else
            {
                BundleRecord record;
                var loaded = bundleManager.TryLoadBundle(bundleName, out record);
                // 如果是单一场景,立刻清空当前已经加载的场景Bundle
                // 场景本身由新场景执行覆盖
                if (mode == LoadSceneMode.Single)
                {
                    foreach (var scene in _sceneHandles)
                    {
                        scene.Dispose();
                        bundleManager.UnloadBundle(scene.bundleName);
                    }

                    _sceneHandles.Clear();
                }
                handle = new SceneHandle(bundleName, sceneName, record, mode);
                _sceneHandles.Add(handle);
                // 允许不加回调加载场景,场景本身记录Bundle足矣
                if (onSceneLoad != null)
                    handle.AddCallback(onSceneLoad);
                if (loaded)
                    handle.OnAssetBundleLoad();
            }

            return handle;
        }
        
        // 就做个这个接口清了算了。
        public void UnloadAllScene()
        {
            foreach (var sceneHandle in _sceneHandles)
            {
                if (sceneHandle.mode != LoadSceneMode.Single)
                    sceneHandle.UnloadScene();
                bundleManager.UnloadBundle(sceneHandle.bundleName);
            }
        
            _sceneHandles.Clear();
        }

        // /// <summary>
        // ///     主动卸载一个叠加场景,Single应该直接走LoadScene覆盖
        // /// </summary>
        // public void UnloadScene(string bundleName, string sceneName)
        // {
        //     var i = _sceneHandles.FindIndex(a => a.bundleName == bundleName && a.sceneName == sceneName);
        //     if (i >= 0)
        //     {
        //         var handle = _sceneHandles[i];
        //         handle.UnloadScene();
        //         bundleManager.UnloadBundle(handle.bundleName);
        //         _sceneHandles.RemoveAt(i);
        //     }
        // }
        
        /// <summary>
        ///     异步处理资源回调接口
        /// </summary>
        /// <param name="name">资源名称</param>
        /// <param name="token">回调接收者,用于监视回调前是否接收者析构</param>
        /// <param name="callback">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        public void LoadAsset<T>(string name, Component token, AssetLoadCallback<T> callback) where T : Object
        {
#if UNITY_EDITOR
            ExtensionCheck(name);
#endif
            var bundleName = name.ToLower().ToUrl();
            var assetName = Path.GetFileNameWithoutExtension(name);
            LoadAsset(bundleName, assetName, token, callback);
        }

        public T LoadAssetSync<T>(string name) where T : Object
        {
#if UNITY_EDITOR
            ExtensionCheck(name);
#endif
            var bundleName = name.ToLower().ToUrl();
            var assetName = Path.GetFileNameWithoutExtension(name);
            return LoadAssetSync<T>(bundleName, assetName);
        }
#if UNITY_EDITOR
        private static void ExtensionCheck(string name)
        {
            if (Path.HasExtension(name))
                Debug.LogError(string.Format("The path {0} has extension! You should be constant with Resources.Load!",
                    name));
        }
#endif

        /// <summary>
        ///     异步处理资源回调接口
        /// </summary>
        /// <param name="bundleName">资源包名</param>
        /// <param name="assetName">资源名</param>
        /// <param name="token">回调接收者,用于监视回调前是否接收者析构</param>
        /// <param name="callback">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        public void LoadAsset<T>(string bundleName, string assetName, Component token, AssetLoadCallback<T> callback)
            where T : Object
        {
            var handle = LoadBundle(bundleName);
            handle.AddLoading(assetName, token, callback);
        }

        // public void Preload(string bundleName, Component token, BundleLoadCallback callback)
        // {
        //     BundleHandle handle;
        //     if (_bundleHandles.TryGetValue(bundleName, out handle))
        //     {
        //         handle.preload = true;
        //         if (handle.isBundleLoaded)
        //         {
        //             if (callback != null)
        //                 callback.Invoke(handle.bundleName);
        //         }
        //         else if (callback != null)
        //             _bundleCallbacks.Add(new BundleCallbackData(bundleName, token, callback));
        //     }
        //     else
        //     {
        //         if (callback != null)
        //             _bundleCallbacks.Add(new BundleCallbackData(bundleName, token, callback));
        //         handle = LoadBundle(bundleName);
        //         handle.preload = true;
        //     }
        // }
        //
        // public BundleHandle PreloadSync(string bundleName)
        // {
        //     var handle = LoadBundleSync(bundleName);
        //     if (handle != null)
        //         handle.preload = true;
        //     return handle;
        // }

        // public void RemovePreload(string bundleName)
        // {
        //     BundleHandle handle;
        //     if (_bundleHandles.TryGetValue(bundleName, out handle))
        //     {
        //         handle.preload = false;
        //         CheckBundleUnload(handle);
        //     }
        //     for (var i = _bundleCallbacks.Count - 1; i >= 0; i--)
        //         if (_bundleCallbacks[i].bundleName == bundleName)
        //             _bundleCallbacks.RemoveAt(i);
        // }

        private BundleHandle LoadBundleSync(string bundleName)
        {
            BundleHandle handle;
            var loadBundle = true;
            if (_bundleHandles.TryGetValue(bundleName, out handle))
                if (handle.isBundleLoaded)
                    loadBundle = false;
            // 注:无需触发OnBundleLoaded,原有Bundle会在LoadBundleSync触发
            if (loadBundle)
            {
                var record = bundleManager.LoadBundleSync(bundleName);
                if (record == null)
                {
                    Debug.LogError("Cannot handle the situation of an empty Bundle Record!");
                }
                else
                {
                    if (handle == null)
                    {
                        handle = new BundleHandle(bundleName);
                        _bundleHandles[bundleName] = handle;
                        handle.isBundleLoaded = true;
                        handle.bundle = record.assetBundle;
                    }
                }
            }

            return handle;
        }

        private BundleHandle LoadBundle(string bundleName)
        {
            BundleHandle handle;
            if (!_bundleHandles.TryGetValue(bundleName, out handle))
            {
                handle = new BundleHandle(bundleName);
                BundleRecord record;
                if (bundleManager.TryLoadBundle(bundleName, out record))
                    handle.OnAssetBundleLoad(record);
                _bundleHandles.Add(bundleName, handle);
            }

            return handle;
        }

        public T LoadAssetSync<T>(string bundleName, string assetName) where T : Object
        {
            T result = null;
            var handle = LoadBundleSync(bundleName);
            if (handle != null && handle.bundle)
                result = handle.LoadSync<T>(assetName);
            return result;
        }

        public void RemoveAsset(string bundle, string assetName)
        {
            BundleHandle handle;
            if (_bundleHandles.TryGetValue(bundle, out handle))
            {
                handle.RemoveAsset(assetName);
                CheckBundleUnload(handle);
            }
        }

        private void CheckBundleUnload(BundleHandle handle)
        {
            if (handle.loadedList.Count == 0 &&
                handle.loadingList.Count == 0)
            {
                handle.Dispose();
                _bundleHandles.Remove(handle.bundleName);
                bundleManager.UnloadBundle(handle.bundleName);
            }
        }

        // 特殊处理整个AssetBundle抛弃的流程
        public void UnloadBundle(string bundle)
        {
            BundleHandle handle;
            if (_bundleHandles.TryGetValue(bundle, out handle))
            {
                handle.Dispose();
                _bundleHandles.Remove(bundle);
            }

            bundleManager.UnloadBundle(bundle);
        }

        public void CancelCallback<T>(string bundle, string assetName, Component token, AssetLoadCallback<T> callback)
            where T : Object
        {
            BundleHandle handle;
            if (_bundleHandles.TryGetValue(bundle, out handle))
                handle.RemoveLoading(assetName, token, callback);
        }
    }

    public delegate void AssetLoadCallback<in T>(string bundleName, string assetName, T asset) where T : Object;

    public delegate void BundleLoadCallback(string handle);

    public delegate void SceneLoadCallback(bool success, string name);

    public class BundleHandle
    {
        public readonly string bundleName;
        public readonly List<AssetHandle> loadedList = new List<AssetHandle>();
        public readonly List<AssetLoadHandle> loadingList = new List<AssetLoadHandle>();
        public AssetBundle bundle;

        public bool isBundleLoaded;
        // // 是否预加载或者挂住的资源包,不执行释放
        // public bool preload;

        public BundleHandle(string bundleName)
        {
            this.bundleName = bundleName;
        }

        public T LoadSync<T>(string assetName) where T : Object
        {
            T result = null;
            var load = true;
            var type = typeof(T);
            foreach (var loaded in loadedList)
                if (loaded.type == type && loaded.assetName == assetName)
                {
                    result = loaded.asset as T;
                    load = false;
                    break;
                }

            if (load)
                if (isBundleLoaded && bundle)
                {
                    result = bundle.LoadAsset<T>(assetName);
                    var loadingHandle = loadingList.Find(a => a.assetName == assetName && a.type == type);
                    // 触发异步加载完成事件,应该没有所谓的先后顺序,除非原项目做了其他的妖,就需要提出到Manager层延后触发
                    if (loadingHandle != null)
                        OnAssetLoadComplete(loadingHandle, result);
                    // 没有异步加载等待时,就自行建立加载成功记录
                    else
                        loadedList.Add(new AssetHandle(assetName, result, type));
                }

            return result;
        }

        public void AddLoading<T>(string assetName, Component token, AssetLoadCallback<T> callback) where T : Object
        {
            var type = typeof(T);
            var loading = true;
            if (isBundleLoaded)
            {
                if (bundle)
                {
                    var handle = loadedList.Find(a => a.type == type && a.assetName == assetName);
                    // 资源已经加载完成的情况
                    if (handle != null)
                    {
                        loading = false;
                        var item = handle.asset as T;
                        try
                        {
                            callback.Invoke(bundleName, assetName, item);
                        }
                        catch (Exception e)
                        {
                            Debug.LogError(e);
                        }
                    }
                }
                // 加载到空Bundle的情况,直接返回空值
                else
                {
                    loading = false;
                    callback.Invoke(bundleName, assetName, null);
                }
            }

            if (loading)
            {
                var handle = loadingList.Find(a => a.type == type && a.assetName == assetName);
                if (handle == null)
                {
                    handle = new AssetLoadHandle(type, bundleName, assetName);
                    handle.OnComplete += OnAssetLoadComplete;
                    // 注:异步回调至少等一帧,因此可以先加载等回调
                    if (bundle)
                        handle.Load(bundle);
                    loadingList.Add(handle);
                }

                handle.AddCallback(token, callback);
            }
        }

        public void Dispose()
        {
            if (loadingList != null)
                foreach (var loading in loadingList)
                    loading.Dispose();
        }

        public void RemoveLoading<T>(string assetName, Component token, AssetLoadCallback<T> callback) where T : Object
        {
            var type = typeof(T);
            foreach (var handle in loadingList)
                if (handle.type == type && handle.assetName == assetName)
                    handle.RemoveCallback(token, callback);
        }

        // 注:删除回调不会出现泄漏 - 资源一旦从Bundle提取就唯一,并且不能主动析构
        // Bundle本身会因为AssetManager层没有引用而卸载
        public void RemoveAsset(string assetName)
        {
            for (var i = loadedList.Count - 1; i >= 0; i--)
            {
                var handle = loadedList[i];
                //if (handle.type == type && handle.assetName == assetName)
                if (handle.assetName == assetName)
                    loadedList.RemoveAt(i);
            }

            for (var i = loadingList.Count - 1; i >= 0; i--)
            {
                var handle = loadingList[i];
                //if (handle.type == type && handle.assetName == assetName)
                if (handle.assetName == assetName)
                    loadingList.RemoveAt(i);
            }
        }

        /// <summary>
        ///     开始加载AssetBundle,返回是否有必要保留bundle
        /// </summary>
        public void OnAssetBundleLoad(BundleRecord assetBundle)
        {
            if (isBundleLoaded)
            {
                Debug.LogError(string.Format(
                    "Unexpected process, BundleHandle {0} has already hold a bundle, but get a new one!", bundleName));
            }
            else
            {
                isBundleLoaded = true;
                bundle = assetBundle.assetBundle;
                var callback = false;
                if (bundle)
                {
                    // 特殊处理场景资源包 - 同样回调空值,并且使用占位资源
                    if (bundle.isStreamedSceneAssetBundle)
                    {
                        callback = true;
                        Debug.LogError(
                            "You should never load a scene bundle via Bundle Handle! Use LoadScene interface instead!");
                    }
                    else
                    {
                        foreach (var loading in loadingList)
                            loading.Load(bundle);
                    }
                }
                else
                {
                    callback = true;
                }

                if (callback)
                {
                    foreach (var loading in loadingList)
                        loading.Callback(null);
                    loadingList.Clear();
                }
            }
        }

        private void OnAssetLoadComplete(AssetLoadHandle loadHandle, Object asset)
        {
            loadingList.Remove(loadHandle);
            var handle = new AssetHandle(loadHandle.assetName, asset, loadHandle.type);
            loadedList.Add(handle);
            loadHandle.Callback(asset);
        }
    }

    public abstract class AssetCallbackData
    {
        public readonly string assetName;
        public readonly string bundleName;
        public readonly Component token;

        public AssetCallbackData(string bundleName, string assetName, Component token)
        {
            this.bundleName = bundleName;
            this.assetName = assetName;
            this.token = token;
        }

        public abstract void CallBack(Object asset);
    }

    public class AssetCallbackData<T> : AssetCallbackData where T : Object
    {
        public readonly AssetLoadCallback<T> callBack;

        public AssetCallbackData(string bundleName, string assetName, Component token, AssetLoadCallback<T> callback) :
            base(bundleName, assetName,
                token)
        {
            callBack = callback;
        }

        public override void CallBack(Object asset)
        {
            var item = asset as T;
            try
            {
                callBack.Invoke(bundleName, assetName, item);
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
    }

    public class AssetHandle
    {
        public Object asset;
        public string assetName;
        public Type type;

        public AssetHandle(string assetName, Object asset, Type type)
        {
            this.assetName = assetName;
            this.asset = asset;
            this.type = type;
        }
    }

    public class BundleCallbackData
    {
        private readonly BundleLoadCallback _callback;
        private readonly Component _token;
        public readonly string bundleName;

        public BundleCallbackData(string bundleName, Component token, BundleLoadCallback callback)
        {
            this.bundleName = bundleName;
            _token = token;
            _callback = callback;
        }

        public void CallBack(BundleRecord bundleHandle)
        {
            if (_token && _callback != null)
                _callback.Invoke(bundleHandle.name);
        }
    }

    public class AssetLoadHandle
    {
        private readonly List<AssetCallbackData> _callbacks = new List<AssetCallbackData>();
        public readonly string assetName;
        public readonly string bundleName;
        public readonly Type type;
        private AssetBundleRequest _assetBundleRequest;

        public AssetLoadHandle(Type type, string bundleName, string assetName)
        {
            this.type = type;
            this.bundleName = bundleName;
            this.assetName = assetName;
        }

        public event UnityAction<AssetLoadHandle, Object> OnComplete;

        public void Load(AssetBundle assetBundle)
        {
            _assetBundleRequest = assetBundle.LoadAssetAsync(assetName, type);
            _assetBundleRequest.completed += OnAssetLoad;
        }

        public void AddCallback<T>(Component token, AssetLoadCallback<T> callback) where T : Object
        {
            _callbacks.Add(new AssetCallbackData<T>(bundleName, assetName, token, callback));
        }

        public void RemoveCallback<T>(Component token, AssetLoadCallback<T> callback) where T : Object
        {
            for (var i = 0; i < _callbacks.Count; i++)
                if (_callbacks[i].token == token)
                {
                    var callbackFunc = _callbacks[i] as AssetLoadCallback<T>;
                    if (callbackFunc == callback)
                    {
                        _callbacks.RemoveAt(i);
                        break;
                    }
                }
        }

        public void Dispose()
        {
            if (_assetBundleRequest != null)
            {
                _assetBundleRequest.completed -= OnAssetLoad;
                _assetBundleRequest = null;
            }
        }

        public void Callback(Object asset)
        {
            foreach (var callback in _callbacks)
                if (callback.token)
                    callback.CallBack(asset);
                else
                    Debug.LogError(string.Format("A listener to asset {0} didn't remove callback before destroyed!",
                        assetName));
        }

        private void OnAssetLoad(AsyncOperation asyncOperation)
        {
            if (asyncOperation == _assetBundleRequest)
            {
                if (OnComplete != null)
                    OnComplete.Invoke(this, _assetBundleRequest.asset);
            }
            else
            {
                Debug.LogError("AssetLoadHandle didn't receive expected AssetBundleRequest from callback!");
            }
        }
    }

    public class SceneHandle
    {
        public enum SceneState
        {
            notLoad,
            loaded,
            Error
        }

        /// <summary>
        ///     加载资源包在整个流程的占比
        /// </summary>
        public const float bundlePercentage = 0.3f;

        public readonly string bundleName;
        public readonly List<SceneLoadCallback> callbacks = new List<SceneLoadCallback>();
        public readonly LoadSceneMode mode;
        public readonly string sceneName;
        private AsyncOperation _asyncOperation;
        public BundleRecord record;

        public SceneHandle(string bundleName,
            string sceneName,
            BundleRecord bundleRecord,
            LoadSceneMode mode)
        {
            this.bundleName = bundleName;
            this.sceneName = sceneName;
            record = bundleRecord;
            this.mode = mode;
        }

        public SceneState state { get; private set; }

        /// <summary>
        ///     开始加载AssetBundle,返回是否有必要保留bundle
        /// </summary>
        public void OnAssetBundleLoad()
        {
            if (record == null || !record.assetBundle || record.state == BundleState.Error)
            {
                state = SceneState.Error;
                Debug.LogError(string.Format("Scene Bundle {0} for scene {1} is not properly loaded!", bundleName,
                    sceneName));
                Callback();
            }
            else if (_asyncOperation == null)
            {
                _asyncOperation = UnitySceneManager.LoadSceneAsync(sceneName, mode);
                _asyncOperation.completed += OnSceneLoadComplete;
            }
        }

        public float GetLoadingProgress()
        {
            if (state == SceneState.loaded || state == SceneState.Error)
                return 1f;
            if (record == null)
                return 0f;
            if (_asyncOperation == null)
                return bundlePercentage * record.GetTotalProgress();
            return bundlePercentage + (1f - bundlePercentage) * _asyncOperation.progress;
        }

        public void AddCallback(SceneLoadCallback callback)
        {
            if (state == SceneState.notLoad)
            {
                callbacks.Add(callback);
            }
            else
            {
                var success = state == SceneState.loaded;
                try
                {
                    callback.Invoke(success, sceneName);
                }
                catch (Exception e)
                {
                    Debug.LogError(e);
                }
            }
        }

        public void RemoveCallback(SceneLoadCallback callback)
        {
            for (var i = callbacks.Count - 1; i >= 0; i--)
                if (callbacks[i] == callback)
                    callbacks.RemoveAt(i);
        }

        /// <summary>
        ///     卸载一个叠加场景,主要场景使用肯定出问题
        /// </summary>
        public void UnloadScene()
        {
            if (mode != LoadSceneMode.Additive)
            {
                Debug.LogError("Only additive scenes can be unloaded! Major ones shall only be replaced instead!");
            }
            else
            {
                var scene = UnitySceneManager.GetSceneByName(sceneName);
                if (scene.isLoaded)
                    UnitySceneManager.UnloadSceneAsync(scene);
            }
        }

        public void Dispose()
        {
            callbacks.Clear();
            record = null;
            if (_asyncOperation != null)
                _asyncOperation.completed -= OnSceneLoadComplete;
            state = SceneState.notLoad;
        }

        /// <summary>
        ///     特殊接口,负责重新加载当前场景
        /// </summary>
        public void ReloadScene()
        {
            // state == SceneState.notLoad 尚未完成加载,就继续等待加载完成,不用刷新
            switch (state)
            {
                case SceneState.Error:
                    Debug.LogError("Try to reload a scene that has error during previous loading!");
                    break;
                case SceneState.loaded:
                    OnAssetBundleLoad();
                    break;
            }
        }

        private void OnSceneLoadComplete(AsyncOperation asyncOperation)
        {
            if (_asyncOperation != asyncOperation)
            {
                state = SceneState.Error;
                Debug.LogError("Unexpected process - the AsyncOperation is not what SceneHandle is waiting for!");
                Callback();
            }
            else
            {
                state = SceneState.loaded;
                Callback();
            }

            _asyncOperation = null;
        }

        private void Callback()
        {
            foreach (var callback in callbacks)
                CallBackOne(callback);
            callbacks.Clear();
        }

        public void CallBackOne(SceneLoadCallback callback)
        {
            var success = state == SceneState.loaded;
            try
            {
                callback.Invoke(success, sceneName);
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
    }
}