using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

using UnityEditor.Build.Pipeline;
using UnityEditor.Build.Pipeline.Interfaces;

namespace YooAsset.Editor
{
	public class ManifestContext : IContextObject
	{
		internal PackageManifest Manifest;
	}

	public abstract class TaskCreateManifest
	{
		private readonly Dictionary<string, int> _cachedBundleID = new Dictionary<string, int>(10000);

		/// <summary>
		/// 创建补丁清单文件到输出目录
		/// </summary>
		protected void CreateManifestFile(BuildContext context)
		{
			var buildMapContext = context.GetContextObject<BuildMapContext>();
			var buildParametersContext = context.GetContextObject<BuildParametersContext>();
			var buildParameters = buildParametersContext.Parameters;
			string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();

			// 创建新补丁清单
			PackageManifest manifest = new PackageManifest();
			manifest.FileVersion = YooAssetSettings.ManifestFileVersion;
			manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
			manifest.LocationToLower = buildMapContext.Command.LocationToLower;
			manifest.IncludeAssetGUID = buildMapContext.Command.IncludeAssetGUID;
			manifest.OutputNameStyle = (int)buildParameters.FileNameStyle;
			manifest.BuildPipeline = buildParameters.BuildPipeline;
			manifest.PackageName = buildParameters.PackageName;
			manifest.PackageVersion = buildParameters.PackageVersion;
			manifest.BundleList = GetAllPackageBundle(buildMapContext);
			manifest.AssetList = GetAllPackageAsset(buildMapContext);

			if (buildParameters.BuildMode != EBuildMode.SimulateBuild)
			{
				// 处理资源包的依赖列表
				ProcessBundleDepends(context, manifest);

				// 处理资源包的标签集合
				ProcessBundleTags(manifest);
			}

			// 创建补丁清单文本文件
			{
				string fileName = YooAssetSettingsData.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion);
				string filePath = $"{packageOutputDirectory}/{fileName}";
				ManifestTools.SerializeToJson(filePath, manifest);
				BuildLogger.Log($"创建补丁清单文件：{filePath}");
			}

			// 创建补丁清单二进制文件
			string packageHash;
			{
				string fileName = YooAssetSettingsData.GetManifestBinaryFileName(buildParameters.PackageName, buildParameters.PackageVersion);
				string filePath = $"{packageOutputDirectory}/{fileName}";
				ManifestTools.SerializeToBinary(filePath, manifest);
				packageHash = HashUtility.FileMD5(filePath);
				BuildLogger.Log($"创建补丁清单文件：{filePath}");

				ManifestContext manifestContext = new ManifestContext();
				byte[] bytesData = FileUtility.ReadAllBytes(filePath);
				manifestContext.Manifest = ManifestTools.DeserializeFromBinary(bytesData);
				context.SetContextObject(manifestContext);
			}

			// 创建补丁清单哈希文件
			{
				string fileName = YooAssetSettingsData.GetPackageHashFileName(buildParameters.PackageName, buildParameters.PackageVersion);
				string filePath = $"{packageOutputDirectory}/{fileName}";
				FileUtility.WriteAllText(filePath, packageHash);
				BuildLogger.Log($"创建补丁清单哈希文件：{filePath}");
			}

			// 创建补丁清单版本文件
			{
				string fileName = YooAssetSettingsData.GetPackageVersionFileName(buildParameters.PackageName);
				string filePath = $"{packageOutputDirectory}/{fileName}";
				FileUtility.WriteAllText(filePath, buildParameters.PackageVersion);
				BuildLogger.Log($"创建补丁清单版本文件：{filePath}");
			}
		}

		/// <summary>
		/// 获取资源包的依赖集合
		/// </summary>
		protected abstract string[] GetBundleDepends(BuildContext context, string bundleName);

