using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using Debug = UnityEngine.Debug;

namespace ET
{
    public enum PlatformType
    {
        None,
        Android,
        IOS,
        Windows,
        MacOS,
        Linux
    }

    public enum BuildType
    {
        Development,
        Release,
    }

    public class BuildEditor : EditorWindow
    {
        private PlatformType activePlatform;
        private PlatformType platformType;
        private bool clearFolder = true;
        private bool isBuildExe = true;
        private bool isContainAB = false;
        private CodeOptimization codeOptimization = CodeOptimization.Release;
        private BuildOptions buildOptions;
        private BuildAssetBundleOptions buildAssetBundleOptions = BuildAssetBundleOptions.None;
        public static string YooAssetCodeDll;
        public static string BattlePluginDir = "./Assets/Plugins/BattlePlugin";

        private GlobalConfig globalConfig;

        [MenuItem("ET/Build Tool")]
        public static void ShowWindow()
        {
            GetWindow<BuildEditor>(DockDefine.Types);
        }

        private void OnEnable()
        {
            YooAssetCodeDll = Application.dataPath + "/Res/CodeDll/";
            globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");

#if UNITY_ANDROID
			activePlatform = PlatformType.Android;
#elif UNITY_IOS
			activePlatform = PlatformType.IOS;
#elif UNITY_STANDALONE_WIN
            activePlatform = PlatformType.Windows;
#elif UNITY_STANDALONE_OSX
			activePlatform = PlatformType.MacOS;
#elif UNITY_STANDALONE_LINUX
			activePlatform = PlatformType.Linux;
#else
			activePlatform = PlatformType.None;
#endif
            platformType = activePlatform;
        }

