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

namespace XGame.Editor.Build.AssetBundles
{
    /// <summary>
    /// assbundle的上下文
    /// </summary>
    public class BundleContext
    {
        public readonly string ResStaticDir = $"/{PathDefine.ResStaticName}/";
        public readonly string UiPrefabDir = "/UI/Prefabs/";
        public readonly string EditorDir = "/Editor/";
        public readonly string ResourcesDir = "/Resources/";
        public readonly string PrefabExt = ".prefab";
        public readonly string ShaderExt = ".shader";

        /// <summary>
        /// key:guid
        /// value:AssetInfo
        /// </summary>
        private Dictionary<string, AssetInfo> _assetInfoMap;
        /// <summary>
        /// key:assetPath
        /// value:guid
        /// </summary>
        private Dictionary<string, string> _assetPathToGuidMap;

        public Dictionary<long, AssetBundleData> bundleDataMap;
        /// <summary>
        /// key:bundleId
        /// value:dependencies
        /// </summary>
        private Dictionary<long, string[]> _bundleDependenciesMap;

        public HashSet<string> bundleAssetPaths;
        public Dictionary<long, uint[]> dependenciesMap;

        #region Addressable资源
        public string[] assetsRoots;
        /// <summary>
        /// 等待处理的资源
        /// </summary>
        public HashSet<string> waittingAssetPaths;
        #endregion

        /// <summary>
        /// 贴图资源没有依赖，缓存一份贴图路径数据节省一些判断
        /// </summary>
        public HashSet<string> allTexturePaths;

        public AssetBundleNameMode bundleNameMode;
        /// <summary>
        /// 是否合并shader
        /// </summary>
        public bool isMergeShader;
        /// <summary>
        /// ab分组名字
        /// key:assetPath
        /// value:groupName
        /// </summary>
        public Dictionary<string, string> groupNameMap;
        /// <summary>
        /// 排序好的AssetBundle数据
        /// </summary>
        private List<AssetBundleData> _bundleDataLst;
        /// <summary>
        /// 排序好的AssetBundle数据
        /// </summary>
        public List<AssetBundleData> BundleDataLst => _bundleDataLst;

