using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Fort23.Core;
using Fort23.GameData;
using Fort23.Mono;
using Fort23.UTool;
using hirdParty.DownloadSystem;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip;
using UnityEditor;
using UnityEngine;

namespace Fort23.Editor
{
    public enum PlatformType
    {
        None,
        Android,
        IOS,
        PC,
        MacOS,
        WeixinMiniGame,
    }

    public enum BuildType
    {
        Development,
        Release
    }

    public class BuildEditor : EditorWindow
    {
        private const string relativeDirPrefix = "./Release";
        public static string versionFolder = "./Release/{0}/versionInfo.txt";
        public static string buildFolder = "./Release/{0}/{1}/Bundle/";
        public static string bundleFolder = "./Release/{0}/UnityBundle/";
        public static string buildDllFolder = "./Release/{0}/{1}/DLL/";
        private static readonly string assetConfigName = "assetConfig.txt";
        public static bool IsEnableASYNC;
        public PlatformType activePlatform;
        private BuildAssetBundleOptions buildAssetBundleOptions = BuildAssetBundleOptions.ChunkBasedCompression;
        private BuildOptions buildOptions;
        private BuildType buildType;
        private bool clearFolder;
        private bool isBuildExe;
        private bool isContainAB;
        private static bool isZipBundle;
        private PlatformType platformType;

        private void OnEnable()
        {
#if UNITY_ANDROID
            activePlatform = PlatformType.Android;
#elif UNITY_IOS
			activePlatform = PlatformType.IOS;
#elif UNITY_STANDALONE_WIN
            activePlatform = PlatformType.PC;
#elif UNITY_STANDALONE_OSX
			activePlatform = PlatformType.MacOS;
#else
			activePlatform = PlatformType.None;
#endif
            platformType = activePlatform;
        }

        private void OnGUI()
        {
            EditorGUILayout.LabelField("打包平台:");
            platformType = (PlatformType)EditorGUILayout.EnumPopup(platformType);
            clearFolder = EditorGUILayout.Toggle("清理资源文件夹: ", clearFolder);
            EditorGUILayout.LabelField("BuildAssetBundleOptions(可多选):");
            buildAssetBundleOptions =
                (BuildAssetBundleOptions)EditorGUILayout.EnumFlagsField(buildAssetBundleOptions);
            isContainAB = EditorGUILayout.Toggle("是否打包资源: ", isContainAB);
            isZipBundle = EditorGUILayout.Toggle("压缩资源包: ", isZipBundle);
            isBuildExe = EditorGUILayout.Toggle("是否打包EXE: ", isBuildExe);
            if (isBuildExe) buildType = (BuildType)EditorGUILayout.EnumPopup("BuildType: ", buildType);

            switch (buildType)
            {
                case BuildType.Development:
                    buildOptions = BuildOptions.Development | BuildOptions.AutoRunPlayer |
                                   BuildOptions.ConnectWithProfiler | BuildOptions.AllowDebugging;
                    break;
                case BuildType.Release:
                    buildOptions = BuildOptions.None;
                    break;
            }

            GUILayout.Space(5);


            IsEnableASYNC = EditorGUILayout.Toggle("是否激活异步模拟", IsEnableASYNC);

            if (GUILayout.Button("开始打包"))
            {
                if (platformType == PlatformType.None)
                {
                    ShowNotification(new GUIContent("请选择打包平台!"));
                    return;
                }

                if (platformType != activePlatform && isBuildExe)
                    switch (EditorUtility.DisplayDialogComplex("警告!",
                                $"当前目标平台为{activePlatform}, 如果切换到{platformType}, 可能需要较长加载时间", "切换", "取消", "不切换"))
                    {
                        case 0:
                            activePlatform = platformType;
                            break;
                        case 1: //取消
                            return;
                        case 2: //不切换
                            break;
                    }

                Build(platformType, buildAssetBundleOptions, buildOptions, isBuildExe,
                    isContainAB, clearFolder);
            }

            GUILayout.Space(5);
        }

        [MenuItem("AssetBundles/资源打包")]
        public static void ShowWindow()
        {
            EditorWindow window = GetWindow<BuildEditor>(true, "打包工具");
            window.minSize = new Vector2(420, 220);
            window.maxSize = new Vector2(700, 400);
        }