        private void OnGUI()
        {
            this.platformType = (PlatformType)EditorGUILayout.EnumPopup(platformType);
            this.clearFolder = EditorGUILayout.Toggle("clean folder? ", clearFolder);
            this.isBuildExe = EditorGUILayout.Toggle("build exe?", this.isBuildExe);
            this.isContainAB = false;
            this.codeOptimization = (CodeOptimization)EditorGUILayout.EnumPopup("CodeOptimization ", this.codeOptimization);
            //EditorGUILayout.LabelField("BuildAssetBundleOptions ");
            //this.buildAssetBundleOptions = (BuildAssetBundleOptions)EditorGUILayout.EnumFlagsField(this.buildAssetBundleOptions);

            switch (this.codeOptimization)
            {
                case CodeOptimization.None:
                case CodeOptimization.Debug:
                    this.buildOptions = BuildOptions.Development | BuildOptions.ConnectWithProfiler;
                    break;
                case CodeOptimization.Release:
                    this.buildOptions = BuildOptions.None;
                    break;
            }

            GUILayout.Space(5);

            if (GUILayout.Button("BuildPackage"))
            {
                if (this.platformType == PlatformType.None)
                {
                    ShowNotification(new GUIContent("please select platform!"));
                    return;
                }
                var globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");
                if (globalConfig.CodeMode == CodeMode.Server)
                {
                    ShowNotification(new GUIContent("not supoort CodeMode: Server!"));
                    return;
                }
                if (globalConfig.PlayMode == YooAsset.YooAssets.EPlayMode.EditorSimulateMode)
                {
                    ShowNotification(new GUIContent("YooAsset.PlayMode not support EditorSimulateMode!"));
                    return;
                }

                if (platformType != activePlatform)
                {
                    switch (EditorUtility.DisplayDialogComplex("Warning!", $"current platform is {activePlatform}, if change to {platformType}, may be take a long time", "change", "cancel", "no change"))
                    {
                        case 0:
                            activePlatform = platformType;
                            break;
                        case 1:
                            return;
                        case 2:
                            platformType = activePlatform;
                            break;
                    }
                }

                RemoveBattlePluginDlls();
                BuildHelper.Build(this.platformType, this.buildAssetBundleOptions, this.buildOptions, this.isBuildExe, this.isContainAB, this.clearFolder);
            }

            GUILayout.Label("");
            GUILayout.Label("Build & copy AOTMetadlls");
            if (GUILayout.Button("Hybrid-GenerateAll & copy2CodeDll"))
            {
                //CompileDll时,HotUpdateDll目录的model.dll会被清理掉,生成link.xml会报错
                //所以在compileDll后,增加了BuildModel & copy动作
                //PrebuildCommand.GenerateAll();

                //删除battle相关dll
                RemoveBattlePluginDlls();

                BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
                CompileDllCommand.CompileDll(target);

                //生成最新的mode.dll & hotfix.dll,然后copy到hotupdate目录,供linkxml用
                BuildModelAndHotfix(this.codeOptimization);
                CopyHotfixdllToHybridDir();

                Il2CppDefGeneratorCommand.GenerateIl2CppDef();
                // 这几个生成依赖HotUpdateDlls
                LinkGeneratorCommand.GenerateLinkXml(target);
                // 生成裁剪后的aot dll
                StripAOTDllCommand.GenerateStripedAOTDlls(target, EditorUserBuildSettings.selectedBuildTargetGroup);
                // 桥接函数生成依赖于AOT dll,必须保证已经build过,生成AOT dll
                MethodBridgeGeneratorCommand.GenerateMethodBridge(target);
                ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(target);
                AOTReferenceGeneratorCommand.GenerateAOTGenericReference(target);

                string aotDllDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
                ConfigStringList manifest = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotUpdateAOTDlls.asset", typeof(ConfigStringList)) as ConfigStringList;
                if (manifest == null)
                {
                    throw new Exception($"Assets/Res/Config/HotUpdateAOTDlls 配置不存在");
                }
                List<string> AOTMetaAssemblies = (manifest.List ?? Array.Empty<string>()).ToList();
                foreach (var dll in AOTMetaAssemblies)
                {
                    string dllPath = $"{aotDllDir}/{dll}";
                    if (!File.Exists(dllPath))
                    {
                        Debug.LogError($"ab中添加AOT补充元数据dll:{dllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。");
                        continue;
                    }
                    string dllBytesPath = $"{YooAssetCodeDll}/{dll}.bytes";
                    File.Copy(dllPath, dllBytesPath, true);
                    Debug.Log($"copy AOT dll {dllPath} -> {dllBytesPath}");
                }
            }

            GUILayout.Label("");
            GUILayout.Label("Build热更dll & ab:");
            this.globalConfig.CodeMode = CodeMode.Client;// (CodeMode)EditorGUILayout.EnumPopup("CodeMode: ", this.globalConfig.CodeMode);

            if (GUILayout.Button("BuildModelAndHotfix"))
            {
                BuildModelAndHotfix(this.codeOptimization);
                //用yooAsset打ab, 不用你了
                //AfterCompiling();
                ShowNotification("Build Model And Hotfix Success!");
            }

            if (GUILayout.Button("BuildAB by YooAsset"))
            {
                var target = EditorUserBuildSettings.activeBuildTarget;
                BuildHelper.BuildAB(target, null);
            }

            GUILayout.Label("");
            GUILayout.Label("Config Tools: =======================");
            if (GUILayout.Button("ExcelExporter"))
            {
                //Directory.Delete("Assets/Bundles/Config", true);
                ToolsEditor.ExcelExporter();
            }

            if (GUILayout.Button("Proto2CS"))
            {
                ToolsEditor.Proto2CS();
            }

            GUILayout.Space(5);
        }

        //在打包前删除战斗相关dll
        //因为战斗dll是需要支持热更的,放在plugin目录的话,所有程序集都会自动引用
        //为了排除战斗dll被主工程打进包带来的各种问题,删除之以防万一
        private static void RemoveBattlePluginDlls()
        {
            var list = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateBattleDlls.asset", typeof(ConfigStringList)) as ConfigStringList;
            if (list == null)
            {
                throw new Exception($"Assets/Res/Config/HotupdateBattleDlls 配置不存在");
            }
            foreach (var dll in list.List)
            {
                var file = Path.Combine(BattlePluginDir, dll);
                File.Delete(file);
                Debug.Log($"delete dll {file}");
            }
            AssetDatabase.Refresh();
        }

