using Cysharp.Threading.Tasks;
using HybridCLR;
using Sirenix.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
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列表的配置也可以通过热更新下下来,这样增加了新的泛型就不怕了
            ConfigStringList hotUpdateAOTDlls =
                (await YooAssetProxy.LoadAssetAsync<ConfigStringList>("HotUpdateAOTDlls"))
                .GetAssetObject<ConfigStringList>();

			List<UniTask<RawFileOperation>> tasks = new List<UniTask<RawFileOperation>>();
            foreach (var aotDll in hotUpdateAOTDlls.List)
            {
                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()
        {
            Dictionary<string, Type> typesInAssembly = new Dictionary<string, Type>();
            Assembly modelAss = null;
#if ! UNITY_EDITOR 
            var battleDlls = (await YooAssetProxy.LoadAssetAsync<ConfigStringList>("HotupdateBattleDlls")).GetAssetObject<ConfigStringList>();
            foreach (var dll in battleDlls.List)
            {
                Log.Info($"to load: {dll}");
                Assembly.Load((await YooAssetProxy.GetRawFileAsync(dll)).GetRawBytes());
            }
#endif
            var hotDlls = (await YooAssetProxy.LoadAssetAsync<ConfigKeyValueMap>("HotupdateDlls")).GetAssetObject<ConfigKeyValueMap>();
            foreach(var kv in hotDlls.keyValueMaps)
            {
                Assembly ass;
                if(kv.Value.IsNullOrWhitespace())
                {
                    Log.Info($"to load: {kv.Key}");
                    ass = Assembly.Load((await YooAssetProxy.GetRawFileAsync(kv.Key)).GetRawBytes());
                }
                else
                {
                    Log.Info($"to load: {kv.Key}");
                    ass = Assembly.Load((await YooAssetProxy.GetRawFileAsync(kv.Key)).GetRawBytes(),
                        (await YooAssetProxy.GetRawFileAsync(kv.Value)).GetRawBytes());
                }

                foreach (Type type in ass.GetTypes())
                {
                    typesInAssembly[type.FullName] = type;
                }
                if(kv.Key.Equals("Model.dll"))
                {
                    modelAss = ass;
                }
            }
            EventSystem.Instance.Add(typesInAssembly);

            if(modelAss == null)
            {
                Log.Error("Not Found 'Model.dll'! ");
                return;
            }
            Log.Info("to call Client.Entry.");
            IStaticMethod start = new MonoStaticMethod(modelAss, "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()
        {
            //防裁剪
             var xx = ImageConversion.EnableLegacyPngGammaRuntimeLoadBehavior;
            Image x = Image.FromFile("");
        }
    }
}