//#if UNITY_EDITOR
//using UnityEditor;
//#endif
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine.Rendering;

public enum BuildTarget
{
    Android,
    iOS,
    WebGL,
    WSAPlayer,
    StandaloneWindows64,
    StandaloneOSX,
    StandaloneLinux64,
    ServerStandaloneWindows64,
    ServerStandaloneOSX,
    ServerStandaloneLinux64,
}

public class BuildAssetBundlesExample
{
    private const string TargetPrefKey = "assetBundleTarget_Enable";

    #region Set Build Target

    private static ulong buildTargetFlag
    {
        get
        {
            string value = EditorPrefs.GetString(TargetPrefKey, "");
            if (string.IsNullOrEmpty(value)) return 0ul;
            return Convert.ToUInt64(value, 2);
        }
        set
        {
            char[] bits = new char[64];

            for (int i = 63; i >= 0; i--)
            {
                bits[63 - i] = ((value & (1UL << i)) != 0) ? '1' : '0';
            }

            string ustring = new string(bits);
            EditorPrefs.SetString(TargetPrefKey, ustring);
        }
    }

    private static BuildTarget[] GetCurrentEnableBuildTarget()
    {
        List<BuildTarget> buildTargets = new List<BuildTarget>();

        for (int i = 0; i <= 63; i++)
        {
            if ((buildTargetFlag & (1ul << i)) != 0)
            {
                buildTargets.Add((BuildTarget)i);
            }
        }

        return buildTargets.ToArray();
    }