        public BundleContext(Dictionary<string, AssetInfo> assetInfoMap)
        {
            _assetInfoMap = assetInfoMap;
            bundleDataMap = new Dictionary<long, AssetBundleData>();
            _bundleDependenciesMap = new Dictionary<long, string[]>();
            bundleAssetPaths = new HashSet<string>();
            dependenciesMap = new Dictionary<long, uint[]>();

            waittingAssetPaths = new HashSet<string>();
            allTexturePaths = new HashSet<string>();
            groupNameMap = new Dictionary<string, string>();
            Init();
        }
        private void Init()
        {
            _assetPathToGuidMap = new Dictionary<string, string>();
            foreach (var guid in _assetInfoMap.Keys)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                if (!string.IsNullOrEmpty(assetPath))
                {
                    _assetPathToGuidMap.Add(assetPath, guid);
                }
            }
        }
        #region addressable
        public bool IsAddressableAsset(string guid)
        {
            return _assetInfoMap.ContainsKey(guid);
        }
        public bool TryGetAddressableNameByGuid(string guid, out string addressableName)
        {
            if (_assetInfoMap.TryGetValue(guid, out var info))
            {
                addressableName = info.addressableName;
                return true;
            }
            addressableName = string.Empty;
            return false;
        }
        public bool TryGetAddressableNameByPath(string assetPath, out string addressableName)
        {
            if (_assetPathToGuidMap.TryGetValue(assetPath, out var guid))
            {
                addressableName = _assetInfoMap[guid].addressableName;
                return true;
            }
            addressableName = string.Empty;
            return false;
        }
        /// <summary>
        /// key:assetPath
        /// value:addressableName
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> GetAddressableInfoMap()
        {
            var map = new Dictionary<string, string>();
            foreach(var item in _assetPathToGuidMap)
            {
                var assetInfo = _assetInfoMap[item.Value];
                if (!string.IsNullOrEmpty(assetInfo.relativePath))
                {
                    //忽略Resources的资源
                    continue;
                }
                map.Add(item.Key, assetInfo.addressableName);
            }
            return map;
        }
        /// <summary>
        /// 资源路径是否有效
        /// </summary>
        /// <param name="assetPath"></param>
        /// <returns></returns>
        public bool IsValidPath(string assetPath)
        {
            if (string.IsNullOrEmpty(assetPath))
            {
                return false;
            }
            foreach (var root in assetsRoots)
            {
                if (assetPath.StartsWith(root))
                {
                    return true;
                }
            }
            return false;
        }
        #endregion
        /// <summary>
        /// 支持addressable加载
        /// 多语言的单独分文件夹
        /// 根据分组配置划分文件夹
        /// originGuid 用于生成bundleId，一般使用guid
        /// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
        /// </summary>
        /// <param name="assetPaths"></param>
        /// <param name="originGuid"></param>
        public void AddAddressableBundle(string[] assetPaths, string originGuid)
        {
            string groupName;
            var firstAsset = assetPaths[0];
            var langFlag = AddressableHelper.GetLanguageTypeByAssetPath(firstAsset);
            if (langFlag == LanguageType.NONE)
            {
                // 取Group配置
                groupNameMap.TryGetValue(firstAsset, out groupName);
            }
            else
            {
                groupName = langFlag.ToString().ToLower();
            }
            var originName = bundleNameMode == AssetBundleNameMode.Guid ? originGuid : AssetDatabase.GUIDToAssetPath(originGuid);
            var bundleId = Crc32.GetCrc32(originName);
            var count = assetPaths.Length;
            var addressableNames = new string[count];
            var isTextureBundle = true;
            for (var i = 0; i < count; i++)
            {
                var assetPath = assetPaths[i];
                if (isTextureBundle && allTexturePaths.Contains(assetPath) == false)
                { // 有一个资源非贴图
                    isTextureBundle = false;
                }
                if (TryGetAddressableNameByPath(assetPath, out var addressableName) == false)
                {
                    Debug.LogWarning($"Asset can't find addressableName. Path:{assetPath}");
                    addressableName = assetPath;
                }
                addressableNames[i] = addressableName;
                // 移除等待
                waittingAssetPaths.Remove(assetPath);
            }
            var bundle = new AssetBundleData()
            {
                assetBundleName = string.IsNullOrEmpty(groupName) ? bundleId.ToString() : $"{groupName}/{bundleId}",
                assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
                assetNames = assetPaths,
                addressableNames = addressableNames,
                bundleId = bundleId,
                originBundleName = originName,
                isTextureBundle = isTextureBundle,
                bundleType = AssetBundleType.Addressable
            };
            TryAddAssetBundle(ref bundle);
        }
        /// <summary>
        /// 普通的ab包，不支持addressable加载
        /// 全部生成在根目录下，不支持分组
        /// originName 用于生成bundleId，一般使用guid
        /// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
        /// </summary>
        /// <param name="assetPaths"></param>
        /// <param name="originName"></param>
        /// <param name="isDependency"></param>
        /// <returns></returns>
        public AssetBundleData AddRawAssetBundle(string[] assetPaths, string originName, bool isDependency)
        {
            if (bundleNameMode == AssetBundleNameMode.AssetPath && isDependency == false)
            {
                var path = AssetDatabase.GUIDToAssetPath(originName);
                if (string.IsNullOrEmpty(path))
                {
                    Log.Error($"AddRawAssetBundle originName is not guid. originName:{originName}");
                }
                else
                {
                    originName = path;
                }
            }
            var bundleId = Crc32.GetCrc32(originName);
            var bundle = new AssetBundleData()
            {
                assetBundleName = bundleId.ToString(),
                assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
                assetNames = assetPaths,
                addressableNames = assetPaths,
                bundleId = bundleId,
                originBundleName = originName,
                isTextureBundle = IsTexturePaths(assetPaths),
                bundleType = isDependency ? AssetBundleType.Dependency : AssetBundleType.Raw
            };
            TryAddAssetBundle(ref bundle);
            return bundle;
        }
        private bool IsTexturePaths(string[] assetPaths)
        {
            foreach(var assetPath in assetPaths)
            {
                if (allTexturePaths.Contains(assetPath) == false)
                {
                    return false;
                }
            }
            return true;
        }
        private void TryAddAssetBundle(ref AssetBundleData bundle)
        {
            var bundleId = bundle.bundleId;
            bundleAssetPaths.UnionWith(bundle.assetNames);
            if (bundleDataMap.ContainsKey(bundleId))
            {
                var lastName = bundleDataMap[bundleId].originBundleName;
                var nextName = bundle.originBundleName;
                Debug.LogError($"[XBuild] AssetBundleId重复. Id:{bundleId} LastName:{lastName} LastPath:{AssetDatabase.GUIDToAssetPath(lastName)} NextName:{nextName} NextPath:{AssetDatabase.GUIDToAssetPath(nextName)}");
                return;
            }
            bundleDataMap.Add(bundleId, bundle);
        }
        private List<string> tempLst = new List<string>();
        public string[] ToArrayBySort(HashSet<string> assetPaths)
        {
            if (assetPaths.Count > 1)
            {
                tempLst.AddRange(assetPaths);
                tempLst.Sort((a, b) => string.Compare(a, b, StringComparison.OrdinalIgnoreCase));
                var array = tempLst.ToArray();
                tempLst.Clear();
                return array;
            }
            return assetPaths.ToArray();
        }
        /// <summary>
        /// 将所有AssetBundleData转成List并排序
        /// </summary>
        public List<AssetBundleData> SortAssetBundleDatas()
        {
            _bundleDataLst = bundleDataMap.Values.ToList();
            _bundleDataLst.Sort(CompareAssetBundle);
            return _bundleDataLst;
        }
        public int CompareAssetBundle(AssetBundleData bundleA, AssetBundleData bundleB)
        {
            if (bundleNameMode == AssetBundleNameMode.AssetPath)
            {
                var guidA = AssetDatabase.AssetPathToGUID(bundleA.originBundleName);
                var guidB = AssetDatabase.AssetPathToGUID(bundleB.originBundleName);
                //有assetPath的bundle排前面
                var idx_0 = string.IsNullOrEmpty(guidA) == false ? -1 : 1;
                var idx_1 = string.IsNullOrEmpty(guidB) == false ? -1 : 1;
                var remain = idx_0 - idx_1;
                if (remain != 0)
                {
                    return remain;
                }
            }
            else
            {
                var pathA = AssetDatabase.GUIDToAssetPath(bundleA.originBundleName);
                var pathB = AssetDatabase.GUIDToAssetPath(bundleB.originBundleName);

                //有assetPath的bundle排前面
                var idx_0 = string.IsNullOrEmpty(pathA) == false ? -1 : 1;
                var idx_1 = string.IsNullOrEmpty(pathB) == false ? -1 : 1;
                var remain = idx_0 - idx_1;
                if (remain != 0)
                {
                    return remain;
                }
                if (idx_0 == -1)
                {
                    return string.Compare(pathA, pathB, StringComparison.OrdinalIgnoreCase);
                }
            }
            return string.Compare(bundleA.originBundleName, bundleB.originBundleName, StringComparison.OrdinalIgnoreCase);
        }
        public System.Diagnostics.Stopwatch bundleDependenciesSw = new System.Diagnostics.Stopwatch();
        public string[] GetDependencies(uint bundleId, string[] assetNames)
        {
            if (_bundleDependenciesMap.TryGetValue(bundleId, out var dependencies) == false)
            {
                bundleDependenciesSw.Start();
                dependencies = AssetDatabase.GetDependencies(assetNames, true);
                bundleDependenciesSw.Stop();
                _bundleDependenciesMap.Add(bundleId, dependencies);
            }
            return dependencies;
        }
    }
}
