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>
	{
        public void Start()
		{
            AsyncLoadResAndCode().Forget();
        }

        private async UniTaskVoid AsyncLoadResAndCode()
        {
            YooAssets.EPlayMode playMode;
            if (Define.EnableCodes)
            {
                playMode = YooAssets.EPlayMode.EditorSimulateMode;
            }
            else
            {
                GlobalConfig globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");
                playMode = globalConfig.PlayMode;
            }
            Log.Info($"start mode: {playMode}");

            // 启动YooAsset引擎
            await YooAssetProxy.StartYooAssetEngine(playMode);

            // 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 (Define.EnableCodes)
            {
                LoadLocalCode();
            }
            else
            {
                await LoadMetadataForAOT();
                await LoadHotfixCode();
            }
        }

		// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
		private async UniTask LoadMetadataForAOT()
		{
            //AOT dll列表的配置也可以通过热更新下下来,这样增加了新的泛型就不怕了
            HotUpdateAssemblyManifest mainfest =
                (await YooAssetProxy.LoadAssetAsync<HotUpdateAssemblyManifest>("HotUpdateAssemblyManifest"))
                .GetAssetObject<HotUpdateAssemblyManifest>();
            var DLLNameList_ForABLoad = mainfest.AOTMetadataDlls;

			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.Error($"Error occurs when loadMetadataForAOT: {ret}");
                }
            }
        }

        //热更加载 model.dll & hotfix.dll
        private async UniTask LoadHotfixCode()
        {
            Log.Info("to load model.dll & hotfix.dll.");

            var rawfile_modeldll = await YooAssetProxy.GetRawFileAsync("Model.dll");
            var rawfile_modelpdb = await YooAssetProxy.GetRawFileAsync("Model.pdb");
            var modelAssembly = Assembly.Load(rawfile_modeldll.GetRawBytes(), rawfile_modelpdb.GetRawBytes());

            var rawfile_hotfixdll = await YooAssetProxy.GetRawFileAsync("Hotfix.dll");
            var rawfile_hotfixpdb = await YooAssetProxy.GetRawFileAsync("Hotfix.pdb");
            var hotfixAssembly = Assembly.Load(rawfile_hotfixdll.GetRawBytes(), rawfile_hotfixpdb.GetRawBytes());

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

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

        //不使用dll,打开了EnableCode,使用Editor编辑模式
        private void LoadLocalCode()
        {
            //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);

            bool bStart = false;
            foreach (Assembly ass in assemblies)
            {
                string name = ass.GetName().Name;
                if (name == "Unity.Model.Codes")
                {
                    bStart = true;
                    IStaticMethod start = new StaticMethod(ass, "ET.Entry", "Start");
                    start.Run();
                    break;
                }
            }

            if (!bStart)
            {
                throw new Exception("not found Assembly: 'Unity.Model.Codes'");
            }
        }

        //编译时会检查这个方法,需保留
        public void LoadHotfix()
        {
        }
    }
}