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

namespace XGame.Framework.Asset
{

    /// <summary>
    /// 游戏加载模块
    /// 运行时允许同时存在多个
    /// </summary>
    public class AssetModule : IAssetModule, IAssetEventListener, System.IDisposable
    {
        /// <summary>
        /// 已加载的资源
        /// </summary>
        private Dictionary<long, AssetInstance> _loadedAssetMap = new Dictionary<long, AssetInstance>();
        /// <summary>
        /// key: IAssetLoadAsync<TResult>
        /// value: assetTag
        /// </summary>
        private List<IAsync> _loadingAsyncs = new List<IAsync>();
        /// <summary>
        /// 预加载的资源名字
        /// </summary>
        private HashSet<string> _preloadedAssetNames = new HashSet<string>();

        public string ConvertAddressableName(string addressableName)
        {
            return AssetManager.ConvertAddressableName(addressableName);
        }

        public IAssetLoadAsync<TResult> LoadAsync<TResult>(string addressableName)
        {
            var loadAsync = AssetManager.LoadAsync<TResult>(addressableName);
            AddAssetLoadAsync(loadAsync);
            return loadAsync;
        }
        ///// <summary>
        ///// 不推荐业务使用同步加载，先屏蔽
        ///// </summary>
        ///// <typeparam name="TResult"></typeparam>
        ///// <param name="addressableName"></param>
        ///// <returns></returns>
        //public TResult LoadSync<TResult>(string addressableName)
        //{
        //    var result = AssetManager.LoadSync<TResult>(addressableName);
        //    if (result != null)
        //    {
        //        //储存
        //        OnGetResult(ConvertAddressableName(addressableName), result);
        //    }
        //    return result;
        //}
        public void Recycle(object obj)
        {
            if (obj is Component component && component != null)
            {
                obj = component.gameObject;
            }
            if (obj == null) return;
            InternalRecycle(obj);
            AssetManager.Recycle(obj);
        }

        internal void Recycle(GameObject obj, bool destroyImmediate)
        {
            if (obj == null) return;
            InternalRecycle(obj);
            AssetManager.Recycle(new RecycleObject(obj)
            {
                destroyImmediate = destroyImmediate,
            });
        }

        //public void StopAsync<TResult>(IAssetLoadAsync<TResult> loadAsync)
        //{
        //    loadAsync.RemoveAll();
        //}
        public void StopAllAsyncs()
        {
            ClearLoadingAsyncs();
        }

        public void UnloadUnusedAssets()
        {
            AssetManager.UnloadUnusedAssets();
        }

        public IAsync LoadSceneBundleAsync(string sceneName)
        {
            var async = AssetManager.LoadSceneBundle(sceneName);
            return async;
        }

        public void UnLoadSceneBundle(string sceneName)
        {
            AssetManager.UnLoadSceneBundle(sceneName);
        }
        #region 预加载
        public IAssetPreloadAsync PreloadAsync<TResult>(string addressableName)
        {
            var loadAsync = LoadAsync<TResult>(addressableName);
            AddAssetLoadAsync(loadAsync);
            loadAsync?.On(_ =>
            {
                var assetName = loadAsync.AddressableName;
                AssetManager.SetPreloadTag(assetName, true);
                _preloadedAssetNames.Add(assetName);
            });
            return loadAsync as IAssetPreloadAsync;
        }

