using XGame.Framework.Asyncs;
using XGame.Framework.Utils;
using System.Collections.Generic;
using UnityEngine;

namespace XGame.Framework.Asset
{
    /// <summary>
    /// AssetBundle的加载方式，需要先实现Bundle的Manifest管理
    /// </summary>
    internal partial class AssetBundleLoader : BaseLoader, IAssetLoader
    {
        private IAssetBundleManifest _bundleManifest;
        private IAssetReferencesManifest _referenceManifest;
        private ISceneInfosManifest _sceneInfosManifest;
        /// <summary>
        /// 已加载的Bundle仓库
        /// </summary>
        private BundleRepository Repository { set; get; }
        public bool IsBundleEncrypt { set; get; }

        public AssetBundleLoader(IAssetBundleManifest bundleManifest, IAssetReferencesManifest referenceManifest, ISceneInfosManifest sceneInfosManifest)
        {
            Repository = new BundleRepository();

            _bundleManifest = bundleManifest;
            _referenceManifest = referenceManifest;
            _sceneInfosManifest = sceneInfosManifest;
        }
        public IAssetAsync LoadAsync<TResult>(string addressableName)
        {
            if (!TryGetBundleInfo(addressableName, out string bundleName, out string[] dependencies))
            { //找不到bundle信息
                AssetsLog.Error($"Can't find bundleInfo. Name:{addressableName}");
                return null;
            }
            var assetFromCacheAsync = CreateAssetFromCacheAsync(addressableName);
            if (assetFromCacheAsync != null)
            { //从Repository拿出来的asset，可以直接结束该异步
                assetFromCacheAsync.Start();
                return assetFromCacheAsync;
            }
            if (AsyncPool.GetAssetAsync(addressableName) is AssetLoadFromBundleAsync assetLoadAsync)
            {
                //Asset 正在加载
                return assetLoadAsync;
            }
            assetLoadAsync = new AssetLoadFromBundleAsync
            {
                AddressableName = addressableName,
                BundleName = bundleName,
                IsSprite = typeof(TResult) == typeof(Sprite),
                GetBundleFromCache = GetAssetBundleByName,
            };

            List<IAssetAsync> asyncs = new List<IAssetAsync>();
            if (dependencies != null)
            {//收集依赖的bundle Async
                foreach (var dep in dependencies)
                {
                    var async = CreateBundleAsync(dep);
                    if (async != null)
                    {
                        asyncs.Add(async);
                    }
                }
            }
            var bundleAsync = CreateBundleAsync(bundleName);
            if (bundleAsync != null)
            { //自己的bundle Async
                asyncs.Add(bundleAsync);
            }
            if (asyncs.Count > 0)
            {
                AsyncGroup group = new AsyncGroup();
                foreach (var async in asyncs)
                {
                    group.AddAsync(async);
                }
                //先赋值
                assetLoadAsync.BundleAsyncGroup = group;
                //再将Async加进Pool
                foreach (var async in asyncs)
                {
                    AsyncPool.AddAsync(async);
                }
                group.End();
                asyncs.Clear();

            }
            assetLoadAsync.On(OnAssetCompleted);
            AsyncPool.AddAsync(assetLoadAsync);
            return assetLoadAsync;
        }

        public object LoadSync<TResult>(string addressableName)
        {
            if (!TryGetBundleInfo(addressableName, out string bundleName, out string[] dependencies))
            { //找不到bundle信息
                AssetsLog.Error($"Can't find bundleInfo. Name:{addressableName}");
                return null;
            }
            //先从Repository查找缓存的Asset
            if (Repository.ContainsUnusedAsset(addressableName))
            {
                return Repository.PopUnusedAsset(addressableName);
            }

            if (AsyncPool.ContainsAssetAsync(addressableName))
            {//该Asset正在加载使用异步加载
                AssetsLog.Info($"{addressableName} is loading asynchronously, but load synchronously again.");
            }
            //先加载依赖的bundle
            if (dependencies != null)
            {
                foreach (var dep in dependencies)
                {
                    LoadBundleSync(dep);
                }
            }
            //加载bundle
            var assetType = typeof(TResult);
            var isSprite = assetType == typeof(Sprite);
            var bundle = LoadBundleSync(bundleName);
            var asset = isSprite ? bundle?.LoadAsset(addressableName, assetType) : bundle?.LoadAsset(addressableName);
            if (asset != null)
                Repository.RegistAssetHash(asset.GetInstanceID(), addressableName);
            return asset;
        }

        public IAssetAsync LoadSceneBundleAsync(string sceneName)
        {
            //获取bundle信息
            if (!TryGetSceneBundleInfo(sceneName, out string bundleName, out string[] dependencies))
            { //找不到bundle信息
                AssetsLog.Error($"Can't find bundleInfo. Name:{sceneName}");
                return null;
            }
            if (AsyncPool.GetAssetAsync(sceneName) is SceneLoadAsync sceneLoadAsync)
            {
                //AssetBundle 正在加载
                return sceneLoadAsync;
            }

            sceneLoadAsync = new SceneLoadAsync
            {
                AddressableName = sceneName,
            };
            List<IAssetAsync> asyncs = new List<IAssetAsync>();
            if (dependencies != null)
            {//收集依赖的bundle Async
                foreach (var dep in dependencies)
                {
                    var async = CreateBundleAsync(dep);
                    if (async != null)
                    {
                        asyncs.Add(async);
                    }
                }
            }
            var bundleAsync = CreateBundleAsync(bundleName);
            if (bundleAsync != null)
            { //自己的bundle Async
                asyncs.Add(bundleAsync);
            }
            if (asyncs.Count > 0)
            {
                AsyncGroup group = new AsyncGroup();
                foreach (var async in asyncs)
                {
                    group.AddAsync(async);
                }
                //先赋值
                sceneLoadAsync.BundleAsyncGroup = group;
                //再将Async加进Pool
                foreach (var async in asyncs)
                {
                    AsyncPool.AddAsync(async);
                }
                group.End();
                asyncs.Clear();
            }
            sceneLoadAsync.On(OnSceneComplete);
            AsyncPool.AddAsync(sceneLoadAsync);
            return sceneLoadAsync;
        }

