using Cysharp.Threading.Tasks;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace TFramework
{
    public class ResourceManager : BaseManager
    {
        /// <summary>
        /// 所有AssetBundle资源包清单
        /// </summary>
        public AssetBundleManifest AssetBundleManifest { get; private set; }

        /// <summary>
        /// 缓存的所有AssetBundle包【AB包名称、AB包】
        /// </summary>
        public Dictionary<string, AssetBundle> AssetBundles { get; private set; } = new Dictionary<string, AssetBundle>();

        /// <summary>
        /// 当前激活的场景
        /// </summary>
        public Scene CurrentScene { get; private set; }

        public void Awake()
        {
            CurrentScene= SceneManager.GetActiveScene();
        }

        public T LoadAsset<T>(LoadResHandle<T> resHandle) where T : UnityEngine.Object
        {
            return LoadAsset<T>(resHandle.ResourcePath, resHandle.AssetPath, resHandle.AssetBundleName, resHandle.LoadMode);
        }

        /// <summary>
        /// 同步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resPath"></param>
        /// <param name="assetPath"></param>
        /// <param name="assetBundleName"></param>
        /// <param name="resLoadMode"></param>
        /// <returns></returns>
        public T LoadAsset<T>(string resPath, string assetPath, string assetBundleName = "", ResLoadMode resLoadMode = ResLoadMode.Resourece) where T : UnityEngine.Object
        {
            if (resLoadMode == ResLoadMode.Resourece)
                return Resources.Load<T>(resPath);
            else
            {
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode == UpdateMode.Editor)
                    return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(assetPath);

#endif
                assetBundleName = assetBundleName.ToLower();
                if (AssetBundleManifest == null)
                {
                    if (!AssetBundles.ContainsKey(AssetConfig.AssetBundleManifestName))
                        AssetBundles.Add(AssetConfig.AssetBundleManifestName,
                            AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + AssetConfig.AssetBundleManifestName));
                    AssetBundleManifest = AssetBundles[AssetConfig.AssetBundleManifestName].LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                }
                string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                for (int i = 0; i < dependencies.Length; i++)
                {
                    if (!AssetBundles.ContainsKey(dependencies[i]))
                        AssetBundles.Add(dependencies[i], AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + dependencies[i]));
                }
                if (!AssetBundles.ContainsKey(assetBundleName))
                    AssetBundles.Add(assetBundleName, AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + assetBundleName));
                return AssetBundles[assetBundleName].LoadAsset<T>(assetPath);
            }
        }

        /// <summary>
        /// 同步加载Resources下的资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="resPath">资源路径</param>
        /// <returns></returns>
        public T LoadAsset<T>(string resPath) where T : UnityEngine.Object => LoadAsset<T>(resPath, "", "");

        /// <summary>
        /// 同步加载AssetBundle资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="assetPath">资源路径</param>
        /// <param name="assetBundleName">AB包名</param>
        /// <returns></returns>
        public T LoadAsset<T>(string assetPath, string assetBundleName) where T : UnityEngine.Object => LoadAsset<T>("", assetPath, assetBundleName, ResLoadMode.AssetBundle);


        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="resPath">资源路径</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="onLoadDone">加载完成事件</param>
        /// <returns></returns>
        public Coroutine LoadAssetAsync<T>(LoadResHandle<T> resHandle) where T : UnityEngine.Object
        {
            return StartCoroutine(LoadAsync(resHandle));
        }

        private IEnumerator LoadAsync<T>(LoadResHandle<T> resHandle) where T : UnityEngine.Object
        {
            UnityEngine.Object asset = null;
            if (resHandle.LoadMode == ResLoadMode.Resourece)
            {
                ResourceRequest request = Resources.LoadAsync<T>(resHandle.ResourcePath);
                while (!request.isDone)
                {
                    resHandle.OnLoading?.Invoke(request.progress);
                    yield return null;
                }
                resHandle.OnLoading?.Invoke(1);
                if (!request.asset)
                    throw new UnityException($"资源加载失败：Resources文件夹中不存在资源 {resHandle.ResourcePath}!");
                else
                    asset = request.asset;
            }
            else
            {
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode == UpdateMode.Editor)
                {
                    resHandle.OnLoading?.Invoke(1);
                    asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(resHandle.AssetPath);
                    if (asset)
                        asset = asset as T;
                    else
                        throw new UnityException($"加载资源失败：路径{resHandle.AssetPath}中不存在资源 {resHandle.AssetPath}！");
                }
                else
                {
                    //等待相关依赖资源的加载
                    yield return LoadDependenciesAssetBundleAsync(resHandle.AssetBundleName.ToLower());
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName.ToLower(), resHandle.OnLoading);
                    if (AssetBundles.ContainsKey(resHandle.AssetBundleName.ToLower()))
                    {
                        //asset = AssetBundles[resHandle.AssetBundleName].LoadAsset<T>(resHandle.AssetPath);
                        AssetBundleRequest request = AssetBundles[resHandle.AssetBundleName.ToLower()].LoadAssetAsync<T>(resHandle.AssetPath);
                        yield return request;
                        while (!request.isDone)
                        {
                            resHandle.OnLoading(request.progress / 2 + 0.5f);
                            yield return null;
                        }
                        asset = request.asset;
                        if (!asset)
                            throw new UnityException(string.Format("加载资源失败：AB包 {0} 中不存在资源 {1}！", resHandle.AssetBundleName, resHandle.AssetPath));
                    }
                }