        private static void CopyHotfixdllToHybridDir()
        {
            var target = EditorUserBuildSettings.activeBuildTarget;
            ConfigKeyValueMap manifest = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateDlls.asset", typeof(ConfigKeyValueMap)) as ConfigKeyValueMap;
            if (manifest == null)
            {
                throw new Exception($"Assets/Res/Config/HotupdateDlls 配置不存在");
            }
            foreach (var kvs in manifest.keyValueMaps)
            {
                var bytes = Path.Combine(YooAssetCodeDll, $"{kvs.Key}.bytes");
                if (!File.Exists(bytes))
                {
                    Debug.LogError($"not exists: {bytes}");
                }
                else
                {
                    File.Copy(bytes, Path.Combine(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), kvs.Key), true);
                    Debug.Log($"copy hot dll {bytes} -> to dir: HotUpdateDlls");
                }
            }

            var list = AssetDatabase.LoadAssetAtPath("Assets/Res/Config/HotupdateBattleDlls.asset", typeof(ConfigStringList)) as ConfigStringList;
            if (list == null)
            {
                throw new Exception($"Assets/Res/Config/HotupdateBattleDlls 配置不存在");
            }
            foreach (var dll in list.List)
            {
                var bytes = Path.Combine(YooAssetCodeDll, $"{dll}.bytes");
                if (!File.Exists(bytes))
                {
                    Debug.LogError($"not exists: {bytes}");
                }
                else
                {
                    File.Copy(bytes, Path.Combine(SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target), dll), true);
                    Debug.Log($"copy hot dll {bytes} -> to dir: HotUpdateDlls");
                }
            }
        }

        public static void BuildModelAndHotfix(CodeOptimization codeOptimization = CodeOptimization.Release)
        {
            if (Define.EnableCodes)
            {
                throw new Exception("Remove macro 'ENABLE_CODES' first!");
            }
            var globalConfig = Resources.Load<GlobalConfig>("GlobalConfig");
            if (globalConfig.CodeMode == CodeMode.Server)
            {
                throw new Exception("Don't compile server here, open 'ET.sln' to compile!");
            }
            BuildAssembliesHelper.BuildModel(codeOptimization, globalConfig);
            BuildAssembliesHelper.BuildHotfix(codeOptimization, globalConfig);

            BuildAssembliesHelper.BuildMuteAssembly("Unity.Mono", new List<string>() { "Assets/Scripts/Codes/Mono/" }, Array.Empty<string>(), codeOptimization, globalConfig.CodeMode);
            File.Copy(Path.Combine(Define.BuildOutputDir, "Unity.Mono.dll"), Path.Combine(YooAssetCodeDll, $"Unity.Mono.dll.bytes"), true);
            File.Copy(Path.Combine(Define.BuildOutputDir, "Unity.Mono.pdb"), Path.Combine(YooAssetCodeDll, $"Unity.Mono.pdb.bytes"), true);
            Debug.Log("copy Unity.Mono.dll to Assets/Res/CodeDll success!");
        }

        //将model.dll打包成ab
        /*private static void AfterCompiling()
		{
			Directory.CreateDirectory(BuildAssembliesHelper.CodeDir);

			// 设置ab包
			AssetImporter assetImporter = AssetImporter.GetAtPath("Assets/Bundles/Code");
			assetImporter.assetBundleName = "Code.unity3d";
			AssetDatabase.SaveAssets();
			AssetDatabase.Refresh();
            
			Debug.Log("build success!");
		}*/

        public static void ShowNotification(string tips)
        {
            EditorWindow game = EditorWindow.GetWindow(typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView"));
            game?.ShowNotification(new GUIContent($"{tips}"));
        }
    }
}