using XGame.Framework.Asset;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using XGame.Framework.Json;

namespace XGame.Editor.Build.AssetBundles
{
    internal class AssetBundleGroupsTask
    {
        public class AssetbundleGroupsManifest
        {
            //public UnityEditor.AssetBundleBuild[] bundleBuilds;
            public AssetbundleGroup[] bundleGroups;
        }
        public class AssetbundleGroup
        {
            public int groupId;
            public AssetbundleData[] bundles;
        }
        public class AssetbundleData
        {
            public uint bundleId;
            public int buildIndex;
            public uint[] dependencies;
        }
        public enum AbGroupMode
        {
            Normal = 0,
            Dependencies,
        }

        private string _outputPath;
        private AbGroupMode _groupMode;
        //public AssetBundleGroupsTask()
        //{
        //    _outputPath = string.Empty;
        //    _groupMode = AbGroupMode.Dependencies;
        //}
        public AssetBundleGroupsTask(string outputPath, AbGroupMode groupMode)
        {
            _outputPath = outputPath;
            _groupMode = groupMode;

            if (string.IsNullOrEmpty(outputPath))
                return;
            var dirPath = Path.GetDirectoryName(outputPath);
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
                BuildLog.Log($"AssetBundleGroupsTask CreateDirectory: {dirPath}");
            }
        }