        public void UnLoadSceneBundle(string sceneName)
        {
            if (TryGetSceneBundleInfo(sceneName, out string bundleName, out string[] dependencies))
            {
                //更新引用计数
                Repository.ReleaseBundle(bundleName);
                if (dependencies != null)
                {
                    foreach (var dep in dependencies)
                    {
                        Repository.ReleaseBundle(dep);
                    }
                }
            }
        }


        AssetBundle LoadBundleSync(string bundleName)
        {
            AssetBundle bundle;

            var bundleAsset = Repository.GetBundle(bundleName);
            if (bundleAsset != null)
            {
                bundle = bundleAsset.Clone() as AssetBundle;
            }
            else
            {
                if (AsyncPool.ContainsAssetAsync(bundleName))
                {//Bundle正在加载使用异步加载
                    AssetsLog.Info($"{bundleName} is loading asynchronously, but load synchronously again.");
                }
                var path = FileUtils.GetAssetBundlePath(bundleName);
                var offset = GetBundleOffest(bundleName);
                bundle = LoadFromFile(path, offset);
                bundleAsset = AssetCreator.Create(bundleName, bundle) as BundleAsset;
                if (bundleAsset != null)
                {
                    bundleAsset.Retain();
                    Repository.AddBundle(bundleAsset);
                }
                else
                {
                    AssetsLog.Warn("Asset Error: Load Bundle Failed. bundle:{0}", bundleName);
                }
            }
            return bundle;
        }
        private uint GetBundleOffest(string bundleName)
        {
#if UNITY_EDITOR
            if (!IsBundleEncrypt)
            {
                return 0;
            }
#endif
            return _bundleManifest.GetBundleOffest(bundleName);
        }
        /// <summary>
        /// AssetBundle.LoadFromFile的包装
        /// </summary>
        /// <param name="path"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        private AssetBundle LoadFromFile(string path, ulong offset)
        {
            var bundle = offset > 0 ? AssetBundle.LoadFromFile(path, 0, offset) : AssetBundle.LoadFromFile(path);
            return bundle;
        }
        bool TryGetBundleInfo(string addressableName, out string bundleName, out string[] dependencies)
        {
            if (_bundleManifest == null || string.IsNullOrEmpty(addressableName))
            {
                bundleName = string.Empty;
                dependencies = null;
                return false;
            }
            bundleName = _bundleManifest.GetBundleNameByAssetName(addressableName);
            dependencies = _bundleManifest.GetDependenciesByBundleName(bundleName);
            return !string.IsNullOrEmpty(bundleName);
        }
        bool TryGetSceneBundleInfo(string sceneName, out string bundleName, out string[] dependencies)
        {
            var assetName = _sceneInfosManifest?.GetSceneAssetName(sceneName) ?? string.Empty;
            return TryGetBundleInfo(assetName, out bundleName, out dependencies);
        }
        /// <summary>
        /// 检查某个资源是否被引用着
        /// </summary>
        /// <param name="addressableName"></param>
        /// <returns></returns>
        bool IsReferencing(string addressableName)
        {
            if (AsyncPool.ContainsAssetAsync(addressableName))
            {//该资源正在加载中
                return true;
            }
            var references = _referenceManifest.GetReferencesByAssetName(addressableName);
            if (references == null)
                return false;
            foreach (var reference in references)
            {
                if (AsyncPool.ContainsAssetAsync(reference))
                { //加载中
                    return true;
                }
                if (Repository.ContainsAsset(reference) && Repository.ContainsUnusedAsset(reference) == false)
                { //已加载，并且不在回收缓存里
                    return true;
                }
            }
            return false;
        }

        void OnUnloadAsset(string addressableName)
        {
            if (!TryGetBundleInfo(addressableName, out string bundleName, out string[] dependencies))
            { //找不到bundle信息
                return;
            }
            Repository.ReleaseBundle(bundleName);
            if (dependencies != null)
            {
                foreach (var dependency in dependencies)
                {
                    Repository.ReleaseBundle(dependency);
                }
            }
        }
        /// <summary>
        /// 卸载资源
        /// 该方法只会将Asset回收进Repository缓存
        /// </summary>
        /// <param name="addressableName"></param>
        /// <param name="source"></param>
        public void Unload(string addressableName, object source)
        {
            if (source is Object obj)
            {
                var instanceId = obj.GetInstanceID();

                if (string.IsNullOrEmpty(Repository.GetAssetNameByHash(instanceId)))
                {
                    AssetsLog.Warn($"{instanceId} is unregistered. Name:{addressableName}");
                }
                Repository.PushUnusedAsset(obj);

            }
        }
        /// <summary>
        /// 卸载卸载所有没有使用的资源
        /// </summary>
        public void UnloadUnusedAssets()
        {
            Repository.UnloadUnusedAssets(IsReferencing, OnUnloadAsset);
        }


        protected override void OnDispose()
        {
            Repository.Dispose();
            (_bundleManifest as System.IDisposable)?.Dispose();
            (_referenceManifest as System.IDisposable)?.Dispose();
            (_sceneInfosManifest as System.IDisposable)?.Dispose();
            _bundleManifest = null;
            _referenceManifest = null;
            _sceneInfosManifest = null;
        }

    }
}