        public void UnloadPreloadAsset(string addressableName)
        {
            if (_preloadedAssetNames.Count == 0)
                return;
            addressableName = ConvertAddressableName(addressableName);
            if (_preloadedAssetNames.Contains(addressableName))
            {
                _preloadedAssetNames.Remove(addressableName);
                AssetManager.SetPreloadTag(addressableName, false);
            }
        }
        #endregion
        void System.IDisposable.Dispose()
        {
            ClearLoadingAsyncs();
            foreach (var assetName in _preloadedAssetNames)
            {
                AssetManager.SetPreloadTag(assetName, false);
            }
            _preloadedAssetNames.Clear();

            foreach (var it in _loadedAssetMap)
            {
                //var component = it.Value.Asset != null && it.Value.Asset is UnityEngine.Component;
                //GameObject被销毁后,System.object对象判断还是非空，只能通过UnityEngine.Object来判断非空
                bool isNull = it.Value.Asset == null || (it.Value.Asset as UnityEngine.Object == null);
                if (isNull)
                { //资源被业务销毁
                    AssetsLog.Warn($"AssetModule try to recycle asset. But asset has been destroyed. {it.Value}");
                }
                if (isNull || it.Value.Reference > 1)
                {
                    AssetManager.Recycle(new RecycleObject()
                    {
                        asset = isNull ? null : it.Value.Asset,
                        hashcode = it.Key,
                        reference = it.Value.Reference,
                        destroyImmediate = false,
                    });
                }
                else if (it.Value.Reference < 1)
                {
                    AssetsLog.Error($"Asset referece count error. {it.Value}");
                }
                else
                {
                    AssetManager.Recycle(it.Value.Asset);
                }
                //for (int i = 0; i < it.Value.Reference; i++)
                //{//该步骤待优化
                //    assetMgr.Recycle(it.Value.Asset);
                //}
            }
            _loadedAssetMap.Clear();

        }

        void InternalRecycle(object obj)
        {
            if (obj != null && _loadedAssetMap != null)
            {
                var hashCode = obj.GetLongHashCode();
                if (_loadedAssetMap.TryGetValue(hashCode, out AssetInstance asset))
                {
                    if (asset.Release())
                    {
                        _loadedAssetMap.Remove(hashCode);
                    }
                }
            }
        }

        #region 加载中的异步
        private void AddAssetLoadAsync(IAsync loadAsync)
        {
            //AssetsLog.Debug($"AddAssetLoadAsync:{loadAsync}");
            if (loadAsync is IAssetEventListenerSetter setter)
            {
                setter.AssetEventListener = this;
            }
            if (loadAsync == null || loadAsync.IsCompleted)
                return;
            _loadingAsyncs.Add(loadAsync);
        }
        /// <summary>
        /// 清空加载中的异步加载回调
        /// </summary>
        private void ClearLoadingAsyncs()
        {
            if (_loadingAsyncs.Count == 0)
                return;
            for(var i = _loadingAsyncs.Count - 1; i >= 0; i--)
            {
                _loadingAsyncs[i].RemoveAll();
            }
            _loadingAsyncs.Clear();
        }
        #endregion

        #region IAssetEventListener 实现

        void IAssetEventListener.OnGetResult(string addressableName, object result)
        {
            Assert.IsNotNull(result);
            if (_preloadedAssetNames.Contains(addressableName))
            { // 删除预加载记录，AssetManager内部会自动清除预加载标记
                _preloadedAssetNames.Remove(addressableName);
            }
            //储存对象
            if (result is Component component && component != null)
            { //Component脚本改为存储GameObject的hash
                result = component.gameObject;
            }
            var hashCode = result.GetLongHashCode();
            if (_loadedAssetMap.TryGetValue(hashCode, out AssetInstance asset))
            {
                asset.Retain();
            }
            else
            {
                asset = new AssetInstance(result, addressableName);
                _loadedAssetMap.Add(hashCode, asset);
            }
        }

        void IAssetEventListener.ReleaseAsync(IAsync loadAsync)
        {
            //AssetsLog.Debug($"IAssetResultHandle.ReleaseAsync:{loadAsync}");
            _loadingAsyncs.Remove(loadAsync);
        }
        #endregion

        #region 注册加载资源的Component信息，主要是预防业务将动态加载的GameObject下依赖的贴图等资源Recycle
        internal bool IsRegister(long assetHash, long parentHash)
        {
            if (_loadedAssetMap.TryGetValue(assetHash, out var asset))
            {
                return asset.IsContainParent(parentHash);
            }
            return false;
        }

        internal bool Register(long assetHash, long parentHash)
        {
            if (_loadedAssetMap.TryGetValue(assetHash, out var asset))
            {
                return asset.AddParent(parentHash);
            }
            return false;
        }

        public bool UnRegister(long assetHash, long parentHash)
        {
            if (_loadedAssetMap.TryGetValue(assetHash, out var asset))
            {
                return asset.RemoveParent(parentHash);
            }
            return false;
        }
        #endregion

    }
}