        public bool Excute()
        {
            XBuildPipeline.GenerateBundleManifests();
            var manifest = AssetBundleUtils.LoadManifest();
            if (manifest == null || manifest.bundleInfos == null)
                return false;
            if (!BundleGroup(manifest.bundleInfos, out var bundleGroupMap))
            {
                return false;
            }
            return Export(manifest.bundleInfos, bundleGroupMap);
        }
        internal bool BundleGroup(AssetBundleInfo[] bundleInfos, out Dictionary<uint, int> bundleGroupMap)
        {
            if (_groupMode == AbGroupMode.Dependencies)
            {
                return BundleGroupByDependencies(bundleInfos, out bundleGroupMap);
            }
            return BundleGroupNormal(bundleInfos, out bundleGroupMap);
        }
        private bool BundleGroupNormal(AssetBundleInfo[] bundleInfos, out Dictionary<uint, int> bundleGroupMap)
        {
            bundleGroupMap = new Dictionary<uint, int>();
            var groupIndex = 0;
            var bundleIds = new HashSet<uint>();
            var groups = new HashSet<int>();
            foreach (var bundleInfo in bundleInfos)
            {
                //先找出该bundle和依赖的所有groupId
                var min = -1;
                if (bundleGroupMap.TryGetValue(bundleInfo.bundleId, out var groupId))
                {
                    min = groupId;
                    groups.Add(groupId);
                }
                if (bundleInfo.dependencies != null)
                {
                    foreach (var dependence in bundleInfo.dependencies)
                    {
                        if (bundleGroupMap.TryGetValue(dependence, out groupId))
                        {
                            groups.Add(groupId);
                            if (min == -1 || groupId < min)
                                min = groupId;
                        }
                    }
                }
                if (groups.Count > 0)
                {
                    groups.Remove(min);
                    if (groups.Count > 0)
                    {
                        //关联的bundle也必须同步修改groupId
                        foreach (var key in bundleIds)
                        {
                            var val = bundleGroupMap[key];
                            if (groups.Contains(val))
                            {
                                bundleGroupMap[key] = min;
                            }
                        }
                    }
                    //取groupId最小值
                    groupId = min;
                }
                else
                {
                    //新增一个Group
                    groupId = groupIndex++;
                }
                //设置GroupId
                if (!bundleGroupMap.ContainsKey(bundleInfo.bundleId))
                {
                    bundleGroupMap[bundleInfo.bundleId] = groupId;
                    bundleIds.Add(bundleInfo.bundleId);
                }
                if (bundleInfo.dependencies != null)
                {
                    foreach (var dependence in bundleInfo.dependencies)
                    {
                        if (!bundleGroupMap.ContainsKey(dependence))
                        {
                            bundleGroupMap[dependence] = groupId;
                            bundleIds.Add(dependence);
                        }
                    }
                }
                //清除缓存
                groups.Clear();
            }
            return true;
        }
        private bool BundleGroupByDependencies(AssetBundleInfo[] bundleInfos, out Dictionary<uint, int> bundleGroupMap)
        {
            bundleGroupMap = new Dictionary<uint, int>();
            var bundlesMap = new Dictionary<uint, AssetBundleInfo>();
            var bundleIds = new List<uint>();
            foreach (var bundleInfo in bundleInfos)
            {
                bundlesMap.Add(bundleInfo.bundleId, bundleInfo);
                if (bundleInfo.DependenciesCount == 0)
                {
                    bundleGroupMap.Add(bundleInfo.bundleId, 0);
                }
                else
                {
                    bundleIds.Add(bundleInfo.bundleId);
                }
            }
            var groupId = 1;
            var tempIds = new List<uint>();
            while (bundleIds.Count > 0)
            {
                foreach (var bundleId in bundleIds)
                {
                    var bundleInfo = bundlesMap[bundleId];
                    var isTrue = true;
                    foreach (var dep in bundleInfo.dependencies)
                    {
                        if (!bundleGroupMap.ContainsKey(dep))
                        {
                            isTrue = false;
                            break;
                        }
                    }
                    if (isTrue)
                    {
                        tempIds.Add(bundleId);
                    }
                }
                if (tempIds.Count > 0)
                {
                    foreach (var bundleId in tempIds)
                    {
                        bundleIds.Remove(bundleId);
                        bundleGroupMap.Add(bundleId, groupId);
                    }
                    groupId += 1;
                    tempIds.Clear();
                }
                else
                {
                    Log.Error($"Assetbundle分组失败。已分数量:{bundleGroupMap.Count} 剩余:{bundleIds.Count} Group:{groupId}");
                    return false;
                }
            }
            return true;
        }
        private bool Export(AssetBundleInfo[] bundleInfos, Dictionary<uint, int> bundleGroupMap)
        {
            var outputPath = _outputPath;
            var abCount = bundleInfos.Length;
            var manifest = new AssetbundleGroupsManifest()
            {
                //bundleBuilds = new UnityEditor.AssetBundleBuild[abCount],
            };
            var bundleDataMap = new Dictionary<uint, AssetbundleData>();
            var abIndex = 0;
            foreach (var bundleInfo in bundleInfos)
            {
                var bundleId = bundleInfo.bundleId;
                bundleDataMap[bundleId] = new AssetbundleData()
                {
                    bundleId = bundleId,
                    buildIndex = abIndex,
                    dependencies = bundleInfo.dependencies,
                };
                //manifest.bundleBuilds[abIndex] = bundleBuildMap[bundleId].ToUniBuild();
                abIndex++;
            }
            var bundleGroups = bundleGroupMap.GroupBy(item => item.Value);
            var groupCount = bundleGroups.Count();
            var abGroups = new List<AssetbundleGroup>();
            foreach (var bundleGroup in bundleGroups)
            {
                var count = bundleGroup.Count();
                var abGroup = new AssetbundleGroup()
                {
                    groupId = bundleGroup.Key,
                    bundles = new AssetbundleData[count],
                };
                var bundleIdx = 0;
                foreach (var item in bundleGroup)
                {
                    abGroup.bundles[bundleIdx++] = bundleDataMap[item.Key];
                }
                abGroups.Add(abGroup);
            }
            abGroups.Sort((a, b) => a.groupId - b.groupId);
            manifest.bundleGroups = abGroups.ToArray();
            var text = XJson.ToJson(manifest);
            File.WriteAllText(outputPath, text, System.Text.Encoding.UTF8);
            BuildLog.Log($"GenAssetbundleGroups finish. GroupCount:{groupCount} Path:{outputPath}");
            return true;
        }
    }
}

