// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license. 

using Newtonsoft.Json.Linq;
using SimplygonUI;
using SimplygonUnityEditorPlugin.Logger;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditor.PackageManager.Requests;
using UnityEngine;

namespace Simplygon.Unity.EditorPlugin
{
    public class SimplygonObserver : Observer
    {
        private ThreadDispatcher MainThreadDispatcher = null;

        public SimplygonObserver(ThreadDispatcher mainThreadDispatcher)
        {
            MainThreadDispatcher = mainThreadDispatcher;
        }

        public override bool OnProgress(spObject subject, float progressPercent)
        {
            // Note: Posting progress on non-main thread results in deadlock.
            // Currently there is no way to get the main thread to update during progress.
            // To avoid further issues, only post progress that is running on the same thread.
            // Only affects progress reported by Pipelines with RunMode RunInThisProcess.
            if (MainThreadDispatcher.IsOnSameThread())
            {
                EditorUtility.DisplayProgressBar("Simplygon", "Please wait while the asset is optimized.", progressPercent / 100.0f);
            }

            return true;
        }
    }

    public enum Tabs
    {
        Processing = 0,
        Log
    }

    public class SimplygonWindow : EditorWindow, ISimplygonPipelineDropdownCallback, ISimplygonIntegrationCallback
    {
        private global::Simplygon.EErrorCodes simplygonErrorCode = 0;
        private string simplygonErrorMessage = string.Empty;

        private Vector2 simplygonPipelineSettingsScrollPosition = Vector2.zero;
        private List<SimplygonPipeline> pipelines = new List<SimplygonPipeline>();

        private Rect addLODComponentRect;
        private bool editPipeline;
        private bool isUSDPackage20Installed;
        private bool isUSDPackage30Installed;
        private bool isUSDPackage300exp2Installed;
        private ListRequest packageRequest;
        private List<GameObject> selectedGameObjects = new List<GameObject>();

        private Texture enabledSimplygonTexture = null;
        private Texture disabledSimplygonTexture = null;

        private EPipelineRunMode PipelineRunMode = EPipelineRunMode.RunInNewProcess;

        private int selectedTriangleCount = 0;
        private int selectedMeshCount = 0;
        private int selectedMaterialCount = 0;

        private GUIStyle titleStyle = null;
        private GUIStyle statusBarStyle = null;

        private int selectedTab = (int)Tabs.Processing;
        private string[] uiTabs = { Tabs.Processing.ToString(), Tabs.Log.ToString() };

        private SimplygonLoggerUI simplygonLoggerView = null;
        private SimplygonLoggerSingleLineView simplygonLoggerSingleLineView = null;

        private ThreadDispatcher mainThreadDispatcher = null;

        private bool isCustomStylesInitialized = false;

        public SimplygonWindow()
        {
            simplygonLoggerView = new SimplygonLoggerUI(SimplygonLogger.Instance);
            simplygonLoggerSingleLineView = new SimplygonLoggerSingleLineView(this, SimplygonLogger.Instance);

            EditorApplication.playModeStateChanged += EditorModeChanged;
        }

        private void Log(Category category, string message)
        {
            SimplygonLogger.Instance.Log(category, message);

            if (category == Category.Error)
            {
                Debug.LogError(message);
            }
            else if (category == Category.Warning)
            {
                Debug.LogWarning(message);
            }
            else if (category == Category.Information)
            {
                Debug.Log(message);
            }
            else
            {
                //Debug.LogException(message);
            }
        }

        void EditorModeChanged(PlayModeStateChange playModeState)
        {
            if (playModeState == PlayModeStateChange.EnteredEditMode)
            {
                enabledSimplygonTexture = SimplygonUtils.LoadSimplygonLogo();
                disabledSimplygonTexture = SimplygonUtils.LoadTransparentSimplygonLogo();
            }
        }

        [MenuItem("Window/Simplygon")]
        static void Init()
        {
            var simplygonWindow = EditorWindow.GetWindow(typeof(SimplygonWindow), false, "Simplygon");
        }

        private void InitializeCustomStyles()
        {
            isCustomStylesInitialized = true;

            if (titleStyle == null)
            {
                titleStyle = new GUIStyle(EditorStyles.boldLabel)
                {
                    alignment = TextAnchor.MiddleLeft,
                    fixedHeight = 25.0f
                };
            }

            if (statusBarStyle == null)
            {
                statusBarStyle = new GUIStyle(EditorStyles.label)
                {
                    margin = new RectOffset(2, 0, 0, 0),
                    padding = new RectOffset(0, 0, 0, 0),
                    fixedHeight = 20f
                };
            }
        }

