using System.Collections.Generic;
using UnityEngine;

namespace XGame.Framework.Asset
{
    //internal partial class AssetBundleLoader
    //{
    //}
    /// <summary>
    /// AssetBundleLoader已加载的Bundle仓库
    /// </summary>
    internal class BundleRepository
    {
        /// <summary>
        /// 已加载的Bundle
        /// key:bundleName
        /// </summary>
        private Dictionary<string, BundleAsset> BundleMap { set; get; }
        /// <summary>
        /// 已加载的Asset的InstanceId
        /// UnityEngine.Object用InstanceId代替HashCode，hash值可能出现重复
        /// </summary>
        private Dictionary<int, string> UsingHashCodeMap { set; get; }
        /// <summary>
        /// 等待卸载的Asset
        /// </summary>
        private Dictionary<string, Object> UnusedAssetMap { set; get; }

        public BundleRepository()
        {
            BundleMap = new Dictionary<string, BundleAsset>();
            UsingHashCodeMap = new Dictionary<int, string>();
            UnusedAssetMap = new Dictionary<string, Object>();
        }

        #region Bundle

        public bool ContainsBundle(string bundleName)
        {
            return BundleMap.ContainsKey(bundleName);
        }

        public void AddBundle(BundleAsset bundleAsset)
        {
            if (bundleAsset == null)
            {
                AssetsLog.Warn("Bundle Repository Add Invalid Bundle.");
                return;
            }
            var bundleName = bundleAsset.AddressableName;
            if (BundleMap.ContainsKey(bundleName))
            {
                AssetsLog.Warn($"Add Bundle Repeat. Name:{bundleName}");
                return;
            }
            BundleMap.Add(bundleName, bundleAsset);
        }

        public BundleAsset GetBundle(string bundleName)
        {
            BundleMap.TryGetValue(bundleName, out var bundle);
            return bundle;
        }

        public void RemoveBundle(string bundleName)
        {
            BundleMap.Remove(bundleName);
        }

        public void RetainBundle(string bundleName)
        {
            if (BundleMap.TryGetValue(bundleName, out var bundle))
            {
                bundle.Retain();
            }
        }

        public void ReleaseBundle(string bundleName)
        {
            if (BundleMap.TryGetValue(bundleName, out var bundle))
            {
                bundle.Release();
            }
        }
        #endregion

        #region AssetHashCode
        public bool RegistAssetHash(int hashCode, string addressableName)
        {
            if (!UsingHashCodeMap.ContainsKey(hashCode))
            {
                UsingHashCodeMap.Add(hashCode, addressableName);
                return true;
            }
            AssetsLog.Warn($"Bundle asset hash repeat. Hash:{hashCode} Last:{UsingHashCodeMap[hashCode]} Next:{addressableName}");
            return false;
        }
        // ReSharper disable once IdentifierTypo
        public void UnRegistAssetHash(int hashCode)
        {
            if (UsingHashCodeMap.ContainsKey(hashCode))
            {
                UsingHashCodeMap.Remove(hashCode);
            }
        }

        public string GetAssetNameByHash(int hashCode)
        {
            UsingHashCodeMap.TryGetValue(hashCode, out var addressableName);
            return addressableName;
        }

        public bool ContainsAssetHash(int hashCode)
        {
            return UsingHashCodeMap.ContainsKey(hashCode);
        }

        public bool ContainsAsset(string addressableName)
        {
            return UsingHashCodeMap.ContainsValue(addressableName);
        }
        #endregion

        #region UnusedAsset

        public bool ContainsUnusedAsset(string addressableName)
        {
            UnusedAssetMap.TryGetValue(addressableName, out var asset);
            return asset != null;
        }
        public void PushUnusedAsset(Object source)
        {
            if (source == null) return;
            var instanceId = source.GetInstanceID();
            var addressableName = GetAssetNameByHash(instanceId);
            if (string.IsNullOrEmpty(addressableName))
            {
                AssetsLog.Debug($"Object is unregistered. Hash:{instanceId} Name:{source.name}");
                return;
            }
            if (UnusedAssetMap.ContainsKey(addressableName))
            {
                AssetsLog.Debug($"Object had in map. But add repeat. Name:{addressableName} Next:{instanceId} Last:{UnusedAssetMap[addressableName]?.GetInstanceID()}");
                return;
            }
            UnusedAssetMap.Add(addressableName, source);
        }

        public Object PopUnusedAsset(string addressableName)
        {
            if (UnusedAssetMap.TryGetValue(addressableName, out var source))
            {
                UnusedAssetMap.Remove(addressableName);
            }
            return source;
        }

        public void UnloadUnusedAssets(System.Func<string, bool> isReferencing, System.Action<string> onUnloadAsset)
        {
            //释放未被引用的Asset
            var unloadList = new List<string>();
            foreach (var item in UnusedAssetMap)
            {
                var addressableName = item.Key;
                if (!isReferencing(item.Key))
                {
                    unloadList.Add(addressableName);
                }
            }
            foreach (var addressableName in unloadList)
            {
                var asset = UnusedAssetMap[addressableName];
                var instanceId = asset?.GetInstanceID() ?? 0;
                UnRegistAssetHash(instanceId);
                UnusedAssetMap.Remove(addressableName);
                onUnloadAsset?.Invoke(addressableName);
                asset.Unload();
                //if (asset is Component || asset is GameObject || asset is AssetBundle)
                //{
                //    //非独立资源
                //    //Object.Destroy(asset);
                //}
                //else
                //{
                //    //独立资源才能使用Resources.UnloadAsset
                //    Resources.UnloadAsset(asset);
                //}
                AssetsLog.Debug($"Resources UnloadAsset: {addressableName}");
            }
            unloadList.Clear();
            //释放引用计数为零的Bundle
            foreach (var bundleItem in BundleMap)
            {
                if (bundleItem.Value.Reference <= 0)
                {
                    unloadList.Add(bundleItem.Key);
                }
            }
            foreach (var bundlePath in unloadList)
            {
                var bundleAsset = BundleMap[bundlePath];
                BundleMap.Remove(bundlePath);
                bundleAsset.Dispose();
                AssetsLog.Debug($"AssetBundle Unload: {bundlePath}");
            }
            unloadList.Clear();
            Resources.UnloadUnusedAssets();
        }
        #endregion

        public void Dispose()
        {
            foreach (var asset in UnusedAssetMap)
            {
                asset.Value.Unload();
                //Resources.UnloadAsset(asset.Value);
            }
            UnusedAssetMap.Clear();
            foreach (var it in BundleMap)
            {
                it.Value.Dispose();
            }
            BundleMap.Clear();
            UsingHashCodeMap.Clear();
        }
    }

}