        private static void Build(PlatformType type, BuildAssetBundleOptions buildAssetBundleOptions,
            BuildOptions buildOptions, bool isBuildExe, bool isContainAB, bool clearFolder)
        {
           
            AssetDatabase.Refresh();
            var buildTarget = BuildTarget.StandaloneWindows;
            var exeName = "release";
            switch (type)
            {
                case PlatformType.PC:
                    buildTarget = BuildTarget.StandaloneWindows64;
                    exeName += ".exe";
                    break;
                case PlatformType.Android:
                    buildTarget = BuildTarget.Android;
                    exeName += ".apk";
                    break;
                case PlatformType.IOS:
                    buildTarget = BuildTarget.iOS;
                    break;
                case PlatformType.MacOS:
                    buildTarget = BuildTarget.StandaloneOSX;
                    break;
                case PlatformType.WeixinMiniGame:
                    buildTarget = BuildTarget.WeixinMiniGame;
                    break;
            }

         
            var build = string.Format(buildFolder, type,
                $"{1}.{1}.{1}");
            string bundlePath = string.Format(bundleFolder, type);

            if (clearFolder && Directory.Exists(bundlePath))
            {
                Directory.Delete(bundlePath, true);
                Directory.CreateDirectory(bundlePath);
            }
            else
            {
                Directory.CreateDirectory(bundlePath);
            }

            if (Directory.Exists(build))
            {
                Directory.Delete(build, true);
                Directory.CreateDirectory(build);
            }
            else
            {
                Directory.CreateDirectory(build);
            }

            // 版本号写入
            var version = string.Format(versionFolder, type);
            var versionInfo =
                $"version={1}\nbuildVersion={1}\nassetVersion={1}";
            if (!File.Exists(version)) File.Create(version).Close();

            File.WriteAllText(version, versionInfo);

            LogTool.Log("开始资源打包");
            LogTool.Log("资源标记");
            AssetBundleEditor.BuildAllBundle(buildTarget);
            LogTool.Log("资源标记完成");
            AssetBundleBuild[] buildMap = new AssetBundleBuild[1];
            
            BuildPipeline.BuildAssetBundles(bundlePath, buildAssetBundleOptions, buildTarget);
            var assetMd5Info = new AssetMD5Info(new List<MD5FileInfo>());
            var allBundles = Directory.GetFiles(bundlePath);
            foreach (var bundle in allBundles)
            {
                if (bundle.Contains(".manifest")) continue;
                var fileInfo = new FileInfo(bundle);
                byte[] data = File.ReadAllBytes(bundle);
                // data = DllTool.KeyEncryption(data);
                string p = build + fileInfo.Name;
                // Debug.Log("拷贝的路径" + p);
                File.WriteAllBytes(p, data);
                var md5Info = new MD5FileInfo();
                md5Info.md5 = MD5Helper.FileMD5(data);

                // var bundleFile = bundle.Replace(".manifest", "");


                md5Info.size = fileInfo.Length;
                md5Info.fileName = Path.GetFileName(bundle);
                assetMd5Info.fileInfo.Add(md5Info);
            }

            {
                var fold = string.Format(buildFolder, GetPlatformType(),
                    $"{1}.{1}.{1}");
                var assetConfigPath = fold + assetConfigName;
                var fileInfo = new FileInfo(assetConfigPath);
                byte[] data = File.ReadAllBytes(assetConfigPath);
                // data = DllTool.KeyEncryption(data);
                string p = build + fileInfo.Name;
                // Debug.Log("拷贝的路径" + p);
                File.WriteAllBytes(p, data);
                var md5Info = new MD5FileInfo();
                md5Info.md5 = MD5Helper.FileMD5(data);

                // var bundleFile = bundle.Replace(".manifest", "");


                md5Info.size = fileInfo.Length;
                md5Info.fileName = Path.GetFileName(assetConfigPath);
                assetMd5Info.fileInfo.Add(md5Info);
                
            }

            LogTool.Log("完成资源打包");
            var md5Json = JsonHelper.ToJson(assetMd5Info);
            File.WriteAllText(build + "/MD5.txt", md5Json);
            LogTool.Log("MD5文件生成完成");
            if (isContainAB)
            {
                if (isZipBundle)
                {
                    ZipBundle(build, Application.dataPath + "/StreamingAssets/assetBundle.zip");
                }
                else
                {
                    FileHelper.CopyDirectory(build, "Assets/StreamingAssets/Bundle/");
                    // 去掉.manifest文件
                    var files = Directory.GetFiles("Assets/StreamingAssets/Bundle/");
                    for (var i = 0; i < files.Length; i++)
                        if (files[i].Contains(".manifest"))
                            File.Delete(files[i]);

                    AssetDatabase.Refresh(ImportAssetOptions.Default);
                    LogTool.Log("全部资源复制到StreamingAssets完成");
                }

                // 暂时不清理StreamingAssets文件夹，有可能有自定义的一些文件放入其中，被清理了会出问题
                // FileHelper.CleanDirectory("Assets/StreamingAssets/");
            }

            if (isBuildExe)
            {
                AssetDatabase.Refresh();
                LogTool.Log("开始EXE打包");
                SaveGroup();
                BuildPipeline.BuildPlayer(EditorBuildSettings.scenes, $"{relativeDirPrefix}/{exeName}", buildTarget, buildOptions);
                LogTool.Log("完成exe打包");
                string url = Application.dataPath.Split("Assets")[0] + relativeDirPrefix.Split("./")[1];
                Application.OpenURL(url);
            }
        }

