using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace XGame.Editor.Build.AssetBundles
{
    /// <summary>
    /// 收集bundle依赖
    /// </summary>
    internal class BundleDepsController
    {
        private BundleContext _context;
        public BundleDepsController(BundleContext context)
        {
            _context = context;
        }
        /// <summary>
        /// key: assetPath
        /// value: bundleId
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, uint>  GetAssetPathMap()
        {
            var assetPathMap = new Dictionary<string, uint>();
            foreach (var keyValue in _context.bundleDataMap)
            {
                var build = keyValue.Value;
                foreach (var assetPath in build.assetNames)
                {
                    if (assetPathMap.ContainsKey(assetPath))
                    {
                        BuildLog.Error($"assetPath repeat. Id:{build.bundleId} Name:{build.originBundleName} Asset:{assetPath}");
                    }
                    else
                    {
                        assetPathMap.Add(assetPath, build.bundleId);
                    }
                }
            }
            return assetPathMap;
        }
        /// <summary>
        /// 收集所有bundle的依赖bundleId
        /// </summary>
        /// <returns></returns>
        public bool CollectBundleDependencies()
        {
            var assetPathToBundleIdMap = GetAssetPathMap();
            var dependenciesMap = _context.dependenciesMap;
            dependenciesMap.Clear();
            var depAssetsMap = new Dictionary<long, HashSet<string>>();
            int index = 0;
            var tempAssetPaths = new HashSet<string>();
            foreach (var item in _context.bundleDataMap)
            {
                EditorUtility.DisplayProgressBar("Collect Asset Dependencies", item.Value.assetBundleName, (++index) / (float)_context.bundleDataMap.Count);

                tempAssetPaths.UnionWith(item.Value.assetNames);
                var assetPaths = new HashSet<string>();
                if (item.Value.isTextureBundle == false)
                { //全是贴图的bundle不用查找
                    var dependencies = _context.GetDependencies(item.Value.bundleId, item.Value.assetNames);
                    foreach (var depPath in dependencies)
                    {
                        if (assetPathToBundleIdMap.ContainsKey(depPath) && !tempAssetPaths.Contains(depPath))
                        {
                            assetPaths.Add(depPath);
                        }
                    }
                }
                depAssetsMap.Add(item.Key, assetPaths);
                tempAssetPaths.Clear();
            }
            EditorUtility.ClearProgressBar();
            Debug.Log($"AssetBundle GetDependencies Time:{_context.bundleDependenciesSw.ElapsedMilliseconds}");
            var bundleIds = new HashSet<long>();
            foreach (var item in depAssetsMap)
            {
                if (item.Value.Count == 0)
                {
                    dependenciesMap.Add(item.Key, new uint[0]);
                    //Debug.Log($"Assetbundle依赖为零. Name:{item.Key}");
                }
                else
                {
                    bundleIds.Add(item.Key);
                }
            }
            var tempIds = new HashSet<long>();
            var depBundleIds = new List<uint>();
            while (bundleIds.Count > 0)
            {
                EditorUtility.DisplayProgressBar("Collect Bundle Dependencies", $"剩余: {bundleIds.Count}", dependenciesMap.Count / (float)_context.bundleDataMap.Count);
                foreach (var bundleId in bundleIds)
                {
                    var dependencies = depAssetsMap[bundleId];
                    var isTrue = true;
                    foreach (var dependency in dependencies)
                    {
                        var depBundleId = assetPathToBundleIdMap[dependency];
                        if (!dependenciesMap.ContainsKey(depBundleId))
                        {
                            isTrue = false;
                            break;
                        }
                    }
                    if (isTrue)
                    {
                        tempIds.Add(bundleId);
                    }
                }
                if (tempIds.Count > 0)
                {
                    foreach (var bundleId in tempIds)
                    {
                        bundleIds.Remove(bundleId);

                        var dependencies = depAssetsMap[bundleId];
                        foreach (var dependency in dependencies)
                        {
                            var depId = (uint)assetPathToBundleIdMap[dependency];
                            depBundleIds.Add(depId);
                            depBundleIds.AddRange(dependenciesMap[depId]);
                            //if (bundleId == 2940165715 || bundleId == 1899048051 || bundleId == 2238712722)
                            //{
                            //    var sb = new System.Text.StringBuilder();
                            //    sb.Append(bundleId);
                            //    sb.Append(" depBundle:");
                            //    sb.Append(depId);
                            //    sb.Append("  dependencies:");
                            //    foreach (var item in depBundleIdsMap[depId])
                            //    {
                            //        sb.Append(item);
                            //        sb.Append("; ");
                            //    }
                            //    Debug.Log(sb.ToString());
                            //}
                        }
                        depBundleIds = depBundleIds.Distinct().ToList();
                        depBundleIds.Remove((uint)bundleId);
                        dependenciesMap.Add(bundleId, depBundleIds.ToArray());
                        depBundleIds.Clear();
                    }
                    tempIds.Clear();
                }
                else
                {
                    EditorUtility.ClearProgressBar();
                    //理论上这个肯定不会触发
                    Log.Error($"CollectBundleDependencies failed. 如果是Assetbundle出现互相依赖，请调整相关资源使相互依赖的Asset在同一个Assetbundle内.  Success: {dependenciesMap.Count} Failure: {bundleIds.Count}");
                    foreach (var bundleId in bundleIds)
                    {
                        var build = _context.bundleDataMap[bundleId];
                        var dependencies = depAssetsMap[bundleId];
                        foreach (var dep in dependencies)
                        {
                            if (assetPathToBundleIdMap.TryGetValue(dep, out var depBundleId) && dependenciesMap.ContainsKey(depBundleId) == false)
                            {
                                Log.Error($"bundleId:{bundleId} Name:{build.originBundleName} dependence:{dep} depBundleId:{depBundleId}");
                            }
                        }
                    }
                    return false;
                }
            }
            EditorUtility.ClearProgressBar();
            return true;
        }

    }
}