        public void OnEnable()
        {
            mainThreadDispatcher = new ThreadDispatcher();
            packageRequest = UnityEditor.PackageManager.Client.List();

            isUSDPackage20Installed = false;
            isUSDPackage30Installed = false;
            isUSDPackage300exp2Installed = false;

            enabledSimplygonTexture = SimplygonUtils.LoadSimplygonLogo();
            disabledSimplygonTexture = SimplygonUtils.LoadTransparentSimplygonLogo();

            SimplygonPipelineDatabase.Refresh();
            foreach (var pipeline in pipelines)
            {
                pipeline.OnUIEnable(this);
            }

            simplygonLoggerView.OnUIEnable();

            //Log(Category.Information, "This is information.");
            //Log(Category.Warning, "This is warning.");
            //Log(Category.Error, "This is error.");
        }

        public void OnDestroy()
        {
        }

        public void Update()
        {
        }

        public void OnSelectionChange()
        {
            Repaint();
        }

        void OnLostFocus()
        {
            EditorUtility.ClearProgressBar();
        }

        public void OnAddMaterialCaster(object materialCasterInfo)
        {

        }

        public void RemovePipeline(object pipeline)
        {
            pipelines.Remove((SimplygonPipeline)pipeline);
        }

        public void NewLODComponentSelected(AdvancedDropdownItem item)
        {
            if (item is SimplygonPipelineDropdownItem)
            {
                var pipelineItem = ((SimplygonPipelineDropdownItem)item);

                if (!string.IsNullOrEmpty(pipelineItem.Target.FilePath))
                {
                    var newPipeline = new SimplygonPipeline(pipelineItem.Target.FilePath, JObject.Parse(System.IO.File.ReadAllText(pipelineItem.Target.FilePath)));

                    if (newPipeline != null)
                    {
                        if (newPipeline.PipelineType == ESimplygonPipeline.Passthrough)
                        {
                            foreach (var cascadedPipeline in newPipeline.CascadedPipelines)
                            {
                                cascadedPipeline.InitializeUI();
                                cascadedPipeline.SetParentCallback(this);
                                cascadedPipeline.OnUIEnable(this);
                                pipelines.Add(cascadedPipeline);
                            }
                        }
                        else
                        {
                            newPipeline.InitializeUI();
                            newPipeline.SetParentCallback(this);
                            newPipeline.OnUIEnable(this);
                            pipelines.Add(newPipeline);
                        }
                    }
                }
                else
                {
                    var newPipeline = pipelineItem.Target.DeepCopy();
                    newPipeline.InitializeUI();
                    newPipeline.SetParentCallback(this);
                    newPipeline.OnUIEnable(this);
                    pipelines.Add(newPipeline);

                    if (newPipeline.AutomaticMaterialCastersFromSelection)
                    {
                        newPipeline.UpdateAutomaticMaterialCasters();
                    }
                }
            }
        }
        public void NewMaterialCasterSelected(SimplygonMaterialCaster materialCaster)
        {

        }

        public void RemoveAllMaterialCasters()
        {
        }

        public void NewCascadedLODComponentSelected(SimplygonPipeline pipeline)
        {

        }

        public bool IsLODComponentDropdownEnabled()
        {
            return true;
        }
        public bool IsCascadedLODComponentDropdownEnabled()
        {
            return false;
        }
        public bool IsMaterialCasterComponentDropdownEnabled()
        {
            return false;
        }

        public bool IsPipelineMenuEnabled()
        {
            return false;
        }

        public bool IsRemoveButtonEnabled()
        {
            return true;
        }

        public void SavePipeline()
        {
            SaveAsPipeline(string.Empty, true);
        }

        public void SaveAsPipeline(string filePath, bool serializeUICompontents = true)
        {
            SimplygonPipeline outputPipeline = null;
            if (pipelines.Count > 1)
            {
                outputPipeline = new SimplygonPipeline(ESimplygonPipeline.Passthrough, string.Empty, false);

                foreach (var pipeline in pipelines)
                {
                    outputPipeline.CascadedPipelines.Add(pipeline);
                }

                outputPipeline.FilePath = pipelines.First().FilePath;
            }
            else if (pipelines.Count == 1)
            {
                outputPipeline = pipelines[0];
            }

            if (string.IsNullOrEmpty(filePath))
            {
                filePath = outputPipeline.FilePath;
            }

            if (string.IsNullOrEmpty(filePath))
            {
                string pipelinePath = EditorUtility.SaveFilePanel($"Save pipeline", SimplygonPipelineDatabase.PipelineDirectory, $"MyPipeline.json", "json");

                if (!string.IsNullOrEmpty(pipelinePath))
                {
                    try { Directory.CreateDirectory(Path.GetDirectoryName(pipelinePath)); } catch { }
                    File.WriteAllText(pipelinePath, outputPipeline.SaveJson(serializeUICompontents).ToString());
                    SimplygonPipelineDatabase.Refresh();
                }
                else
                {
                    Log(Category.Error, "Could not save pipeline due to an invalid (null or empty) file path.");
                }
            }
            else
            {

                try { Directory.CreateDirectory(Path.GetDirectoryName(filePath)); } catch { }
                File.WriteAllText(filePath, outputPipeline.SaveJson(serializeUICompontents).ToString());
                SimplygonPipelineDatabase.Refresh();
            }
        }