		/// <summary>
		/// 获取主资源对象列表
		/// </summary>
		private List<PackageAsset> GetAllPackageAsset(BuildMapContext buildMapContext)
		{
			List<PackageAsset> result = new List<PackageAsset>(1000);
			foreach (var bundleInfo in buildMapContext.Collection)
			{
				var assetInfos = bundleInfo.GetAllManifestAssetInfos();
				foreach (var assetInfo in assetInfos)
				{
					PackageAsset packageAsset = new PackageAsset();
					packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
					packageAsset.AssetPath = assetInfo.AssetPath;
					packageAsset.AssetGUID = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetGUID : string.Empty;
					packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
					packageAsset.BundleID = GetCachedBundleID(assetInfo.BundleName);
					result.Add(packageAsset);
				}
			}
			return result;
		}

		/// <summary>
		/// 获取资源包列表
		/// </summary>
		private List<PackageBundle> GetAllPackageBundle(BuildMapContext buildMapContext)
		{
			List<PackageBundle> result = new List<PackageBundle>(1000);
			foreach (var bundleInfo in buildMapContext.Collection)
			{
				var packageBundle = bundleInfo.CreatePackageBundle();
				result.Add(packageBundle);
			}

			// 注意：缓存资源包索引
			for (int index = 0; index < result.Count; index++)
			{
				string bundleName = result[index].BundleName;
				_cachedBundleID.Add(bundleName, index);
			}

			return result;
		}

		/// <summary>
		/// 处理资源包的依赖集合
		/// </summary>
		private void ProcessBundleDepends(BuildContext context, PackageManifest manifest)
		{
			// 查询引擎生成的资源包依赖关系，然后记录到清单
			foreach (var packageBundle in manifest.BundleList)
			{
				int mainBundleID = GetCachedBundleID(packageBundle.BundleName);
				var depends = GetBundleDepends(context, packageBundle.BundleName);
				List<int> dependIDs = new List<int>(depends.Length);
				foreach (var dependBundleName in depends)
				{
					int bundleID = GetCachedBundleID(dependBundleName);
					if (bundleID != mainBundleID)
						dependIDs.Add(bundleID);
				}
				packageBundle.DependIDs = dependIDs.ToArray();
			}
		}

		/// <summary>
		/// 处理资源包的标签集合
		/// </summary>
		private void ProcessBundleTags(PackageManifest manifest)
		{
			Dictionary<int, HashSet<string>> cacheBundleTags = new Dictionary<int, HashSet<string>>(10000);

			foreach (var packageAsset in manifest.AssetList)
			{
				// 主资源包
				int bundleID = packageAsset.BundleID;
				CacheBundleTags(cacheBundleTags, bundleID, packageAsset.AssetTags);

				// 依赖资源包
				var packageBundle = manifest.BundleList[bundleID];
				foreach (var dependBundleID in packageBundle.DependIDs)
				{
					CacheBundleTags(cacheBundleTags, dependBundleID, packageAsset.AssetTags);
				}
			}

			for (int index = 0; index < manifest.BundleList.Count; index++)
			{
				manifest.BundleList[index].Tags = cacheBundleTags[index].ToArray();
			}
		}
		private void CacheBundleTags(Dictionary<int, HashSet<string>> cacheBundleTags, int bundleID, string[] assetTags)
		{
			if (cacheBundleTags.ContainsKey(bundleID) == false)
				cacheBundleTags.Add(bundleID, new HashSet<string>());

			foreach (var assetTag in assetTags)
			{
				if (cacheBundleTags[bundleID].Contains(assetTag) == false)
					cacheBundleTags[bundleID].Add(assetTag);
			}
		}

		/// <summary>
		/// 获取资源包的索引ID
		/// </summary>
		private int GetCachedBundleID(string bundleName)
		{
			if (_cachedBundleID.TryGetValue(bundleName, out int value) == false)
			{
				throw new Exception($"Not found cached bundle ID : {bundleName}");
			}
			return value;
		}
	}
}