using Cysharp.Threading.Tasks;
using HybridCLR;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using UnityEngine;
using YooAsset;

namespace ET
{
	public class CodeLoader: Singleton<CodeLoader>
	{
		private Assembly model;

		public void Start()
		{
			if (Define.EnableCodes)
			{
				GlobalConfig globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");
				if (globalConfig.CodeMode != CodeMode.ClientServer)
				{
					throw new Exception("ENABLE_CODES mode must use ClientServer code mode!");
				}
				
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				Dictionary<string, Type> types = AssemblyHelper.GetAssemblyTypes(assemblies);
				EventSystem.Instance.Add(types);
				foreach (Assembly ass in assemblies)
				{
					string name = ass.GetName().Name;
					if (name == "Unity.Model.Codes")
					{
						this.model = ass;
					}
				}
				
				IStaticMethod start = new StaticMethod(this.model, "ET.Entry", "Start");
				start.Run();
			}
			else
			{
                LoadAssetsAndHotfix().Forget();
			}
		}

        private async UniTaskVoid LoadAssetsAndHotfix()
        {
            // 启动YooAsset引擎,并在初始化完毕后进行热更代码加载
            await YooAssetProxy.StartYooAssetEngine(YooAssets.EPlayMode.HostPlayMode);

            // Shader Warm Up
            /*ShaderVariantCollection shaderVariantCollection =
                (await YooAssetProxy.LoadAssetAsync<ShaderVariantCollection>(
                    YooAssetProxy.GetYooAssetFormatResPath("ProjectSShaderVariant",
                        YooAssetProxy.YooAssetResType.Shader)))
                .GetAssetObject<ShaderVariantCollection>();

            Stopwatch stopwatch = Stopwatch.StartNew();
            shaderVariantCollection.WarmUp();
            stopwatch.Stop();
            Log.Info($"Shader Warm Up完成, 耗时: {stopwatch.ElapsedMilliseconds}ms,shaderCount: {shaderVariantCollection.shaderCount} variantCount: {shaderVariantCollection.variantCount}");*/

#if !UNITY_EDITOR
            //给mscorlib.dll等AOT补充元数据
            await LoadMetadataForAOT();
#endif
			await LoadHotfixCode();
        }

		// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
		private async UniTask LoadMetadataForAOT()
		{
			//TODO: DLL列表
			List<string> DLLNameList_ForABLoad = new List<string> (){  } ;

			List<UniTask<RawFileOperation>> tasks = new List<UniTask<RawFileOperation>>();

            foreach (var aotDll in DLLNameList_ForABLoad)
            {
                tasks.Add(YooAssetProxy.GetRawFileAsync(aotDll));
            }
            RawFileOperation[] rawFileOperations = await UniTask.WhenAll(tasks);

            foreach (var task in rawFileOperations)
            {
                var ret = RuntimeApi.LoadMetadataForAOTAssembly(task.GetRawBytes(), HomologousImageMode.SuperSet);
                if (ret == LoadImageErrorCode.OK)
                {
                    Log.Info($"LoadMetadataForAOTAssembly({task.GetBundleName()}) ok.");
                }
                else
                {
                    Log.Error($"LoadMetadataForAOTAssembly({task.GetBundleName()}) Error: {ret}");
                }
            }
        }

        //TODO: load model.dll  hotfix.dll
        public async UniTask LoadHotfixCode()
        {
            Log.Info("to load hotfix.dll.");
            byte[] assBytes = (await YooAssetProxy.GetRawFileAsync("Code_ProjectS_Hotfix.dll")).GetRawBytes();
            byte[] pdbBytes = (await YooAssetProxy.GetRawFileAsync("Code_ProjectS_Hotfix.pdb")).GetRawBytes();
            var assembly = Assembly.Load(assBytes, pdbBytes);

            //Dictionary<string, Type> types = AssemblyHelper.GetAssemblyTypes(typeof(Game).Assembly, typeof(Init).Assembly, this.model, hotfixAssembly);
            //EventSystem.Instance.Add(types);

            Log.Info("to call Client.Entry.");
            IStaticMethod start = new MonoStaticMethod(assembly, "ET.Client.Entry", "Start");
            start.Run();
        }
    }
}