    [MenuItem("Asset Bundle/Build Target/Android", priority = 1)]
    static void ToggleAndroidBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.Android;
    }

    [MenuItem("Asset Bundle/Build Target/Android", true)]
    static bool ToggleAndroidBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/Android", (buildTargetFlag & (1ul << (int)BuildTarget.Android)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/iOS", priority = 2)]
    static void ToggleiOSBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.iOS;
    }

    [MenuItem("Asset Bundle/Build Target/iOS", true)]
    static bool ToggleiOSBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/iOS", (buildTargetFlag & (1ul << (int)BuildTarget.iOS)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/WebGL", priority = 3)]
    static void ToggleWebGLBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.WebGL;
    }

    [MenuItem("Asset Bundle/Build Target/WebGL", true)]
    static bool ToggleWebGLBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/WebGL", (buildTargetFlag & (1ul << (int)BuildTarget.WebGL)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/WSAPlayer (Window Store App Player)", priority = 4)]
    static void ToggleWSAPlayerBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.WSAPlayer;
    }

    [MenuItem("Asset Bundle/Build Target/WSAPlayer (Window Store App Player)", true)]
    static bool ToggleWSAPlayerBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/WSAPlayer (Window Store App Player)", (buildTargetFlag & (1ul << (int)BuildTarget.WSAPlayer)) != 0);
        return true;
    }

    #region Build Target Standalone

    [MenuItem("Asset Bundle/Build Target/Window/Standalone", priority = 5)]
    static void ToggleWindowBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.StandaloneWindows64;
    }

    [MenuItem("Asset Bundle/Build Target/Window/Standalone", true)]
    static bool ToggleWindowBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/Window/Standalone", (buildTargetFlag & (1ul << (int)BuildTarget.StandaloneWindows64)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/MacOS/Standalone", priority = 7)]
    static void ToggleMacOSBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.StandaloneOSX;
    }

    [MenuItem("Asset Bundle/Build Target/MacOS/Standalone", true)]
    static bool ToggleMacOSBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/MacOS/Standalone", (buildTargetFlag & (1ul << (int)BuildTarget.StandaloneOSX)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/Linux/Standalone", priority = 9)]
    static void ToggleLinuxBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.StandaloneLinux64;
    }

    [MenuItem("Asset Bundle/Build Target/Linux/Standalone", true)]
    static bool ToggleLinuxBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/Linux/Standalone", (buildTargetFlag & (1ul << (int)BuildTarget.StandaloneLinux64)) != 0);
        return true;
    }

    #endregion

    #region Build Target Server

    [MenuItem("Asset Bundle/Build Target/Window/Server", priority = 6)]
    static void ToggleServerWindowBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.ServerStandaloneWindows64;
    }

    [MenuItem("Asset Bundle/Build Target/Window/Server", true)]
    static bool ToggleServerWindowBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/Window/Server", (buildTargetFlag & (1ul << (int)BuildTarget.ServerStandaloneWindows64)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/MacOS/Server", priority = 8)]
    static void ToggleServerMacOSBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.ServerStandaloneOSX;
    }

    [MenuItem("Asset Bundle/Build Target/MacOS/Server", true)]
    static bool ToggleServerMacOSBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/MacOS/Server", (buildTargetFlag & (1ul << (int)BuildTarget.ServerStandaloneOSX)) != 0);
        return true;
    }

    [MenuItem("Asset Bundle/Build Target/Linux/Server", priority = 10)]
    static void ToggleServerLinuxBuildTarget()
    {
        buildTargetFlag ^= 1ul << (int)BuildTarget.ServerStandaloneLinux64;
    }

    [MenuItem("Asset Bundle/Build Target/Linux/Server", true)]
    static bool ToggleServerLinuxBuildTargetValidate()
    {
        Menu.SetChecked("Asset Bundle/Build Target/Linux/Server", (buildTargetFlag & (1ul << (int)BuildTarget.ServerStandaloneLinux64)) != 0);
        return true;
    }

    #endregion

    #endregion

    // Build ---------------------------------------------------------------------
    [MenuItem("Asset Bundle/Clean All Asset Bundle Name Set", priority = 17)]
    static void Clean_BundleNameSet()
    {
        string[] selectedBundleGUIDs = Selection.assetGUIDs;
        List<AssetImporter> selectedAssetBundles = GetSelectedBundles(selectedBundleGUIDs);

        if (selectedAssetBundles.Count == 0)
        {
            //Clean all
            string[] assetPaths = AssetDatabase.GetAllAssetPaths();

            foreach (string assetPath in assetPaths)
            {
                AssetImporter assetImporter = AssetImporter.GetAtPath(assetPath);
                if (assetImporter.assetBundleName != null && !assetPath.Contains(".cs"))
                {
                    assetImporter.assetBundleName = null;
                }
            }
        }
        else
        {
            //Clean selected bundle
            foreach (var s in selectedAssetBundles)
            {
                s.assetBundleName = null;
            }
        }
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

    [MenuItem("Asset Bundle/Set Recomment Graphic API")]
    static void SetRecommentGraphicAPI()
    {
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.Android, new GraphicsDeviceType[] { GraphicsDeviceType.OpenGLES3 });
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.iOS, new GraphicsDeviceType[] { GraphicsDeviceType.Metal });
        //PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.WebGL, new GraphicsDeviceType[] { GraphicsDeviceType.WebGL2 });
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.WSAPlayer, new GraphicsDeviceType[] { GraphicsDeviceType.Direct3D11, GraphicsDeviceType.Direct3D12 });
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.StandaloneWindows64, new GraphicsDeviceType[] { GraphicsDeviceType.Direct3D11, GraphicsDeviceType.Direct3D12 });
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.StandaloneOSX, new GraphicsDeviceType[] { GraphicsDeviceType.Metal });
        PlayerSettings.SetGraphicsAPIs(UnityEditor.BuildTarget.StandaloneLinux64, new GraphicsDeviceType[] { GraphicsDeviceType.OpenGLCore });
    }

    [MenuItem("Asset Bundle/Build Selected Asset Bundle", priority = 18)]
    static void Builds_OptimizeTime_EditorView()
    {
        string[] selectedBundleGUIDs = Selection.assetGUIDs;

        List<AssetImporter> selectedAssetBundles = GetSelectedBundles(selectedBundleGUIDs);

        if (selectedAssetBundles.Count > 1 || selectedAssetBundles.Count == 0)
        {
            UnityEngine.Debug.Log("Please select only 1 prefab");
            return;
        }

        if (string.IsNullOrEmpty(selectedAssetBundles[0].assetBundleName))
        {
            UnityEngine.Debug.Log("Please enter asset bundle name");
            return;
        }

        string buildLocation = CreateBuildLocation(selectedAssetBundles[0].assetBundleName);
        CreateBuildLocationDirectories(buildLocation);

        BuildSelectedTarget(buildLocation, selectedAssetBundles[0].assetBundleName,
                        GetCurrentEnableBuildTarget());

        if (!BuildPipeline.isBuildingPlayer)
        {
            RelocateFinalFiles(buildLocation, selectedAssetBundles[0].assetBundleName,
                GetCurrentEnableBuildTarget());

            DeleteBuildLocationDirectories(buildLocation);
            Process.Start(Path.GetFullPath(buildLocation));
        }
    }

    // Common ---------------------------------------------------------------------
    private static void CleanUnselectedBundleName(List<AssetImporter> selectedAssetBundles)
    {
        string[] assetPaths = AssetDatabase.GetAllAssetPaths();
        bool isNone = true;

        foreach (string assetPath in assetPaths)
        {
            AssetImporter assetImporter = AssetImporter.GetAtPath(assetPath);
            if (assetImporter.assetBundleName != null && !assetPath.Contains(".cs"))
            {
                foreach (var s in selectedAssetBundles)
                {
                    isNone = true;
                    if (s.assetBundleName.Equals(assetImporter.assetBundleName))
                    {
                        isNone = false;
                        break;
                    }
                }
                if (isNone)
                {
                    assetImporter.assetBundleName = null;
                }

            }
        }
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

    private static string CreateBuildLocation(string bundleName)
    {
        if (!Directory.Exists("./AssetBundleBuilds"))
        {
            Directory.CreateDirectory("./AssetBundleBuilds");
        }

        string buildLocation = "./AssetBundleBuilds/" + bundleName;
        return buildLocation;
    }
    private static List<AssetImporter> GetSelectedBundles(string[] selectedBundleGUIDs)
    {
        List<AssetImporter> selectedAssetBundles = new List<AssetImporter>();
        foreach (string bundleGUID in selectedBundleGUIDs)
        {
            // Get the path of the bundle asset
            string bundlePath = AssetDatabase.GUIDToAssetPath(bundleGUID);
            AssetImporter assetImporter = AssetImporter.GetAtPath(bundlePath);
            selectedAssetBundles.Add(assetImporter);
        }
        return selectedAssetBundles;
    }

    private static void BuildSelectedTarget(string buildLocation, string bundleName, BuildTarget[] targets)
    {
        AssetBundleBuild build = new AssetBundleBuild();
        build.assetBundleName = bundleName;
        build.assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
        foreach (BuildTarget target in targets)
        {
            switch (target)
            {
                case BuildTarget.Android:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/Android", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.Android);
                    break;
                case BuildTarget.iOS:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/IOS", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.iOS);
                    break;
                case BuildTarget.WebGL:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/WebGL", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.WebGL);
                    break;
                case BuildTarget.WSAPlayer:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/UWP", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.WSAPlayer);
                    break;
                case BuildTarget.StandaloneWindows64:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/Windows", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.StandaloneWindows64);
                    break;
                case BuildTarget.StandaloneOSX:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/MAC", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.StandaloneOSX);
                    break;
                case BuildTarget.StandaloneLinux64:
                    BuildPipeline.BuildAssetBundles(buildLocation + "/Linux", new AssetBundleBuild[] { build }, BuildAssetBundleOptions.None, UnityEditor.BuildTarget.StandaloneLinux64);
                    break;
                case BuildTarget.ServerStandaloneWindows64:
                    BuildAssetBundlesParameters serverWindowAssetBundleParameters = new BuildAssetBundlesParameters()
                    {
                        outputPath = buildLocation + "/ServerWindows",
                        options = BuildAssetBundleOptions.None,
                        bundleDefinitions = new[] { build },
                        targetPlatform = UnityEditor.BuildTarget.StandaloneWindows64,
                        subtarget = (int)StandaloneBuildSubtarget.Server,

                    };
                    BuildPipeline.BuildAssetBundles(serverWindowAssetBundleParameters);
                    break;
                case BuildTarget.ServerStandaloneOSX:
                    BuildAssetBundlesParameters serverMacAssetBundleParameters = new BuildAssetBundlesParameters()
                    {
                        outputPath = buildLocation + "/ServerMAC",
                        options = BuildAssetBundleOptions.None,
                        bundleDefinitions = new[] { build },
                        targetPlatform = UnityEditor.BuildTarget.StandaloneOSX,
                        subtarget = (int)StandaloneBuildSubtarget.Server,

                    };
                    BuildPipeline.BuildAssetBundles(serverMacAssetBundleParameters);
                    break;
                case BuildTarget.ServerStandaloneLinux64:
                    BuildAssetBundlesParameters serverLinuxAssetBundleParameters = new BuildAssetBundlesParameters()
                    {
                        outputPath = buildLocation + "/ServerLinux",
                        options = BuildAssetBundleOptions.None,
                        bundleDefinitions = new[] { build },
                        targetPlatform = UnityEditor.BuildTarget.StandaloneLinux64,
                        subtarget = (int)StandaloneBuildSubtarget.Server,

                    };
                    BuildPipeline.BuildAssetBundles(serverLinuxAssetBundleParameters);
                    break;
                default:
                    UnityEngine.Debug.Log("This platform is not support: " + target.ToString());
                    break;

            }
        }
    }

    private static void RelocateFinalFiles(string buildLocation, string bundleName, BuildTarget[] targets)
    {
        foreach (BuildTarget target in targets)
        {
            switch (target)
            {
                case BuildTarget.Android:
                    string androidBundlePath = buildLocation + "/" + bundleName + "_android";
                    string androidManifestPath = buildLocation + "/" + bundleName + "_android.manifest";
                    File.Copy(buildLocation + "/Android/" + bundleName, androidBundlePath);
                    File.Copy(buildLocation + "/Android/" + bundleName + ".manifest", androidManifestPath);
                    break;
                case BuildTarget.iOS:
                    string iosBundlePath = buildLocation + "/" + bundleName + "_ios";
                    string iosManifestPath = buildLocation + "/" + bundleName + "_ios.manifest";
                    File.Copy(buildLocation + "/IOS/" + bundleName, iosBundlePath);
                    File.Copy(buildLocation + "/IOS/" + bundleName + ".manifest", iosManifestPath);
                    break;
                case BuildTarget.WebGL:
                    string webglBundlePath = buildLocation + "/" + bundleName + "_webgl";
                    string webglManifestPath = buildLocation + "/" + bundleName + "_webgl.manifest";
                    File.Copy(buildLocation + "/WebGL/" + bundleName, webglBundlePath);
                    File.Copy(buildLocation + "/WebGL/" + bundleName + ".manifest", webglManifestPath);
                    break;
                case BuildTarget.WSAPlayer:
                    string uwpBundlePath = buildLocation + "/" + bundleName + "_uwp";
                    string uwpManifestPath = buildLocation + "/" + bundleName + "_uwp.manifest";
                    File.Copy(buildLocation + "/UWP/" + bundleName, uwpBundlePath);
                    File.Copy(buildLocation + "/UWP/" + bundleName + ".manifest", uwpManifestPath);
                    break;
                case BuildTarget.StandaloneWindows64:
                    string windowBundlePath = buildLocation + "/" + bundleName + "_windows";
                    string windowManifestPath = buildLocation + "/" + bundleName + "_windows.manifest";
                    File.Copy(buildLocation + "/Windows/" + bundleName, windowBundlePath);
                    File.Copy(buildLocation + "/Windows/" + bundleName + ".manifest", windowManifestPath);
                    break;
                case BuildTarget.StandaloneOSX:
                    string macBundlePath = buildLocation + "/" + bundleName + "_mac";
                    string macManifestPath = buildLocation + "/" + bundleName + "_mac.manifest";
                    File.Copy(buildLocation + "/MAC/" + bundleName, macBundlePath);
                    File.Copy(buildLocation + "/MAC/" + bundleName + ".manifest", macManifestPath);
                    break;
                case BuildTarget.StandaloneLinux64:
                    string linuxBundlePath = buildLocation + "/" + bundleName + "_linux";
                    string linuxManifestPath = buildLocation + "/" + bundleName + "_linux.manifest";
                    File.Copy(buildLocation + "/Linux/" + bundleName, linuxBundlePath);
                    File.Copy(buildLocation + "/Linux/" + bundleName + ".manifest", linuxManifestPath);
                    break;
                case BuildTarget.ServerStandaloneWindows64:
                    string serverWindowBundlePath = buildLocation + "/" + bundleName + "_windows_server";
                    string serverWwindowManifestPath = buildLocation + "/" + bundleName + "_windows_server.manifest";
                    File.Copy(buildLocation + "/ServerWindows/" + bundleName, serverWindowBundlePath);
                    File.Copy(buildLocation + "/ServerWindows/" + bundleName + ".manifest", serverWwindowManifestPath);
                    break;
                case BuildTarget.ServerStandaloneOSX:
                    string serverMacBundlePath = buildLocation + "/" + bundleName + "_mac_server";
                    string serverManifestPath = buildLocation + "/" + bundleName + "_mac_server.manifest";
                    File.Copy(buildLocation + "/ServerMAC/" + bundleName, serverMacBundlePath);
                    File.Copy(buildLocation + "/serverMAC/" + bundleName + ".manifest", serverManifestPath);
                    break;
                case BuildTarget.ServerStandaloneLinux64:
                    string serverLinuxBundlePath = buildLocation + "/" + bundleName + "_linux_server";
                    string serverLinuxManifestPath = buildLocation + "/" + bundleName + "_linux_server.manifest";
                    File.Copy(buildLocation + "/ServerLinux/" + bundleName, serverLinuxBundlePath);
                    File.Copy(buildLocation + "/ServerLinux/" + bundleName + ".manifest", serverLinuxManifestPath);
                    break;
                default:
                    UnityEngine.Debug.Log("This platform is not support: " + target.ToString());
                    break;
            }
        }
    }

    private static void CreateBuildLocationDirectories(string buildLocation)
    {
        if (Directory.Exists(buildLocation))
        {
            Directory.Delete(buildLocation, true);
        }
        Directory.CreateDirectory(buildLocation);
        Directory.CreateDirectory(buildLocation + "/Android");
        Directory.CreateDirectory(buildLocation + "/IOS");
        Directory.CreateDirectory(buildLocation + "/WebGL");
        Directory.CreateDirectory(buildLocation + "/UWP");
        Directory.CreateDirectory(buildLocation + "/Windows");
        Directory.CreateDirectory(buildLocation + "/MAC");
        Directory.CreateDirectory(buildLocation + "/Linux");
        Directory.CreateDirectory(buildLocation + "/ServerWindows");
        Directory.CreateDirectory(buildLocation + "/ServerMAC");
        Directory.CreateDirectory(buildLocation + "/ServerLinux");
    }

    private static void DeleteBuildLocationDirectories(string buildLocation)
    {
        Directory.Delete(buildLocation + "/Android", true);
        Directory.Delete(buildLocation + "/IOS", true);
        Directory.Delete(buildLocation + "/WebGL", true);
        Directory.Delete(buildLocation + "/UWP", true);
        Directory.Delete(buildLocation + "/Windows", true);
        Directory.Delete(buildLocation + "/MAC", true);
        Directory.Delete(buildLocation + "/Linux", true);
        Directory.Delete(buildLocation + "/ServerWindows", true);
        Directory.Delete(buildLocation + "/ServerMAC", true);
        Directory.Delete(buildLocation + "/ServerLinux", true);
    }
}