        /// <summary>
        /// 压缩bundle文件
        /// </summary>
        private static void ZipBundle(string FolderToZip, string targetZipFile)
        {
            ZipOutputStream s = new ZipOutputStream(File.Create(targetZipFile));
            s.SetLevel(1);
            bool res = ZipFileDictory(FolderToZip, s, "");

            s.Finish();
            s.Close();
            string assetMd5 = Application.dataPath + "/StreamingAssets/assetMD5.txt";

            File.WriteAllText(assetMd5, DateTime.Now.Ticks.ToString());
            Debug.Log("压缩完成" + assetMd5);
        }

        /// <summary>
        /// 递归压缩文件夹方法
        /// </summary>
        private static bool ZipFileDictory(string FolderToZip, ZipOutputStream s, string ParentFolderName)
        {
            bool res = true;
            string[] folders, filenames;
            ZipEntry entry = null;
            FileStream fs = null;
            Crc32 crc = new Crc32();
            try
            {
                entry = new ZipEntry("Bundle/");
                s.PutNextEntry(entry);
                s.Flush();
                filenames = Directory.GetFiles(FolderToZip);
                foreach (string file in filenames)
                {
                    if (file.Contains(".manifest"))
                    {
                        continue;
                    }

                    fs = File.OpenRead(file);
                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(file)));
                    entry.DateTime = DateTime.Now;
                    entry.Size = fs.Length;
                    fs.Close();
                    crc.Reset();
                    crc.Update(buffer);
                    entry.Crc = crc.Value;
                    s.PutNextEntry(entry);
                    s.Write(buffer, 0, buffer.Length);
                }
            }
            catch
            {
                res = false;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                    fs = null;
                }

                if (entry != null)
                {
                    entry = null;
                }

                GC.Collect();
                GC.Collect(1);
            }

            folders = Directory.GetDirectories(FolderToZip);
            foreach (string folder in folders)
            {
                if (!ZipFileDictory(folder, s, Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip))))
                {
                    return false;
                }
            }

            return res;
        }

        public static void SaveGroup()
        {
            string gorup;
            List<string> groups = new List<string>();
#if UNITY_IOS
            gorup = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS);
            groups = gorup.Split(';').ToList();
#elif UNITY_STANDALONE_WIN
            gorup = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
            groups = gorup.Split(';').ToList();
#elif UNITY_ANDROID
            gorup = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
            groups = gorup.Split(';').ToList();
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
            gorup = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
            groups = gorup.Split(';').ToList();
#endif
            if (IsEnableASYNC)
                if (!groups.Contains("ASYNC"))
                    groups.Add("ASYNC");


#if UNITY_IOS
            PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, groups.SplitToString(";"));
#elif UNITY_STANDALONE_WIN
            PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, groups.SplitToString(";"));
#elif UNITY_ANDROID
            PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, groups.SplitToString(";"));
#endif
        }

        public static PlatformType GetPlatformType()
        {
#if UNITY_ANDROID
            return PlatformType.Android;
#elif UNITY_IOS
			return PlatformType.IOS;
#elif UNITY_STANDALONE_WIN
            return PlatformType.PC;
#elif UNITY_STANDALONE_OSX
			return PlatformType.MacOS;
#else
			return PlatformType.None;
#endif
        }
    }
}