using XGame.Editor.Asset;
using XGame.Framework.Asset;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace XGame.Editor.Build.AssetBundles
{
    internal partial class BundleManifestsController
    {
        /// <summary>
        /// ab加密的偏移值上限
        /// </summary>
        internal readonly uint EncryptOffsetLimit = 256;

        private BundleContext _context;
        private AssetBundleCollector _bundleCollector;
        private BundleDepsController _depsController;
        private SceneManifestController _sceneController;

        public BundleManifestsController(BundleContext context, AssetBundleCollector bundleCollector, BundleDepsController depsController, SceneManifestController sceneController)
        {
            _context = context;
            _bundleCollector = bundleCollector;
            _depsController = depsController;
            _sceneController = sceneController;
        }

        public bool GenerateManifests()
        {
            _bundleCollector.CollectBundleBuilds(true);
            //检查重名资源
            if (!CheckAssetsNameRepeat())
            {
                //有同名资源就中断打包
                return false;
            }
            if (!_bundleCollector.CollectSceneBundles())
            {
                //可能有同名Scene
                return false;
            }
            //if (!_bundleCollector.MergeSceneBundles())
            //{
            //    return false;
            //}
            //处理AssetBundle分组
            //_groupController.ModifyBundleNameByGroup();

            BuildLog.Log("BuildBundles count:" + _context.bundleDataMap.Count);
            if (!Directory.Exists(PathDefine.ConfigsRelative))
            {
                Directory.CreateDirectory(PathDefine.ConfigsRelative);
            }

            if (!GenerateBundleManifest() || !_sceneController.GenerateManifest() || !GenReferencesManifest())
            {
                return false;
            }
            BuildLog.Log($"AssetBundle GenerateManifests completed.");
            return true;
        }

        /// <summary>
        /// 收集bundle信息，后期可能需要改成异步操作
        /// </summary>
        /// <param name="bundleBuilds"></param>
        /// <returns></returns>
        private bool GenerateBundleManifest()
        {
            if (_context.bundleDataMap.Count == 0)
            {
                return false;
            }
            if (!_depsController.CollectBundleDependencies())
            {
                return false;
            }
            //BundleManifest
            string bundleManifestPath = PathDefine.BundleManifestPath;//"Assets/KCAssetBundlesData/KCAssetBundleManifest.asset";
            var kcBundleManifest = AssetDatabase.LoadAssetAtPath<AssetBundleInfosSo>(bundleManifestPath);
            if (kcBundleManifest == null)
            {
                kcBundleManifest = ScriptableObject.CreateInstance<AssetBundleInfosSo>();
                AssetDatabase.CreateAsset(kcBundleManifest, bundleManifestPath);
            }
            //bundle信息
            var bundleDataLst = _context.SortAssetBundleDatas();
            var count = bundleDataLst.Count;
            var bundleInfos = new AssetBundleInfo[count];
            var backupInfos = new AssetBundleBackupInfo[count];
            for (var index = 0; index < count; index++)
            {
                var bundleData = bundleDataLst[index];
                EditorUtility.DisplayProgressBar("Collect AssetBundleInfo", bundleData.assetBundleName, index / (float)count);
                var dependencies = _context.dependenciesMap[bundleData.bundleId];
                //var bundleId = CreateBundleId(build.assetBundleName, build.assetBundleVariant, build.addressableNames);
                //var bytes = System.Text.Encoding.UTF8.GetBytes(bundleData.originBundleName);
                //originBundleName大部分为guid，固定32字节，用bundleId取余数增加点offset的差异
                var originName = bundleData.originBundleName;
                var offset = (uint)Math.Clamp(bundleData.bundleId % originName.Length, 4, EncryptOffsetLimit);

                var bundleInfo = new AssetBundleInfo()
                {
                    bundleName = bundleData.assetBundleName,
                    variantName = bundleData.assetBundleVariant,
                    bundleId = bundleData.bundleId,
                    assets = bundleData.addressableNames,
                    dependencies = dependencies,
                    //originBundleName = originName,
                    offset = offset,
                };
                bundleInfos[index] = bundleInfo;
                // 备份数据
                var backupInfo = new AssetBundleBackupInfo()
                {
                    bundleName = bundleData.assetBundleName,
                    variantName = bundleData.assetBundleVariant,
                    bundleId = bundleData.bundleId,
                    assets = bundleData.addressableNames,
                    dependencies = dependencies,
                    offset = offset,
                    bundleType = bundleData.bundleType,
                    nameOrGuid = originName,
                    originAssetPath = _context.bundleNameMode == AssetBundleNameMode.Guid ? AssetDatabase.GUIDToAssetPath(originName) : string.Empty,
                };
                backupInfos[index] = backupInfo;
            }
            EditorUtility.ClearProgressBar();
            //保存
            kcBundleManifest.bundleInfos = bundleInfos;
            EditorUtility.SetDirty(kcBundleManifest);
            AssetBundleBackupManifest.Save(backupInfos, false);
            AssetDatabase.SaveAssets();
            BuildLog.Log("Save BundleManifest.");
            AssetDatabase.Refresh();
            return true;
        }

        bool GenReferencesManifest()
        {
            string refManifestPath = PathDefine.ReferenceManifestPath;
            var referenceManifest = AssetDatabase.LoadAssetAtPath<AssetReferenceInfosSo>(refManifestPath);
            if (referenceManifest == null)
            {
                referenceManifest = ScriptableObject.CreateInstance<AssetReferenceInfosSo>();
                AssetDatabase.CreateAsset(referenceManifest, refManifestPath);
            }
            //asset被引用信息
            var assetReferencesMap = new Dictionary<string, HashSet<string>>();
            //addressable信息
            //ResStatic的bundle不允许动态加载，因此不需要记录引用信息
            var addressableMap = _context.GetAddressableInfoMap();
            var sw = new System.Diagnostics.Stopwatch();
            foreach (var item in addressableMap)
            {
                if (_context.allTexturePaths.Contains(item.Key))
                {
                    //忽略贴图资源
                    continue;
                }
                var assetPath = item.Key;
                var assetAddressable = item.Value;
                sw.Start();
                var dependencies = AssetDatabase.GetDependencies(assetPath, true);
                sw.Stop();
                foreach (var dep in dependencies)
                {
                    if (dep != assetPath && addressableMap.TryGetValue(dep, out var depAddressableName))
                    { // 同bundle内的asset引用也需要记录
                        if (assetReferencesMap.TryGetValue(depAddressableName, out var referenceLst) == false)
                        {
                            referenceLst = new HashSet<string>();
                            assetReferencesMap.Add(depAddressableName, referenceLst);
                        }
                        referenceLst.Add(assetAddressable);
                    }
                }
            }
            BuildLog.Log($"GenReferencesManifest GetDependencies Time:{sw.ElapsedMilliseconds}");
            AssetReferenceInfo[] referenceInfos = new AssetReferenceInfo[assetReferencesMap.Count];
            int index = 0;
            foreach (var item in assetReferencesMap)
            {
                referenceInfos[index++] = new AssetReferenceInfo()
                {
                    assetName = item.Key,
                    references = item.Value.ToArray()
                };
            }
            referenceManifest.references = referenceInfos;
            EditorUtility.SetDirty(referenceManifest);
            AssetDatabase.SaveAssets();
            BuildLog.Log("Save ReferenceManifest.");
            AssetDatabase.Refresh();
            return true;
        }
        /// <summary>
        /// 检查Assetbundle内是否存在同名资源
        /// </summary>
        /// <param name="assetBundleBuilds"></param>
        /// <returns></returns>
        private bool CheckAssetsNameRepeat()
        {
            bool result = true;
            Dictionary<string, string> assetPathMap = new Dictionary<string, string>();
            var allAssetPaths = new HashSet<string>();
            foreach (var item in _context.bundleDataMap)
            {
                assetPathMap.Clear();
                foreach (var assetPath in item.Value.assetNames)
                {
                    var fileName = Path.GetFileName(assetPath);
                    if (assetPathMap.ContainsKey(fileName))
                    {
                        result = false;
                        BuildLog.Error($"[XBuild] AssetBundle存在重名资源。Bundle:{item.Value.originBundleName} 重名资源1:{assetPathMap[fileName]} 重名资源2:{assetPath}");
                    }
                    else
                    {
                        assetPathMap.Add(fileName, assetPath);
                    }
                    if (allAssetPaths.Contains(assetPath))
                    {
                        BuildLog.Error($"[XBuild] AssetBundle存在重复资源。Path:{assetPath}");
                        result = false;
                        continue;
                    }
                    allAssetPaths.Add(assetPath);
                }
            }
            return result;
        }
    }
}