        public void ToggleEditPipeline()
        {
            editPipeline = !editPipeline;

            foreach (var pipeline in pipelines)
            {
                pipeline.SetEditMode(editPipeline);
            }
        }
#if LEGACYSETTINGSSUPPORT
        public void ImportLegacySettings()
        {
            string settingsFilePath = EditorUtility.OpenFilePanelWithFilters($"Import legacy Simplygon settings", null, new string[] { "Simplygon Settings Files(*.spl;*.ini)", "spl,ini" });

            if (!string.IsNullOrEmpty(settingsFilePath))
            {
                try
                {
                    SimplygonPipeline pipeline = null;

                    if (System.IO.Path.GetExtension(settingsFilePath).ToLower() == ".spl")
                    {
                        pipeline = SimplygonUI.SPL.Importer.Import(settingsFilePath);
                    }
                    else if (System.IO.Path.GetExtension(settingsFilePath).ToLower() == ".ini")
                    {
                        pipeline = SimplygonUI.INI.Importer.Import(settingsFilePath);
                    }

                    if (pipeline != null)
                    {
                        pipelines.Clear();
                        if (pipeline.PipelineType == ESimplygonPipeline.Passthrough)
                        {
                            foreach (var cascadedPipeline in pipeline.CascadedPipelines)
                            {
                                cascadedPipeline.InitializeUI();
                                cascadedPipeline.SetParentCallback(this);
                                cascadedPipeline.OnUIEnable(this);
                                pipelines.Add(cascadedPipeline);
                            }

                        }
                        else
                        {
                            pipeline.InitializeUI();
                            pipeline.SetParentCallback(this);
                            pipeline.OnUIEnable(this);
                            pipelines.Add(pipeline);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log(Category.Error, "Could not import legacy settings file (" + settingsFilePath != null ? settingsFilePath : "null" + ").\n\n Details: " + ex);
                }
            }
        }
#endif
        public void LoadPipeline()
        {
            string pipelineFilePath = EditorUtility.OpenFilePanelWithFilters($"Load Simplygon pipeline", null, new string[] { "Simplygon Pipeline Files(*.json)", "json" });

            if (!string.IsNullOrEmpty(pipelineFilePath))
            {
                try
                {
                    dynamic jsonData = JObject.Parse(System.IO.File.ReadAllText(pipelineFilePath));

                    var pipeline = new SimplygonPipeline(pipelineFilePath, jsonData);

                    if (pipeline != null)
                    {
                        pipelines.Clear();
                        if (pipeline.PipelineType == ESimplygonPipeline.Passthrough)
                        {
                            foreach (var cascadedPipeline in pipeline.CascadedPipelines)
                            {
                                cascadedPipeline.InitializeUI();
                                cascadedPipeline.SetParentCallback(this);
                                cascadedPipeline.OnUIEnable(this);
                                pipelines.Add(cascadedPipeline);
                            }

                        }
                        else
                        {
                            pipeline.InitializeUI();
                            pipeline.SetParentCallback(this);
                            pipeline.OnUIEnable(this);
                            pipelines.Add(pipeline);

                        }
                    }
                }
                catch (Exception ex)
                {
                    string errorMessage = "Could not load the following pipeline (";
                    errorMessage += pipelineFilePath ?? "null";
                    errorMessage += ") due to an error.\n\n Details: ";
                    errorMessage += ex;

                    Log(Category.Error, errorMessage);
                }
            }
        }

        public void ExportPipeline()
        {
            string pipelinePath = EditorUtility.SaveFilePanel($"Export pipeline", SimplygonPipelineDatabase.PipelineDirectory, $"MyPipeline.json", "json");

            if (!string.IsNullOrEmpty(pipelinePath))
            {
                SaveAsPipeline(pipelinePath, false);
            }
            else
            {
                Log(Category.Error, "Could not export the pipeline to the following path (" + pipelinePath != null ? pipelinePath : "null" + "), please make sure that the path is valid and that Unity has the required permissions.");
            }
        }

        public void InstallLicense()
        {
            try
            {
                string simplygon9Path = Environment.GetEnvironmentVariable("SIMPLYGON_9_PATH");

                if (!string.IsNullOrEmpty(simplygon9Path))
                {
                    simplygon9Path = Environment.ExpandEnvironmentVariables(simplygon9Path);

                    System.Diagnostics.Process.Start($@"{simplygon9Path}/SimplygonLicenseApplication.exe");
                }
            }
            catch (Exception ex)
            {
                Log(Category.Error, "InstallLicense returned the following error.\n\n Details: " + ex);
            }
        }

        internal void GoToLogTab()
        {
            selectedTab = (int)Tabs.Log;
            Repaint();
        }

        public void VisitDocumentation()
        {
            System.Diagnostics.Process.Start($@"https://documentation.simplygon.com/SimplygonSDK_{SimplygonVersion.Build}");
        }

        public void OnGUI()
        {
            if (!isCustomStylesInitialized)
            {
                InitializeCustomStyles();
            }

            UpdateSelection();
            pipelines.RemoveAll(i => i.PendingPipelineRemoval);

            EditorGUIUtility.labelWidth = 300;

            GUILayout.BeginHorizontal();
            selectedTab = GUILayout.Toolbar(selectedTab, uiTabs);
            GUILayout.EndHorizontal();

            if ((Tabs)selectedTab == Tabs.Processing)
            {
                GUILayout.BeginVertical();
                GUILayout.BeginHorizontal();
                GUILayout.Label(pipelines.Count > 0 ? "Simplygon Pipeline" : "Simplygon Pipeline - Please add a LOD component to get started.", titleStyle);
                GUILayout.FlexibleSpace();
                if (GUILayout.Button(EditorGUIUtility.IconContent("_Popup", ""), GUILayout.Height(25.0f), GUILayout.Width(25.0f)))
                {
                    string editPipelineTooltip = "Customize the pipeline by hiding UI elements and changing default values.";
                    string savePipelineTooltip = @"If you save the pipeline to the default location (%USERPROFILE%\Documents\Simplygon\9\Pipelines) it will be available as a LOD component automatically.";
#if LEGACYSETTINGSSUPPORT
                    string importLegacySettingsTooltip = "Import legacy .SPL and .ini settings.";
#endif
                    string loadPipelineTooltip = @"Load pipeline not in the default location (%USERPROFILE%\Documents\Simplygon\9\Pipelines).";
                    string exportPipelineTooltip = "Export pipeline and remove all UI metadata from the JSON file. Recommended if you plan to use the pipeline without using the Simplygon UI.";

                    GenericMenu pipelineMenu = new GenericMenu();
                    if (pipelines.Count > 0)
                    {
                        pipelineMenu.AddItem(new GUIContent($"Edit pipeline", editPipelineTooltip), editPipeline, ToggleEditPipeline);
                        pipelineMenu.AddItem(new GUIContent($"Save pipeline", savePipelineTooltip), false, SavePipeline);
#if LEGACYSETTINGSSUPPORT
                        pipelineMenu.AddItem(new GUIContent($"Import legacy settings", importLegacySettingsTooltip), false, ImportLegacySettings);
#endif
                        pipelineMenu.AddItem(new GUIContent($"Load pipeline", loadPipelineTooltip), false, LoadPipeline);
                        pipelineMenu.AddItem(new GUIContent($"Export pipeline"), false, ExportPipeline);
                    }
                    else
                    {
                        pipelineMenu.AddDisabledItem(new GUIContent($"Edit pipeline", editPipelineTooltip), editPipeline);
                        pipelineMenu.AddDisabledItem(new GUIContent($"Save pipeline", savePipelineTooltip), false);
#if LEGACYSETTINGSSUPPORT
                        pipelineMenu.AddItem(new GUIContent($"Import legacy settings", importLegacySettingsTooltip), false, ImportLegacySettings);
#endif
                        pipelineMenu.AddItem(new GUIContent($"Load pipeline", loadPipelineTooltip), false, LoadPipeline);
                        pipelineMenu.AddDisabledItem(new GUIContent($"Export pipeline", exportPipelineTooltip), false);
                    }

                    pipelineMenu.AddItem(new GUIContent($"Install license"), false, InstallLicense);
                    pipelineMenu.AddItem(new GUIContent($"Visit documentation"), false, VisitDocumentation);
                    pipelineMenu.ShowAsContext();
                }
                GUILayout.EndHorizontal();
                GUILayout.EndVertical();

                if (pipelines.Count > 0)
                {
                    GUILayout.Label("Unity settings", titleStyle);
                    GUILayout.BeginHorizontal();
                    EditorGUILayout.Space(15, false);
                    PipelineRunMode = (EPipelineRunMode)EditorGUILayout.EnumPopup("Run Mode", PipelineRunMode);
                    GUILayout.EndHorizontal();
                    EditorGUILayout.Space(5, false);
                    GUILayout.BeginVertical();
                    simplygonPipelineSettingsScrollPosition = EditorGUILayout.BeginScrollView(simplygonPipelineSettingsScrollPosition);
                    foreach (var pipeline in pipelines)
                    {
                        pipeline.ShowUI();
                        EditorGUILayout.Space(5, false);
                    }

                    GUILayout.BeginVertical();
                    EditorGUILayout.Space(15, false);
                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    bool addLODComponent = GUILayout.Button("Add LOD Component", GUILayout.Height(25.0f), GUILayout.Width(250.0f));

                    if (Event.current.type == EventType.Repaint)
                    {
                        addLODComponentRect = GUILayoutUtility.GetLastRect();
                    }
                    if (addLODComponent)
                    {
                        var dropdown = new SimplygonPipelineDropdown(this);
                        dropdown.Show(addLODComponentRect);
                    }
                    GUILayout.FlexibleSpace();
                    GUILayout.EndHorizontal();
                    GUILayout.EndVertical();

                    EditorGUILayout.EndScrollView();

                    GUILayout.EndVertical();

                    GUILayout.BeginVertical(EditorStyles.helpBox);
                    if (selectedGameObjects.Count > 0)
                    {
                        selectedGameObjects.RemoveAll(g => g == null);

                        EditorGUILayout.LabelField("Selected game objects:", EditorStyles.boldLabel);
                        foreach (var selectedObject in selectedGameObjects)
                        {
                            EditorGUILayout.LabelField(selectedObject.name);
                        }

                        EditorGUILayout.Space();
                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField($"Meshes: {selectedMeshCount}");
                        EditorGUILayout.LabelField($"Materials: {selectedMaterialCount}");
                        EditorGUILayout.LabelField($"Triangles: {selectedTriangleCount}");
                        EditorGUILayout.EndHorizontal();
                    }
                    else
                    {
                        EditorGUILayout.HelpBox("Please select game object(s) to process.", MessageType.Info);
                    }
                    if (!isUSDPackage20Installed && !isUSDPackage30Installed)
                    {
                        EditorGUILayout.HelpBox("Simplygon requires the USD 2.x or 3.x package. Please install it using the package manager.", MessageType.Error);
                    }
                    else if (!isUSDPackage30Installed)
                    {
                        EditorGUILayout.HelpBox("Simplygon recommends using the USD 3.x package. Please install it using the package manager.", MessageType.Warning);
                    }
                    else if (isUSDPackage300exp2Installed)
                    {
                        EditorGUILayout.HelpBox("Simplygon does not support USD 3.0.0-exp.2 package. Please install another version using the package manager.", MessageType.Warning);
                    }
                    
                    GUILayout.EndVertical();

                    if (packageRequest != null && packageRequest.IsCompleted)
                    {
                        isUSDPackage20Installed = packageRequest.Result.Where(i => i.name.ToLower().Contains("usd") && i.version.StartsWith("2.")).FirstOrDefault() != default;
                        isUSDPackage30Installed = packageRequest.Result.Where(i => i.name.ToLower().Contains("usd") && i.version.StartsWith("3.")).FirstOrDefault() != default;
                        isUSDPackage300exp2Installed = packageRequest.Result.Where(i => i.name.ToLower().Contains("usd") && i.version.StartsWith("3.0.0-exp.2")).FirstOrDefault() != default;
                        packageRequest = null;
                    }

                    if (selectedGameObjects.Count > 0 && (isUSDPackage20Installed || (isUSDPackage30Installed && !isUSDPackage300exp2Installed)))
                    {
                        if (GUILayout.Button(enabledSimplygonTexture, GUILayout.Height(100.0f)))
                        {
                            using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon(out simplygonErrorCode, out simplygonErrorMessage))
                            {
                                if (simplygon != null)
                                {
                                    try
                                    {
                                        simplygon.SendTelemetry("IntegrationInit", "Unity", Application.unityVersion, "{}");

                                        string tempPipelineFile = SimplygonUtils.GetTempPipelineFileName();
                                        SaveAsPipeline(tempPipelineFile, false);

                                        if (!string.IsNullOrEmpty(tempPipelineFile))
                                        {
                                            var progressObserver = new SimplygonObserver(mainThreadDispatcher);
                                            EditorUtility.DisplayProgressBar("Simplygon", "Please wait while the asset is optimized.", 0.0f);
                                            string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();
                                            using (spScene sgScene = SimplygonExporter.Export(simplygon, exportTempDirectory, selectedGameObjects))
                                            {
                                                string processingName = string.Join("_", selectedGameObjects.Select(p => p.name).Distinct().ToArray());

                                                if (sgScene != null && !sgScene.IsNull())
                                                {
                                                    using (spPipelineSerializer sgPipelineSerializer = simplygon.CreatePipelineSerializer())
                                                    using (spPipeline sgPipeline = sgPipelineSerializer.LoadPipelineFromFile(tempPipelineFile))
                                                    {
                                                        simplygon.SetGlobalDefaultTangentCalculatorTypeSetting(ETangentSpaceMethod.MikkTSpace);

                                                        sgPipeline.AddObserver(progressObserver);
                                                        sgPipeline.RunScene(sgScene, PipelineRunMode);

                                                        if (!AssetDatabase.IsValidFolder("Assets/LOD"))
                                                        {
                                                            AssetDatabase.CreateFolder("Assets", "LOD");
                                                        }
                                                        string assetFolderGuid = AssetDatabase.CreateFolder("Assets/LOD", processingName);
                                                        string assetFolderPath = AssetDatabase.GUIDToAssetPath(assetFolderGuid);

                                                        int lodIndex = 1;
                                                        SimplygonImporter.Import(simplygon, sgPipeline, ref lodIndex, assetFolderPath, processingName);
                                                    }
                                                }
                                                else
                                                {
                                                    Debug.LogError("Failed to export selection for Simplygon processing");
                                                }

                                                EditorUtility.ClearProgressBar();

                                                Directory.Delete(Path.GetDirectoryName(tempPipelineFile), true);
                                                Directory.Delete(exportTempDirectory, true);
                                            }
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Debug.LogException(ex);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        GUILayout.Button(disabledSimplygonTexture, GUILayout.Height(100.0f));
                    }
                    if (simplygonErrorCode != EErrorCodes.NoError)
                    {
                        EditorGUILayout.HelpBox(string.Format("Simplygon failed to load: {0} {1}", simplygonErrorCode, simplygonErrorMessage), MessageType.None);
                    }
                }
                else
                {
                    editPipeline = false;

                    GUILayout.BeginVertical();
                    GUILayout.Space(15);
                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    bool addLODComponent = GUILayout.Button("Add LOD Component", GUILayout.Height(25.0f), GUILayout.Width(250.0f));

                    if (Event.current.type == EventType.Repaint)
                    {
                        addLODComponentRect = GUILayoutUtility.GetLastRect();
                    }
                    if (addLODComponent)
                    {
                        var dropdown = new SimplygonPipelineDropdown(this);
                        dropdown.Show(addLODComponentRect);
                    }
                    GUILayout.FlexibleSpace();
                    GUILayout.EndHorizontal();
                    GUILayout.EndVertical();
                    GUILayout.FlexibleSpace();
                }

                var hStatusBarRect = EditorGUILayout.BeginHorizontal();

                string simplygonVersionText = $"Simplygon v{SimplygonVersion.Build} | ";
                GUIContent simplygonVersionContent = new GUIContent(simplygonVersionText);

                var simplygonVersionContentRect = GUILayoutUtility.GetRect(simplygonVersionContent, statusBarStyle);
                GUI.Label(simplygonVersionContentRect, simplygonVersionContent);

                simplygonLoggerSingleLineView.OnGUI(
                    new Rect(simplygonVersionContentRect.xMax, simplygonVersionContentRect.y, hStatusBarRect.width > 0 ? hStatusBarRect.width - simplygonVersionContentRect.width : 0, simplygonVersionContentRect.height)
                    );

                GUILayout.FlexibleSpace();
                EditorGUILayout.EndHorizontal();

            }
            else if ((Tabs)selectedTab == Tabs.Log)
            {
                GUILayout.BeginVertical();
                GUILayout.BeginHorizontal();
                GUILayout.Label("Log", titleStyle);
                GUILayout.FlexibleSpace();
                if (GUILayout.Button(EditorGUIUtility.IconContent("_Popup", ""), GUILayout.Height(25.0f), GUILayout.Width(25.0f)))
                {
                    bool bHasLogEntries = SimplygonLogger.Instance.LogEntries.Count > 0;
                    string copyAllLogItemsToolTip = "Copies all log entries into the clipboard.";
                    string clearAllLogItemsToolTip = "Clears all log entries in the Log window.";
                    string exportAllItemsToolTip = "Exports the content in the Log window to file.";

                    GenericMenu logMenu = new GenericMenu();

                    if (bHasLogEntries)
                    {
                        logMenu.AddItem(new GUIContent($"Copy log entries", copyAllLogItemsToolTip), false, simplygonLoggerView.CopyLogItemsToClipboard);
                        logMenu.AddItem(new GUIContent($"Clear log entries", clearAllLogItemsToolTip), false, simplygonLoggerView.ClearLogItems);
                        logMenu.AddItem(new GUIContent($"Export log to file", exportAllItemsToolTip), false, simplygonLoggerView.ExportLogItemsToFile);
                    }
                    else
                    {
                        logMenu.AddDisabledItem(new GUIContent($"Copy log entries", copyAllLogItemsToolTip));
                        logMenu.AddDisabledItem(new GUIContent($"Clear log entries", clearAllLogItemsToolTip));
                        logMenu.AddDisabledItem(new GUIContent($"Export log to file", exportAllItemsToolTip));
                    }
                    logMenu.ShowAsContext();
                }

                GUILayout.EndHorizontal();
                GUILayout.EndVertical();

                GUILayout.BeginHorizontal();
                GUILayout.FlexibleSpace();

                EditorGUIUtility.labelWidth = 35;
                simplygonLoggerView.FilterMode = (LogFilter)EditorGUILayout.EnumPopup("Filter: ", simplygonLoggerView.FilterMode, GUILayout.Width(140), GUILayout.ExpandWidth(false));

                EditorGUIUtility.labelWidth = 30;
                simplygonLoggerView.SortMode = (LogSortMode)EditorGUILayout.EnumPopup("Sort: ", simplygonLoggerView.SortMode, GUILayout.Width(140), GUILayout.ExpandWidth(false));

                GUILayout.FlexibleSpace();
                GUILayout.EndHorizontal();

                GUILayout.BeginVertical();
                GUILayout.Space(5);
                GUILayout.EndVertical();

                var loggerViewRect = EditorGUILayout.BeginVertical();
                simplygonLoggerView.OnGUI(loggerViewRect);

                GUILayout.FlexibleSpace();
                EditorGUILayout.EndVertical();
            }
        }

        public void SetPipelineSettings(spPipeline sgPipeline, string textureOutputPath, ref int lodIndex)
        {
            using (spPipelineSettings sgPipelineSettings = sgPipeline.GetPipelineSettings())
            {
                sgPipelineSettings.SetEmbedReferences(false);
                sgPipelineSettings.SetTextureOutputPath(Path.Combine(textureOutputPath, $"LOD{lodIndex}"));
                sgPipelineSettings.SetSimplygonBatchPath(null);
            }

            for (uint i = 0; i < sgPipeline.GetCascadedPipelineCount(); ++i)
            {
                lodIndex++;
                SetPipelineSettings(sgPipeline.GetCascadedPipelineByIndex(i), textureOutputPath, ref lodIndex);
            }
        }

        public bool IsGameObjectsSelected()
        {
            return selectedGameObjects.Count > 0;
        }

        public List<KeyValuePair<string, ESimplygonMaterialCaster>> GetMaterialChannelsFromSelection()
        {
            UpdateSelection();

            Dictionary<string, ESimplygonMaterialCaster> materialChannels = new Dictionary<string, ESimplygonMaterialCaster>();

            HashSet<string> textureChannels = new HashSet<string>();
            foreach (var gameObject in selectedGameObjects)
            {
                if (gameObject != null)
                {
                    GetTextureChannelsFromGameObject(gameObject, textureChannels);
                }
            }

            Dictionary<string, List<KeyValuePair<string, ESimplygonMaterialCaster>>> usdPreviewSurfaceShaderMapping = new Dictionary<string, List<KeyValuePair<string, ESimplygonMaterialCaster>>>()
            {
                { "_MainTex", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("diffuseColor", ESimplygonMaterialCaster.ColorCaster ) } },
                { "_BaseColorMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("diffuseColor", ESimplygonMaterialCaster.ColorCaster ) } },
                { "_SpecularColorMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("specularColor", ESimplygonMaterialCaster.ColorCaster ) } },
                { "_BumpMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("normal", ESimplygonMaterialCaster.NormalCaster ) } },
                { "_NormalMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("normal", ESimplygonMaterialCaster.NormalCaster) } },
                { "_ParallaxMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("displacement", ESimplygonMaterialCaster.DisplacementCaster) } },
                { "_OcclusionMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("occlusion", ESimplygonMaterialCaster.AmbientOcclusionCaster) } },
                { "_EmissionMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("emissiveColor", ESimplygonMaterialCaster.ColorCaster) } },
                { "_MetallicGlossMap", new List<KeyValuePair<string, ESimplygonMaterialCaster>>() { new KeyValuePair<string, ESimplygonMaterialCaster>("metallic", ESimplygonMaterialCaster.ColorCaster), new KeyValuePair<string, ESimplygonMaterialCaster>("roughness", ESimplygonMaterialCaster.ColorCaster) } }
        };

            //Always add a diffuse color caster
            materialChannels.Add("diffuseColor", ESimplygonMaterialCaster.ColorCaster);

            foreach (var textureChannel in textureChannels)
            {
                if (usdPreviewSurfaceShaderMapping.ContainsKey(textureChannel))
                {
                    var materialCasters = usdPreviewSurfaceShaderMapping[textureChannel];
                    foreach (var materialCaster in materialCasters)
                    {
                        if (!materialChannels.ContainsKey(materialCaster.Key))
                        {
                            materialChannels.Add(materialCaster.Key, materialCaster.Value);
                        }
                    }
                }
            }

            return materialChannels.ToList();
        }

        private void GetTextureChannelsFromGameObject(GameObject gameObject, HashSet<string> textureChannels)
        {
            MeshRenderer meshRenderer = null;
            SkinnedMeshRenderer skinnedMeshRenderer = null;

            List<Material> materials = new List<Material>();

            if (gameObject.TryGetComponent<MeshRenderer>(out meshRenderer))
            {
                if (meshRenderer.sharedMaterials.Length > 0)
                {
                    materials.AddRange(meshRenderer.sharedMaterials);
                }
            }

            if (gameObject.TryGetComponent<SkinnedMeshRenderer>(out skinnedMeshRenderer))
            {
                if (skinnedMeshRenderer.sharedMaterials.Length > 0)
                {
                    materials.AddRange(skinnedMeshRenderer.sharedMaterials);
                }
            }

            foreach (Material material in materials)
            {
                string[] textureProperties = material.GetTexturePropertyNames();

                foreach (var textureProperty in textureProperties)
                {
                    var texture = material.GetTexture(textureProperty);

                    if (texture != null && !textureChannels.Contains(textureProperty))
                    {
                        textureChannels.Add(textureProperty);
                    }
                }
            }

            for (int i = 0; i < gameObject.transform.childCount; ++i)
            {
                GameObject childGameObject = gameObject.transform.GetChild(i).gameObject;
                if (childGameObject != null)
                {
                    GetTextureChannelsFromGameObject(childGameObject, textureChannels);
                }
            }
        }

        private void UpdateSelection()
        {
            //if (isSelectionChanged && !isHandlingSelection)
            {
                selectedGameObjects = new List<GameObject>();

                selectedTriangleCount = 0;
                selectedMeshCount = 0;
                selectedMaterialCount = 0;

                HashSet<int> materialIds = new HashSet<int>();

                if (Selection.objects != null)
                {
                    foreach (var o in Selection.objects)
                    {
                        if (o == null)
                        {
                            continue;
                        }

                        GameObject go = o as GameObject;

                        if (go != null)
                        {
                            selectedGameObjects.Add(go);

                            foreach (var meshFilter in go.GetComponentsInChildren<MeshFilter>())
                            {
                                if (meshFilter != null && meshFilter.sharedMesh != null)
                                {
                                    selectedTriangleCount += meshFilter.sharedMesh.triangles.Length / 3;
                                    selectedMeshCount++;
                                }
                            }

                            foreach (var meshRenderer in go.GetComponentsInChildren<MeshRenderer>())
                            {
                                if (meshRenderer != null && meshRenderer.sharedMaterials != null && meshRenderer.sharedMaterials.Length > 0)
                                {
                                    foreach (var material in meshRenderer.sharedMaterials)
                                    {
                                        if (material != null)
                                        {
                                            int id = material.GetInstanceID();

                                            if (!materialIds.Contains(id))
                                            {
                                                materialIds.Add(id);
                                            }
                                        }
                                    }
                                }
                            }

                            foreach (var skinnedMeshRenderer in go.GetComponentsInChildren<SkinnedMeshRenderer>())
                            {
                                if (skinnedMeshRenderer != null && skinnedMeshRenderer.sharedMaterials != null && skinnedMeshRenderer.sharedMaterials.Length > 0)
                                {
                                    foreach (var material in skinnedMeshRenderer.sharedMaterials)
                                    {
                                        if (material != null)
                                        {
                                            int id = material.GetInstanceID();

                                            if (!materialIds.Contains(id))
                                            {
                                                materialIds.Add(id);
                                            }
                                        }
                                    }
                                }

                                if (skinnedMeshRenderer != null && skinnedMeshRenderer.sharedMesh != null)
                                {
                                    selectedTriangleCount += skinnedMeshRenderer.sharedMesh.triangles.Length / 3;
                                    selectedMeshCount++;
                                }
                            }
                        }
                    }
                }
                selectedMaterialCount = materialIds.Count;
            }
        }
    }
}
