using XGame.Framework.i18n;
using XGame.Framework.Quality;
using XGame.Framework.Asset.Addressable;
using XGame.Framework.Asset.Addressable.Data;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace XGame.Editor.Asset
{
    internal class AddressableInfoManifest
    {

        private AddressableInfosSo manifest;

        private List<AssetInfo> assetInfos;
        /// <summary>
        /// 多语言资源配置，每次收集资源信息都重置
        /// </summary>
        private Dictionary<long, List<AddressableI18nInfo.I18nData>> i18nInfosMap;
        /// <summary>
        /// Lod资源配置，每次收集资源信息都重置
        /// </summary>
        private Dictionary<long, List<AddressableLodInfo.LodData>> lodInfosMap;
        /// <summary>
        /// 等待二次检测的Asset资源
        /// 由于LOD资源在同一目录下，可能出现lod资源比Origin资源更早记录的情况
        /// 第一次检测时找不到就先记录下来，等所有资源遍历结束再做一次检测
        /// </summary>
        private List<AssetInfo> verifingLodAssets;
        public AddressableClassify ClassifyOperation
        {
            get;
            private set;
        }

        public AddressableInfoManifest(AddressableInfosSo manifest, AddressableClassify classify)
        {
            ClassifyOperation = classify;
            this.manifest = manifest;
            assetInfos = manifest?.GetAssetInfos();

        }
        public bool AddAssetInfo(AssetInfo assetInfo)
        {
            if (manifest == null) return false;
            if (assetInfos == null)
            {
                assetInfos = new List<AssetInfo>();
            }
            var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(assetInfo.assetGUID) || a.addressableName.Equals(assetInfo.addressableName) || a.addressableId == assetInfo.addressableId);
            if (index >= 0)
            {
                Debug.LogError($"Asset Manifest AddAssetInfo Error. Asset Repeat Last:{assetInfos[index]} Next:{assetInfo}");
                return false;
            }
            assetInfos.Add(assetInfo);
            OnAddAssetInfo(assetInfo);
            return true;
        }
        public void RemoveAssetInfoByName(string addressableName)
        {
            if (assetInfos != null)
            {
                var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
                if (index >= 0)
                {
                    assetInfos.RemoveAt(index);
                }
            }
        }
        public bool TryGetAssetInfoByName(string addressableName, out AssetInfo assetInfo)
        {
            if (assetInfos != null)
            {
                var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
                if (index >= 0)
                {
                    assetInfo = assetInfos[index];
                    return true;
                }
            }
            assetInfo = default;
            return false;
        }
        public bool TryGetAssetInfoByGUID(string guid, out AssetInfo assetInfo)
        {
            if (assetInfos != null)
            {
                var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(guid));
                if (index >= 0)
                {
                    assetInfo = assetInfos[index];
                    return true;
                }
            }
            assetInfo = default;
            return false;
        }
        public bool ContainsName(string addressableName)
        {
            if (assetInfos != null)
            {
                var index = assetInfos.FindIndex((a) => a.addressableName.Equals(addressableName));
                return index >= 0;
            }
            return false;
        }
        public bool ContainsGUID(string guid)
        {
            if (assetInfos != null)
            {
                var index = assetInfos.FindIndex((a) => a.assetGUID.Equals(guid));
                return index >= 0;
            }
            return false;
        }
        public void Save()
        {
            if (manifest != null)
            {
                VerifyLodAssetsSecond();
                SaveLodInfos();
                SaveI18nInfos();
                manifest.SetAssetInfos(assetInfos != null && assetInfos.Count > 0 ? assetInfos.ToArray() : null);
            }
        }

        public void DisposeInvalidAssets(System.Func<AssetInfo, bool> moveAssetEvent)
        {
            if (assetInfos != null)
            {
                for (int i = assetInfos.Count - 1; i >= 0; i--)
                {
                    var assetInfo = assetInfos[i];
                    if (!AddressableHelper.IsValidPath(assetInfo.GetAssetPath(), ClassifyOperation))
                    {
                        Debug.LogWarning($"Invalid Asset:{assetInfo}");
                        if (moveAssetEvent != null && moveAssetEvent.Invoke(assetInfo))
                        {
                            assetInfos.RemoveAt(i);
                        }
                    }
                }
            }
        }
        public void CollectAddressableAssets(ref IDictionary<string, AddressableClassify> addressableNameMap,
            ref IDictionary<long, AddressableClassify> addressableIdMap,
            ref IDictionary<string, AddressableClassify> guidMap)
        {
            if (assetInfos != null)
            {
                for (int i = 0; i < assetInfos.Count; i++)
                {
                    var assetInfo = assetInfos[i];
                    //刷新relativePath和assetType
                    bool isInResources = AddressableHelper.IsInResources(assetInfo.GetAssetPath(), out assetInfo.relativePath);
                    //assetInfo.assetType = isInResources ? AddressableAssetType.Resources : AddressableAssetType.Default;
                    assetInfos[i] = assetInfo;
                    //
                    if (addressableNameMap.ContainsKey(assetInfo.addressableName))
                    {
                        Debug.LogError($"Addressable Name Repeat. {assetInfo}");
                    }
                    else
                    {
                        addressableNameMap.Add(assetInfo.addressableName, ClassifyOperation);
                    }
                    if (addressableIdMap.ContainsKey(assetInfo.addressableId))
                    {
                        Debug.LogError($"Addressable ID Repeat. {assetInfo}");
                    }
                    else
                    {
                        addressableIdMap.Add(assetInfo.addressableId, ClassifyOperation);
                    }
                    if (guidMap.ContainsKey(assetInfo.assetGUID))
                    {
                        Debug.LogError($"Addressable GUID Repeat. {assetInfo}");
                    }
                    else
                    {
                        guidMap.Add(assetInfo.assetGUID, ClassifyOperation);
                    }
                }
            }
        }

        //private bool IsValidPath(string assetPath)
        //{
        //    if (string.IsNullOrEmpty(assetPath))
        //    {
        //        return false;
        //    }
        //    switch (ClassifyOperation)
        //    {
        //        case AddressableClassifyOperation.BuiltIn:
        //            if (assetPath.StartsWith(PathDefine.XGamePackageRelative))
        //            {
        //                //在Package Framework目录下
        //                return true;
        //            }
        //            break;
        //        case AddressableClassifyOperation.ResStatic:
        //            if (assetPath.StartsWith(PathDefine.ResStaticToAddressable))
        //            {
        //                //在ResStatic目录下
        //                return true;
        //            }
        //            break;
        //        default:
        //            if (assetPath.StartsWith(PathDefine.ResAddressableRelative))
        //            {
        //                //在Res/Addressable目录下
        //                return true;
        //            }
        //            if (assetPath.StartsWith(PathDefine.ResourcesRelative))
        //            {
        //                //在Resources目录下
        //                return true;
        //            }
        //            break;
        //    }
        //    return false;
        //}

        private void OnAddAssetInfo(AssetInfo assetInfo)
        {
            if (ClassifyOperation == AddressableClassify.Product)
            {
                //项目资源才需要检测LOD和多语言
                VerifyLodAsset(assetInfo, true);
                VerifyI18nAsset(assetInfo);
            }
        }
        public void RefreshAssetInfoByGUID(string assetGUID)
        {
            if (ClassifyOperation == AddressableClassify.Product)
            {
                if (TryGetAssetInfoByGUID(assetGUID, out var assetInfo))
                {
                    OnAddAssetInfo(assetInfo);
                }
            }
        }
        #region i18n
        private void VerifyI18nAsset(AssetInfo assetInfo)
        {
            var assetPath = assetInfo.GetAssetPath();
            var langFlag = AddressableHelper.GetLanguageTypeByAssetPath(assetPath);
            if (langFlag != LanguageType.NONE)
            { // 是多语言
                var originPath = assetPath.Replace($"/{PathDefine.I18nName}/{langFlag.ToName()}", "");
                var originGUID = AssetDatabase.AssetPathToGUID(originPath);
                if (string.IsNullOrEmpty(originGUID) == false && TryGetAssetInfoByGUID(originGUID, out var originAsset))
                {
                    AddI18nData(originAsset.addressableId, assetInfo.addressableId, langFlag);
                }
                else
                {
                    Debug.LogError($"多语言资源错误，找不到对应的原始资源. {assetInfo} OriginPath:{originPath}");
                }
            }
        }
        private bool AddI18nData(long originId, long i18nId, LanguageType langFlag)
        {
            if (i18nInfosMap == null)
            {
                i18nInfosMap = new Dictionary<long, List<AddressableI18nInfo.I18nData>>();
            }
            if (!i18nInfosMap.TryGetValue(originId, out var lstData))
            {
                lstData = new List<AddressableI18nInfo.I18nData>();
                i18nInfosMap.Add(originId, lstData);
            }
            var index = lstData.FindIndex((a) => a.langFlag == langFlag);
            if (index >= 0)
            {
                var data = lstData[index];
                data.addressableId = i18nId;
                lstData[index] = data;
            }
            else
            {
                lstData.Add(new AddressableI18nInfo.I18nData()
                {
                    addressableId = i18nId,
                    langFlag = langFlag,
                });
            }
            return true;
        }
        private void SaveI18nInfos()
        {
            if (i18nInfosMap != null && i18nInfosMap.Count > 0)
            {
                AddressableI18nInfo[] i18nInfos = new AddressableI18nInfo[i18nInfosMap.Count];
                var index = 0;
                foreach (var pair in i18nInfosMap)
                {
                    var info = new AddressableI18nInfo()
                    {
                        addressableId = pair.Key,
                        i18nDatas = pair.Value.ToArray()
                    };
                    i18nInfos[index++] = info;
                }
                manifest.SetAddressableI18nInfos(i18nInfos, false);
            }
            else
            {
                manifest.SetAddressableI18nInfos(null, false);
            }
        }
        #endregion

        #region LOD
        /// <summary>
        /// 二次检验lod资源
        /// </summary>
        private void VerifyLodAssetsSecond()
        {
            if (verifingLodAssets != null)
            {
                foreach (var assetInfo in verifingLodAssets)
                {
                    VerifyLodAsset(assetInfo, false);
                }
                verifingLodAssets.Clear();
            }
        }
        private void VerifyLodAsset(AssetInfo assetInfo, bool isFirst)
        {
            var assetPath = assetInfo.GetAssetPath();
            if (IsLodAssetPath(assetPath, PathDefine.LodHigh, out var originPath, out var quality) ||
                IsLodAssetPath(assetPath, PathDefine.LodLow, out originPath, out quality))
            {
                Debug.Log($"LOD资源. {assetInfo} OriginPath:{originPath}");
                var originGUID = AssetDatabase.AssetPathToGUID(originPath);
                if (string.IsNullOrEmpty(originGUID) == false && TryGetAssetInfoByGUID(originGUID, out var originAsset))
                {
                    AddLodData(originAsset.addressableId, assetInfo.addressableId, quality);
                }
                else if (isFirst)
                {
                    //保存起来，等待二次校验
                    AddVerifingLodAsset(assetInfo);
                }
                else
                {
                    Debug.LogError($"LOD资源错误，找不到对应的原始资源. {assetInfo} OriginPath:{originPath}");
                }
            }
        }

        private void AddVerifingLodAsset(AssetInfo assetInfo)
        {
            if (verifingLodAssets == null)
            {
                verifingLodAssets = new List<AssetInfo>();
            }
            verifingLodAssets.Add(assetInfo);
        }

        private bool IsLodAssetPath(string assetPath, string lodTag, out string originPath, out XGame.Framework.Quality.XQualityLevel quality)
        {
            var fileName = System.IO.Path.GetFileNameWithoutExtension(assetPath);
            if (fileName != null && fileName.ToLower().EndsWith(lodTag))
            {
                var originName = fileName.Substring(0, fileName.Length - lodTag.Length);
                originPath = assetPath.Replace(fileName, originName);
                quality = LodTagToQualityLevel(lodTag);
                return true;
            }
            originPath = string.Empty;
            quality = XGame.Framework.Quality.XQualityLevel.UnKnow;
            return false;
        }

        private XGame.Framework.Quality.XQualityLevel LodTagToQualityLevel(string lodTag)
        {
            if (lodTag.Equals(PathDefine.LodHigh))
            {
                return XGame.Framework.Quality.XQualityLevel.High;
            }
            else if (lodTag.Equals(PathDefine.LodLow))
            {
                return XGame.Framework.Quality.XQualityLevel.Low;
            }
            return XGame.Framework.Quality.XQualityLevel.UnKnow;
        }

        private bool AddLodData(long originId, long lodId, XGame.Framework.Quality.XQualityLevel quality)
        {
            if (lodInfosMap == null)
            {
                lodInfosMap = new Dictionary<long, List<AddressableLodInfo.LodData>>();
            }
            if (!lodInfosMap.TryGetValue(originId, out var lstData))
            {
                lstData = new List<AddressableLodInfo.LodData>();
                lodInfosMap.Add(originId, lstData);
            }
            var index = lstData.FindIndex(data => data.quality == quality);
            if (index >= 0)
            {
                var data = lstData[index];
                data.addressableId = lodId;
                lstData[index] = data;
            }
            else
            {
                lstData.Add(new AddressableLodInfo.LodData()
                {
                    addressableId = lodId,
                    quality = quality
                });
            }
            return true;
        }
        private void SaveLodInfos()
        {
            if (lodInfosMap != null && lodInfosMap.Count > 0)
            {
                var lodInfos = new AddressableLodInfo[lodInfosMap.Count];
                var index = 0;
                foreach (var pair in lodInfosMap)
                {
                    var info = new AddressableLodInfo()
                    {
                        addressableId = pair.Key,
                        lodDatas = pair.Value.ToArray()
                    };
                    lodInfos[index++] = info;
                }
                manifest.SetAddressableLodInfos(lodInfos, false);
            }
            else
            {
                manifest.SetAddressableLodInfos(null, false);
            }
        }
        #endregion

    }
}