#else
                yield return LoadDependenciesAssetBundleAsync(resHandle.AssetBundleName);
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName, resHandle.OnLoading);
                    if (AssetBundles.ContainsKey(resHandle.AssetBundleName.ToLower()))
                    {
                        AssetBundleRequest request = AssetBundles[resHandle.AssetBundleName.ToLower()].LoadAssetAsync<T>(resHandle.AssetPath);
                        yield return request;
                        while(!request.isDone)
                        {
                            resHandle.OnLoading(request.progress / 2+0.5f);
                            yield return null;
                        }
                        asset = request.asset;
                        if (!asset)
                            throw new UnityException(string.Format("加载资源失败：AB包 {0} 中不存在资源 {1}！", resHandle.AssetBundleName, resHandle.AssetPath));
                    }
#endif
            }

            if (asset)
            {
                resHandle.OnLoadOver?.Invoke(asset as T);
            }
            else
                resHandle.OnLoadOver?.Invoke(null);

        }

        /// <summary>
        /// 异步加载场景(Resource模式)
        /// </summary>
        /// <param name="scenePath">场景路径</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="onLoadDone">加载完成事件</param>
        /// <returns></returns>
        public Coroutine LoadSceneAsync(string scenePath, TAction<float> OnLoading, TAction<Scene> OnLoadOver, LoadSceneMode loadSceneMode = LoadSceneMode.Single)
        {
            return StartCoroutine(LoadScene(scenePath, ResLoadMode.Resourece, "", OnLoading, OnLoadOver, loadSceneMode));
        }


        /// <summary>
        /// 异步加载场景
        /// </summary>
        /// <param name="scenePath">场景路径</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="onLoadDone">加载完成事件</param>
        /// <returns></returns>
        public Coroutine LoadSceneAsync(LoadResHandle<Scene> resHandle, LoadSceneMode loadSceneMode = LoadSceneMode.Single)
        {
            return StartCoroutine(LoadScene(resHandle.AssetPath,resHandle.LoadMode,resHandle.AssetBundleName,resHandle.OnLoading,resHandle.OnLoadOver, loadSceneMode));
        }

        private IEnumerator LoadScene(string scenePath, ResLoadMode loadMode ,string assetBundleName ,TAction<float> OnLoading,TAction<Scene> OnLoadOver, LoadSceneMode loadSceneMode)
        {
            if (SceneManager.GetSceneByPath(scenePath) == CurrentScene)
            {
                Debug.LogWarning($"场景{scenePath}已加载！");
                yield break;
            }
            if (loadMode == ResLoadMode.Resourece)
            {
                AsyncOperation ao = SceneManager.LoadSceneAsync(scenePath, loadSceneMode);
                while (!ao.isDone)
                {
                    OnLoading?.Invoke(ao.progress);
                    yield return null;
                }
            }
            else
            {
                yield return LoadDependenciesAssetBundleAsync(assetBundleName.ToLower());
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode == UpdateMode.Editor)
                {
                    LoadSceneParameters parameters = new LoadSceneParameters()
                    {
                        loadSceneMode = LoadSceneMode.Single,
                        localPhysicsMode = LocalPhysicsMode.None
                    };
                    AsyncOperation ao = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(scenePath, parameters);
                    while (!ao.isDone)
                    {
                        OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
                }
                else
                {
                    yield return LoadAssetBundleAsync(assetBundleName.ToLower(), OnLoading);
                    AsyncOperation ao = SceneManager.LoadSceneAsync(scenePath, loadSceneMode);
                    while (!ao.isDone)
                    {
                        OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
                    Debug.Log($"加载场景{scenePath}完成");
                }
#else
                    yield return LoadAssetBundleAsync(assetBundleName, OnLoading);
                    AsyncOperation ao = SceneManager.LoadSceneAsync(scenePath, loadSceneMode);
                    while (!ao.isDone)
                    {
                        OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
#endif
            }
            Debug.Log($"加载场景{scenePath}完成");
            OnLoading?.Invoke(1);
            OnLoadOver?.Invoke(SceneManager.GetSceneByPath(scenePath));
            CurrentScene = SceneManager.GetActiveScene();
            //SceneManager.SetActiveScene(CurrentScene);
        }

        /// <summary>
        /// 异步清理内存 回收垃圾
        /// </summary>
        /// <returns></returns>
        public Coroutine ClearMemoryAsync()
        {
            return StartCoroutine(ClearMemory());
        }

        /// <summary>
        /// 清理内存，释放空闲内存（异步）
        /// </summary>
        /// <returns>协程迭代器</returns>
        private IEnumerator ClearMemory()
        {
            yield return Resources.UnloadUnusedAssets();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        /// <summary>
        /// 异步卸载资源
        /// </summary>
        /// <returns></returns>
        public Coroutine UnLoadAsstAsync(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false)
        {
            return StartCoroutine(UnLoadAsst(resLoadMode, assetBundleName, unloadAllLoadedObjects));
        }

        private IEnumerator UnLoadAsst(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false)
        {
            yield return UnLoadAsset(ResLoadMode.Resourece, assetBundleName, unloadAllLoadedObjects);
        }

        /// <summary>
        /// 异步卸载场景
        /// </summary>
        /// <param name="scene"></param>
        /// <returns></returns>
        public Coroutine UnloadSceneAsync(Scene scene, TAction<float> OnUnloading = null, TAction OnUnloadOver = null)
        {
            Debug.Log($"卸载场景{scene.name}");
            return StartCoroutine(UnloadScene(scene, OnUnloading, OnUnloadOver));
        }

        private IEnumerator UnloadScene(Scene scene, TAction<float> OnUnloading, TAction OnUnloadOver)
        {
            AsyncOperation ao = SceneManager.UnloadSceneAsync(scene);
            while (!ao.isDone)
            {
                OnUnloading?.Invoke(ao.progress);
                yield return null;
            }
            OnUnloadOver?.Invoke();
            CurrentScene = SceneManager.GetActiveScene();

        }

        public IEnumerator UnLoadAsset(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false)
        {
            if (resLoadMode == ResLoadMode.Resourece)
            {
                yield return Resources.UnloadUnusedAssets();
            }
            else
            {
                assetBundleName = assetBundleName.ToLower();
                if (AssetBundles.ContainsKey(assetBundleName))
                {
                    AssetBundles[assetBundleName].Unload(unloadAllLoadedObjects);
                    AssetBundles.Remove(assetBundleName);
                }
                yield return null;
            }
        }

        public async UniTask UnLoadAllABAssetAsync(bool unloadAllLoadedObjects = false)
        {
            List<string> keys = new List<string>(AssetBundles.Keys);
            for (int i = 0; i < keys.Count; i++)
            {
                Debug.Log($"卸载AB包{keys[i]}");
                await AssetBundles[keys[i]].UnloadAsync(unloadAllLoadedObjects);
                AssetBundles.Remove(keys[i]);
            }
        }

        /// <summary>
        /// 异步加载AB包清单
        /// </summary>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadAssetBundleManifestAsync()
        {
            if (string.IsNullOrEmpty(AssetConfig.AssetBundleManifestName))
            {
                throw new UnityException("请设置资源配置文件的 AssetBundleManifestName 属性，为所有AB包提供依赖清单！");
            }
            else
            {
                if (AssetBundleManifest == null)
                {
                    yield return LoadAssetBundleAsync(AssetConfig.AssetBundleManifestName, null);
                    if (AssetBundles.ContainsKey(AssetConfig.AssetBundleManifestName))
                    {
                        AssetBundleManifest = AssetBundles[AssetConfig.AssetBundleManifestName].LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                        UnLoadAsset(ResLoadMode.AssetBundle, AssetConfig.AssetBundleManifestName);
                    }
                }
            }
            yield return null;
        }

        /// <summary>
        /// 异步加载AB包（提供进度回调）
        /// </summary>
        /// <param name="assetBundleName">AB包名称</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="isManifest">是否是加载清单</param>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadAssetBundleAsync(string assetBundleName, TAction<float> onLoading)
        {
            if (!AssetBundles.ContainsKey(assetBundleName))
            {
                AssetBundleCreateRequest abRequst = AssetBundle.LoadFromFileAsync(AssetConfig.AssetBundleRootPath + assetBundleName);

                yield return abRequst;

                while (!abRequst.isDone)
                {
                    onLoading?.Invoke(abRequst.progress / 2);
                    yield return null;
                }

                if (abRequst.assetBundle)
                    AssetBundles.Add(assetBundleName, abRequst.assetBundle);
                else
                    throw new UnityException($" 未加载到AB包【{assetBundleName}】！");
            }
            onLoading?.Invoke(1);
        }

        /// <summary>
        /// 异步加载依赖AB包
        /// </summary>
        /// <param name="assetBundleName">AB包名称</param>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadDependenciesAssetBundleAsync(string assetBundleName)
        {
#if UNITY_EDITOR
            if (AssetConfig.RuntimeMode != UpdateMode.Editor)
            {
                yield return LoadAssetBundleManifestAsync();
                if (AssetBundleManifest != null)
                {
                    string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                    foreach (string item in dependencies)
                    {
                        if (AssetBundles.ContainsKey(item))
                            continue;

                        yield return LoadAssetBundleAsync(item, null);
                    }
                }
            }
#else
                yield return LoadAssetBundleManifestAsync();
                if (AssetBundleManifest != null)
                {
                    string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                    foreach (string item in dependencies)
                    {
                        if (AssetBundles.ContainsKey(item))
                        {
                            continue;
                        }

                        yield return LoadAssetBundleAsync(item,null);
                    }
                }
#endif
            yield return null;
        }
    }
}
