/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "stdafx.h"

// QT
#include <QKeySequence>
#include <QSettings>
#include <QFile>
#include <QMenuBar>
#include <qfiledialog.h>
#include <QMessageBox>
#include <QShortcut>
#include <qxmlstream.h>

//Editor
#include <EditorDefs.h>
#include <IEditorParticleUtils.h>
#include <Include/IEditorParticleManager.h>
#include <Undo/UndoVariableChange.h>
#include <Clipboard.h>
#include "../../../../CryEngine/CryCommon/ParticleParams.h"
#include <AzQtComponents/Components/StylesheetPreprocessor.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzCore/Component/Entity.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Metrics/LyEditorMetricsBus.h>
#include <MathConversion.h>
#include <Viewport.h>
#include <LmbrCentral/Rendering/ParticleComponentBus.h>

//EditorCore
#include <Undo/Undo.h>

//EditorUI_QT
#include <DockableLibraryPanel.h>
#include <DockablePreviewPanel.h>
#include <Utils.h>
#include <VariableWidgets/QKeySequenceEditorDialog.h>
#include <VariableWidgets/QGradientColorDialog.h>
#include <VariableWidgets/QColorEyeDropper.h>
#include <VariableWidgets/QCopyModalDialog.h>
#include <UIFactory.h>
#include <LibraryTreeViewItem.h>
#include <LibraryTreeView.h>
#include <DefaultViewWidget.h>
#include <AttributeItem.h>
#include <Undo/EditorLibraryUndoManager.h>

//Local
#include "MainWindow.h"
#include "ParticleEditorPlugin.h"
#include "DockableAttributePanel.h"
#include "DockableLODPanel.h"
#include "DockableParticleLibraryPanel.h"
#include "QAttributePresetWidget.h"

#define PARTICLE_EDITOR_DATA_SEPARATOR '~'
#define MAX_NUMBER_OF_LAYOUT_MENU 10
#define SETTINGS_LAYOUT_MENU_GROUP "LayoutMenu/"
#define SETTINGS_LAYOUT_MENU_SIZE_KEY "LayoutMenuSize"
#define PARTICLE_EDITOR_LAYOUT_KEY "LayoutParticleEditor"
#define PARTICLE_EDITOR_SETTINGS_KEY "EditorSettingsParticleEditor"

////////////////////////////////////////////////////////////////
//These are default options ...
static int s_width = 800;
static int s_height = 600;
static int s_x = 0;
static int s_y = 0;
////////////////////////////////////////////////////////////////

#define STYLESHEET_PATH_LIGHT "Editor/Styles/EditorStyleSheet.qss"
#define STYLESHEET_PATH_DARK "Editor/Styles/stylesheet_Dark.qss"
#define DEFAULT_PARTICLE_EDITOR_LAYOUT_PATH "Editor/Default.editor_layout"

CMainWindow::CMainWindow(QWidget* parent)
    : QMainWindow(parent)
    , m_libraryTreeViewDock(nullptr)
    , m_attributeViewDock(nullptr)
    , m_previewDock(nullptr)
    , m_libraryMenu(nullptr)
    , m_layoutMenu(nullptr)
    , m_showLayoutMenu(nullptr)
    , m_viewMenu(nullptr)
    , m_fileMenu(nullptr)
    , m_editMenu(nullptr)
    , m_libraryMenuActionGroup(nullptr)
    , m_menuBar(nullptr)
    , m_bIsRefreshing(false)
    , m_RequestedClose(false)
    , m_isFirstSceneSinceLaunch(true)
    , m_iRefreshDelay(-1)
    , m_preset(nullptr)
    , m_stylesheetPreprocessor(new AzQtComponents::StylesheetPreprocessor(this))
    , m_needLibraryRefresh(false)
    , m_requireLayoutReload(false)
    , m_groupWithLodIcon(QIcon("Editor/UI/Icons/treeview/ParticleEditor/group_with_lod_icon.png"))
    , m_lodIcon(QIcon("Editor/UI/Icons/treeview/ParticleEditor/lod_icon.png"))
    , m_groupIcon(QIcon("Editor/UI/Icons/treeview/ParticleEditor/group_icon.png"))
    , m_emptyIcon(QIcon("Editor/UI/Icons/treeview/ParticleEditor/empty_icon.png"))
{
    setWindowTitle(tr("Particle Editor"));

    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());


    GetIEditor()->GetParticleUtils()->HotKey_LoadExisting();

    //Move to last location.
    move(s_x, s_y);

    //Size to last size.
    resize(s_width, s_height);

    CreateDockWindows();
    CreateMenuBar();
    CreateShortcuts();

    //Save the editor layout for reset before we load any user settings
    m_defaultEditorLayout = this->saveState(0);

    GetIEditor()->RegisterNotifyListener(this);

    // Push an event onto the end of the event queue to load our state
    // It has to be on the end of the event queue, so that our layout
    // restore happens after the QtViewPaneManager does it's restore
    QTimer::singleShot(0, this, [this]()
        {
            if (AzQtComponents::StyleManager::isUi10())
            {
                // State loading is busted for undocked windows and layout loading is about to be replaced by UI 2.0, so just restore the default layout when opened for now.
                ResetToDefaultEditorLayout();

                UpdatePalette();
            }
            GetIEditor()->GetParticleUtils()->ToolTip_LoadConfigXML(QString("Editor\\Plugins\\ParticleEditorPlugin\\settings\\ToolTips.xml"));

            RegisterActions();

            m_layoutMenu = new QMenu();
            View_PopulateMenu();
        });

    m_undoManager = new EditorUIPlugin::EditorLibraryUndoManager(GetIEditor()->GetParticleManager());

    LibraryItemUIRequests::Bus::Handler::BusConnect();
    LibraryChangeEvents::Bus::Handler::BusConnect();
}

CMainWindow::~CMainWindow(void)
{
    LibraryChangeEvents::Bus::Handler::BusDisconnect();
    LibraryItemUIRequests::Bus::Handler::BusDisconnect();

    CRY_ASSERT(GetIEditor());

    //Store my size and position for later.
    s_width = width();
    s_height = height();
    QPoint position = pos();
    s_x = position.x();
    s_y = position.y();

    SAFE_DELETE(m_undoManager);
    
    GetIEditor()->UnregisterNotifyListener(this);
    GetISystem()->GetISystemEventDispatcher()->RemoveListener(this);

    for (unsigned int i = 0; i < m_shortcuts.count(); i++)
    {
        delete m_shortcuts[i];
    }
    m_shortcuts.clear();

    SAFE_DELETE(m_libraryMenuActionGroup);
    SAFE_DELETE(m_libraryTreeViewDock);
    SAFE_DELETE(m_attributeViewDock);
    SAFE_DELETE(m_previewDock);
    SAFE_DELETE(m_menuBar);
    SAFE_DELETE(m_libraryMenu);
    SAFE_DELETE(m_layoutMenu);
    SAFE_DELETE(m_showLayoutMenu);
    SAFE_DELETE(m_viewMenu);
    SAFE_DELETE(m_fileMenu);
    SAFE_DELETE(m_editMenu);
    SAFE_DELETE(m_stylesheetPreprocessor);
}

const GUID& CMainWindow::GetClassID()
{
    // {19C5E05B-2AEE-497E-9ABA-BE4DEF9112B9}
    static const GUID guid = {
        0x19c5e05b, 0x2aee, 0x497e, { 0x9a, 0xba, 0xbe, 0x4d, 0xef, 0x91, 0x12, 0xb9 }
    };
    return guid;
}

void CMainWindow::CreateMenuBar()
{
    //Setup menu bar
    m_menuBar = new QMenuBar(this);
    QAction* action = nullptr;
#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, [=]() {                           \
                if (GetIEditor()->GetParticleUtils()->HotKey_IsEnabled())            \
                {                                                                    \
                    this->callback();                                                \
                }                                                                    \
            });                                                                      \
    }
#define ADD_ACTION_MANUAL(text, tooltip, icon, key)                                  \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
    }
    //Use this to make sure that the action is only enabled when an item is selected in the library
#define SETUP_LIBRARY_ACTION()                   \
    {                                            \
        action->setEnabled(false);               \
        m_itemSelectedActions.push_back(action); \
    }

    // File Menu
    m_fileMenu = new QMenu(tr("File"));
    File_PopulateMenu();
    m_menuBar->addMenu(m_fileMenu);

    // Edit Menu
    m_editMenu = new QMenu(tr("Edit"));
    Edit_PopulateMenu();
    connect(m_editMenu, &QMenu::aboutToShow, this, [this] { UpdateEditActions(true); });
    connect(m_editMenu, &QMenu::aboutToHide, this, [this] { UpdateEditActions(false);});
    m_menuBar->addMenu(m_editMenu);

    // View Menu
    m_viewMenu = new QMenu(tr("View"));
    connect(m_viewMenu, &QMenu::aboutToShow, this, &CMainWindow::View_PopulateMenu);
    m_menuBar->addMenu(m_viewMenu);

    setMenuBar(m_menuBar);
#undef ADD_ACTION
#undef SETUP_LIBRARY_ACTION
}

void CMainWindow::UpdateEditActions(bool menuShown)
{
    if (menuShown)
    {
        // Undo
        bool hasUndo = false;
        EBUS_EVENT_RESULT(hasUndo, EditorLibraryUndoRequestsBus, HasUndo);
        m_undoAction->setEnabled(hasUndo);

        // Redo
        bool hasRedo = false;
        EBUS_EVENT_RESULT(hasRedo, EditorLibraryUndoRequestsBus, HasRedo);
        m_redoAction->setEnabled(hasRedo);

        // Copy
        QVector<CBaseLibraryItem*> selectedItems = m_libraryTreeViewDock->GetSelectedItems();
        auto firstSelectedItem = selectedItems.isEmpty() ? nullptr : selectedItems.first();
        m_copyAction->setEnabled(firstSelectedItem);

        // Duplicate
        m_duplicateAction->setEnabled(firstSelectedItem && selectedItems.count() == 1);

        // Add LOD
        bool enable = false;
        if (firstSelectedItem)
        {
            auto pParticle = static_cast<CParticleItem*>(firstSelectedItem);
            enable = pParticle->IsParticleItem && m_libraryTreeViewDock->GetTreeItemFromPath(QString(selectedItems.first()->GetFullName())) != nullptr;
        }
        m_addLODAction->setEnabled(enable);

        // Others
        m_resetSelectedItem->setEnabled(firstSelectedItem);
        m_deleteSelectedItem->setEnabled(firstSelectedItem);
        m_renameSelectedItem->setEnabled(firstSelectedItem);
    }
    else
    {
        // All actions need to be enabled when menu is hidden, so they can respond to shortcuts
        m_undoAction->setEnabled(true);
        m_redoAction->setEnabled(true);
        m_copyAction->setEnabled(true);
        m_duplicateAction->setEnabled(true);
        m_resetSelectedItem->setEnabled(true);
        m_deleteSelectedItem->setEnabled(true);
        m_renameSelectedItem->setEnabled(true);
    }
}

void CMainWindow::OnEditorNotifyEvent(EEditorNotifyEvent e)
{
    // Do this before the early return because the libraries need to be refreshed regardless of whether
    // the editor is visible at the time. If it's not visible, the actual refresh will happen when
    // the editor is shown.
    if (eNotify_OnBeginNewScene == e || eNotify_OnEndSceneOpen == e || eNotify_OnDataBaseUpdate == e || eNotify_OnSceneClosed == e)
    {
        m_needLibraryRefresh = true;
    }

    if (!this->isVisible())
    {
        return;
    }

    switch (e)
    {
    case eNotify_OnBeginNewScene:
        {
            m_undoManager->Reset();
            if (m_isFirstSceneSinceLaunch)
            {
                m_AutoRecovery.AttemptRecovery();
            }

            m_isFirstSceneSinceLaunch = false;

            RefreshLibraries();

            break;
        }
    case eNotify_OnEndSceneOpen:
        {
            m_undoManager->Reset();
            if (m_isFirstSceneSinceLaunch)
            {
                m_AutoRecovery.AttemptRecovery();
            }

            m_isFirstSceneSinceLaunch = false;

            RefreshLibraries();

            break;
        }
    case eNotify_OnIdleUpdate:
        {
            // Run invoke queue
            Utils::runInvokeQueue();

            if (m_RequestedClose)
            {
                CRY_ASSERT(GetIEditor());
                m_RequestedClose = false;
                bool userDidNotCancelClose = OnCloseWindowCheck();
                if (userDidNotCancelClose)
                {
                    CleanupOnClose();
                    GetIEditor()->CloseView(CParticleEditorPlugin::m_RegisteredQtViewPaneName);
                }
            }
            break;
        }
    case eNotify_OnStyleChanged:
        {
            UpdatePalette();
            break;
        }
    case eNotify_OnDataBaseUpdate:
        {
            if (m_isFirstSceneSinceLaunch)
            {
                m_AutoRecovery.AttemptRecovery();
            }

            m_isFirstSceneSinceLaunch = false;

            RefreshLibraries();

            break;
        }
    case eNotify_OnSceneClosed:
        RefreshLibraries();
        break;
    case eNotify_OnBeginLoad:
        //disable input during level loading
        setEnabled(false);
        break;
    case eNotify_OnEndLoad:
        //enable input
        setEnabled(true);
        break;
    default:
        break;
    }
}

void CMainWindow::OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam)
{
    if (event == ESYSTEM_EVENT_LEVEL_UNLOAD)
    {
        PrepareForParticleSystemUnload();
    }
}

void CMainWindow::PrepareForParticleSystemUnload()
{
    m_libraryTreeViewDock->ResetSelection();
    m_attributeViewDock->CloseAllTabs();
}

void CMainWindow::CreateDockWindows()
{
    m_preset = new QAttributePresetWidget(this);
    m_preset->hide();

    setObjectName("dwParticleMain");
    // Enable Dock Nesting so that windows can be configured in better configurations.
    setDockNestingEnabled(true);

    //////////////////////////////////////////////////////////////////////////////////
    // * Tree View
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleManager());
    m_libraryTreeViewDock = new DockableParticleLibraryPanel(this);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalDecorateDefaultView, this, &CMainWindow::Library_DecorateTreesDefaultView, Qt::DirectConnection);
    m_libraryTreeViewDock->Init(tr("Libraries"), GetIEditor()->GetParticleManager());
    m_libraryTreeViewDock->SetHotkeyHandler(m_libraryTreeViewDock, [&](QKeyEvent* e){ keyPressEvent(e); }); //Routing this to MainWindow to handle the global items
    m_libraryTreeViewDock->SetShortcutHandler(m_libraryTreeViewDock, [&](QShortcutEvent* e){ return ShortcutEvent(e); }); //Routing this to MainWindow to handle the global items
    m_libraryTreeViewDock->setObjectName("dwLibraryTreeView");
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalPopulateTitleBarMenu, this, &CMainWindow::Library_PopulateTitleBarMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalPopulateItemContextMenu, this, &CMainWindow::Library_PopulateItemContextMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalPopulateLibraryContextMenu, this, &CMainWindow::Library_PopulateLibraryContextMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemAboutToBeRenamed, this, &CMainWindow::Library_ItemNameValidationRequired);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemRenamed, this, &CMainWindow::Library_ItemRenamed);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemCheckLod, this, &CMainWindow::Library_UpdateTreeItemStyle);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemAdded, this, &CMainWindow::Library_ItemAdded);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalTreeFilledFromLibrary, this, &CMainWindow::Library_TreeFilledFromLibrary);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemAboutToBeDragged, this, &CMainWindow::Library_ItemDragged);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalDragOperationFinished, this, &CMainWindow::Library_DragOperationFinished);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemEnableStateChanged, this, &CMainWindow::Library_ItemEnabledStateChanged);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalDuplicateItem, this, &CMainWindow::Library_ItemDuplicated);
    connect(m_libraryTreeViewDock, &DockableParticleLibraryPanel::SignalSaveAllLibs, &m_AutoRecovery, &ParticleLibraryAutoRecovery::Discard);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemPasted, this, &CMainWindow::Library_ItemPasted);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalPasteItemsToFolder, this, &CMainWindow::Library_ItemsPastedToFolder);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalCopyItems, this, &CMainWindow::Library_CopyTreeItems);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalCopyItem, this, &CMainWindow::Library_ItemCopied);

    addDockWidget(Qt::LeftDockWidgetArea, m_libraryTreeViewDock);

    //////////////////////////////////////////////////////////////////////////////////
    // * Attribute View
    m_attributeViewDock = new DockableAttributePanel(this);
    m_attributeViewDock->Init(tr("Attributes"), GetIEditor()->GetParticleManager());
    m_attributeViewDock->SetHotkeyHandler(m_attributeViewDock, [&](QKeyEvent* e){ keyPressEvent(e); }); //Routing this to MainWindow to handle the global items
    m_attributeViewDock->SetShortcutHandler(m_attributeViewDock, [&](QShortcutEvent* e){ return ShortcutEvent(e); }); //Routing this to MainWindow to handle the global items
    m_attributeViewDock->setObjectName("dwAttributeView");
    connect(m_attributeViewDock, &DockableAttributePanel::SignalPopulateTitleBarMenu, this, &CMainWindow::Attribute_PopulateTitleBarMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemSelected, m_attributeViewDock, &DockableAttributePanel::ItemSelectionChanged);
    connect(m_attributeViewDock, &DockableAttributePanel::SignalTabSelectionChanged, m_libraryTreeViewDock, &DockableLibraryPanel::SelectLibraryAndItemByName);
    connect(m_attributeViewDock, &DockableAttributePanel::SignalPopulateTabBarContextMenu, this, &CMainWindow::Attribute_PopulateTabBarContextMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemRenamed, m_attributeViewDock, &DockableAttributePanel::ItemNameChanged);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalUpdateTabName, m_attributeViewDock, &DockableAttributePanel::UpdateItemName);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemDeleted, m_attributeViewDock, &DockableAttributePanel::CloseTab);
    connect(m_preset, &QAttributePresetWidget::SignalCustomPanel, m_attributeViewDock, &DockableAttributePanel::ImportPanelFromQString);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalOpenInNewTab, m_attributeViewDock, &DockableAttributePanel::OpenInTab);

    connect(m_attributeViewDock, &DockableAttributePanel::SignalBuildCustomAttributeList, m_preset, &QAttributePresetWidget::BuilPresetMenu);
    connect(m_attributeViewDock, &DockableAttributePanel::SignalAddPreset, m_preset, &QAttributePresetWidget::AddPreset);
    connect(m_attributeViewDock, &DockableAttributePanel::SignalResetPresetList, m_preset, &QAttributePresetWidget::BuildDefaultLibrary);

    //POSSIBLE LEGACY ONLY SYNC USAGE HERE
    connect(m_attributeViewDock, &DockableAttributePanel::SignalParameterChanged, this, &CMainWindow::Attribute_ParameterChanged);
    //POSSIBLE LEGACY ONLY SYNC USAGE HERE

    connect(m_attributeViewDock, &DockableAttributePanel::SignalItemUndoPoint, this, &CMainWindow::OnItemUndoPoint);
    addDockWidget(Qt::RightDockWidgetArea, m_attributeViewDock);

    //////////////////////////////////////////////////////////////////////////////////
    // * Preview view
    m_previewDock = new DockablePreviewPanel(this);
    m_previewDock->Init(tr("Preview"));
    m_previewDock->setObjectName("dwPreviewCtl");
    m_previewDock->SetHotkeyHandler(m_previewDock, [&](QKeyEvent* e){ keyPressEvent(e); }); //Routing this to MainWindow to handle the global items
    m_previewDock->SetShortcutHandler(m_previewDock, [&](QShortcutEvent* e){ return ShortcutEvent(e); }); //Routing this to MainWindow to handle the global items
    connect(m_previewDock, &DockablePreviewPanel::SignalPopulateTitleBarMenu, this, &CMainWindow::Preview_PopulateTitleBarMenu);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemSelected, m_previewDock, &DockablePreviewPanel::ItemSelectionChanged);
    addDockWidget(Qt::LeftDockWidgetArea, m_previewDock);

    //////////////////////////////////////////////////////////////////////////////////
    // * Level of detail
    m_LoDDock = new DockableLODPanel(this);
    m_LoDDock->Init(tr("Level of detail"));
    m_LoDDock->setObjectName("dwLODPanel");
    m_LoDDock->SetHotkeyHandler(m_LoDDock, [&](QKeyEvent* e){ keyPressEvent(e); }); //Routing this to MainWindow to handle the global items
    m_LoDDock->SetShortcutHandler(m_LoDDock, [&](QShortcutEvent* e){ return ShortcutEvent(e); }); //Routing this to MainWindow to handle the global items
    connect(m_attributeViewDock, &DockableAttributePanel::SignalLODParticleHighLight, m_LoDDock, &DockableLODPanel::updateTreeItemHighLight);
    connect(m_LoDDock, &DockableLODPanel::SignalPopulateTitleBarMenu, this, &CMainWindow::LOD_PopulateTitleBarMenu);
    connect(m_LoDDock, &DockableLODPanel::SignalChangeLODIcon, m_libraryTreeViewDock, &DockableLibraryPanel::UpdateIconStyle);
    connect(m_libraryTreeViewDock, &DockableLibraryPanel::SignalItemSelected, m_LoDDock, &DockableLODPanel::ItemSelectionChanged);
    connect(m_LoDDock, &DockableLODPanel::SignalLodItemSelectionChanged, m_attributeViewDock, &DockableAttributePanel::LodItemSelectionChanged);
    connect(m_LoDDock, &DockableLODPanel::SignalLodItemSelectionChanged, this, [=](CBaseLibraryItem* item, SLodInfo* lod)
        {
            m_libraryTreeViewDock->blockSignals(true);
            m_libraryTreeViewDock->SelectLibraryAndItemByName(QString(item->GetLibrary()->GetName()), QString(item->GetName()));
            m_libraryTreeViewDock->blockSignals(false);
            m_previewDock->LodSelectionChanged(item, lod);
        });

    addDockWidget(Qt::LeftDockWidgetArea, m_LoDDock);
}

void CMainWindow::ResetToDefaultEditorLayout()
{
    //reset main window layout
    restoreState(m_defaultEditorLayout);
    //reset attribute view layout
    CRY_ASSERT(m_attributeViewDock);
    m_attributeViewDock->ResetToDefaultLayout();
    //reset previewer layout
    CRY_ASSERT(m_previewDock);
    m_previewDock->ResetToDefaultLayout();
    //reset library layout include dont prompt settings
    UIFactory::GetQTUISettings()->ResetToDefault();
}

void CMainWindow::OnActionViewDocumentation()
{
    static const char* documentationUrl = "https://docs.aws.amazon.com/console/lumberyard/particles/editor";
    QDesktopServices::openUrl(QUrl(documentationUrl));
}

#pragma region Actions: View Menu

void CMainWindow::OnRefreshViewMenu()
{
    QList<QAction*> viewActions = m_viewMenu->actions();
    for (int i = 0, numActions = viewActions.size(); i < numActions; i++)
    {
        QAction* action = viewActions[i];
        if (action->text().contains("attribute"))
        {
            if (m_attributeViewDock->isHidden())
            {
                QString newName = "Show attribute";
                action->setText(newName);
            }
            else
            {
                QString newName = "Hide attribute";
                action->setText(newName);
            }
        }
        else if (action->text().contains("library"))
        {
            if (m_libraryTreeViewDock->isHidden())
            {
                QString newName = "Show library";
                action->setText(newName);
            }
            else
            {
                QString newName = "Hide library";
                action->setText(newName);
            }
        }
        else if (action->text().contains("preview"))
        {
            if (m_previewDock->isHidden())
            {
                QString newName = "Show preview";
                action->setText(newName);
            }
            else
            {
                QString newName = "Hide preview";
                action->setText(newName);
            }
        }
        else if (action->text().contains("level of detail"))
        {
            if (m_previewDock->isHidden())
            {
                QString newName = "Show level of detail";
                action->setText(newName);
            }
            else
            {
                QString newName = "Hide level of detail";
                action->setText(newName);
            }
        }
    }
}

void CMainWindow::OnAddNewLayout(QString location, bool loading)
{
    if (m_layoutMenu)
    {
        bool isAdded = false;

        QList<QAction*> menuActions = m_layoutMenu->actions();
        int menu_size = menuActions.size();
        for (int i = 0; i < menu_size; i++)
        {
            QAction* action = menuActions[i];
            if (action->text().contains(location))
            {
                action->setChecked(true);
                isAdded = true;
            }
            else
            {
                action->setChecked(false);
            }
        }

        if (!isAdded)
        {
            QAction* action = new QAction(location, m_layoutMenu);
            action->setCheckable(true);
            action->setChecked(!loading);
            action->setData(this->saveState(0));
            connect(action, &QAction::triggered, this, [this, action]()
                {
                    RestoreAllLayout(action->text());
                    OnAddNewLayout(action->text(), false);
                });
            m_layoutMenu->addAction(action);
            if (!loading)
            {
                StateSave_layoutMenu();
            }
        }
    }
}

void CMainWindow::OnActionViewImportLayout()
{
    QString location = QFileDialog::getOpenFileName(this, "Select location to load layout...", "", tr("Editor Layout Files (*.editor_layout)"));

    RestoreAllLayout(location);
}

void CMainWindow::RestoreAllLayout(QString location)
{
    if (!location.length()) //No path is an invalid path, this was probably
    {
        return;
    }
    QFile file(location);
    if (!file.exists()) // The selected path does not exist on disk
    {
        QMessageBox::warning(this, tr("Warning"), tr("The layout file at: \"%1\" could not be loaded.\n").arg(location));
        return;
    }

    QList<QByteArray> data_separator;

    file.open(QIODevice::ReadOnly);
    QByteArray data_to_restore = file.readAll();
    file.close();
    QDataStream stream(data_to_restore);
    stream.setByteOrder(QDataStream::BigEndian);
    quint32 fileFormat;
    stream >> fileFormat;
    quint32 fileVersion;
    stream >> fileVersion;
    quint32 mainWindowSize;
    stream >> mainWindowSize;
    quint32 attributeSize;
    stream >> attributeSize;
    quint32 previewSize;
    stream >> previewSize;

    QByteArray data_mainWindow;
    QByteArray data_attribute;
    QByteArray data_preview;
    QByteArray data_settings;



    switch (fileVersion)
    {
    case 0x1:
        {
            data_mainWindow.resize(mainWindowSize);
            stream.readRawData(data_mainWindow.data(), mainWindowSize);

            data_attribute.resize(attributeSize);
            stream.readRawData(data_attribute.data(), attributeSize);

            data_preview.resize(previewSize);
            stream.readRawData(data_preview.data(), previewSize);

            break;
        }
    case 0x2:
        {
            quint32 editorSettingSize;
            stream >> editorSettingSize;

            data_mainWindow.resize(mainWindowSize);
            stream.readRawData(data_mainWindow.data(), mainWindowSize);

            data_attribute.resize(attributeSize);
            stream.readRawData(data_attribute.data(), attributeSize);

            data_preview.resize(previewSize);
            stream.readRawData(data_preview.data(), previewSize);

            data_settings.resize(editorSettingSize);
            stream.readRawData(data_settings.data(), editorSettingSize);

            UIFactory::GetQTUISettings()->LoadSetting(data_settings);
            break;
        }
    default:
        {
            //The format is not correct. Try load it using previous version.
            bool loadSuccess = restoreState(data_to_restore, 0);
            if (loadSuccess) // Load old version, notify user to update the layout file
            {
                OnAddNewLayout(location, false);
                OnRefreshViewMenu();
            }
            else // Load fails
            {
                QMessageBox dlg("Warning", tr("The layout file format is incorrect."),
                    QMessageBox::Icon::Warning, QMessageBox::Button::Close, QMessageBox::Button::Cancel, 0, this);
                dlg.exec();
            }
            return;
        }
        break;
    }

    if (fileFormat != PARTICLE_EDITOR_LAYOUT_IDENTIFIER)
    {
        QMessageBox::warning(this, tr("Warning"), tr("The layout file format is incorrect."));
        return;
    }

    restoreState(data_mainWindow, 0); //works
    OnAddNewLayout(location, false);

    OnRefreshViewMenu();

    CRY_ASSERT(m_attributeViewDock);
    m_attributeViewDock->LoadAttributeLayout(data_attribute); //does not work

    CRY_ASSERT(m_previewDock);
    m_previewDock->LoadSessionState(data_preview); //does not work
}

void CMainWindow::OnActionViewExportLayout()
{
    QString location = QFileDialog::getSaveFileName(this, "Select location to save layout...", "", tr("Editor Layout Files (*.editor_layout)"));
    if (!location.length())     //if the user hit cancel
    {
        return;
    }

    QFile file(location);

    //layout setting for mainwindow
    QByteArray data_mainWindow = saveState(0);
    OnAddNewLayout(location, false);

    //layout setting for attribute panel
    QByteArray data_attribute;
    m_attributeViewDock->SaveAttributeLayout(data_attribute);

    //layout setting for preview window
    QByteArray data_preview;
    m_previewDock->SaveSessionState(data_preview);

    QByteArray data_settings;
    UIFactory::GetQTUISettings()->SaveSetting(data_settings);

    quint32 fileFormat = PARTICLE_EDITOR_LAYOUT_IDENTIFIER;
    quint32 fileVersion = PARTICLE_EDITOR_LAYOUT_VERSION;
    quint32 mainWindowSize = data_mainWindow.size();
    quint32 attributeSize = data_attribute.size();
    quint32 previewSize = data_preview.size();
    quint32 settingSize = data_settings.size();

    file.open(QIODevice::WriteOnly);
    QDataStream stream(&file);
    stream.setByteOrder(QDataStream::BigEndian);

    stream << fileFormat;
    stream << fileVersion;
    stream << mainWindowSize;
    stream << attributeSize;
    stream << previewSize;
    stream << settingSize;

    int result = stream.writeRawData(data_mainWindow, mainWindowSize);
    result = stream.writeRawData(data_attribute, attributeSize);
    result = stream.writeRawData(data_preview, previewSize);
    result = stream.writeRawData(data_settings, settingSize);
    QDataStream::Status stats = stream.status();
    qDebug() << stats;
    file.close();
}

void CMainWindow::OnActionViewHideAttributes()
{
    if (m_attributeViewDock->isHidden())
    {
        m_attributeViewDock->show();
    }
    else
    {
        m_attributeViewDock->hide();
    }
}
void CMainWindow::OnActionViewHideLibrary()
{
    if (m_libraryTreeViewDock->isHidden())
    {
        m_libraryTreeViewDock->show();
    }
    else
    {
        m_libraryTreeViewDock->hide();
    }
}
void CMainWindow::OnActionViewHidePreview()
{
    if (m_previewDock->isHidden())
    {
        m_previewDock->show();
    }
    else
    {
        m_previewDock->hide();
    }
}

void CMainWindow::OnActionViewHideLOD()
{
    if (m_LoDDock->isHidden())
    {
        m_LoDDock->show();
    }
    else
    {
        m_LoDDock->hide();
    }
}

#pragma endregion

#pragma region Actions: File Menu
void CMainWindow::OnActionClose()
{
    m_RequestedClose = true;
}
#pragma endregion

#pragma region Actions: Standard (Copy/Paste/Undo/Redo)
void CMainWindow::OnActionStandardUndo()
{
    EBUS_EVENT(EditorLibraryUndoRequestsBus, Undo);
}

void CMainWindow::OnActionStandardRedo()
{
    EBUS_EVENT(EditorLibraryUndoRequestsBus, Redo);
}
#pragma endregion

#pragma region State persistence

// NOTE: Make sure all dock widgets and widget contained therein have an object name! (using setObjectName).
//       This is used by Qt to correctly identify widgets when storing/loading data.

void CMainWindow::StateLoad_Properties()
{
    CRY_ASSERT(m_attributeViewDock);
    m_attributeViewDock->LoadSessionState();
    CRY_ASSERT(m_preset);
    m_preset->LoadSessionPresets();
}

void CMainWindow::StateLoad_Preview()
{
    CRY_ASSERT(m_previewDock);
    m_previewDock->LoadSessionState();
}

void CMainWindow::StateSave()
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());

    QSettings settings("Amazon", "Lumberyard");
    settings.setValue(PARTICLE_EDITOR_LAYOUT_KEY, this->saveState(0));
    settings.sync();
    GetIEditor()->GetParticleUtils()->HotKey_SaveCurrent();

    QByteArray qsettingsdata;
    UIFactory::GetQTUISettings()->SaveSetting(qsettingsdata);
    settings.setValue(PARTICLE_EDITOR_SETTINGS_KEY, qsettingsdata);

    CRY_ASSERT(m_attributeViewDock);
    m_attributeViewDock->SaveSessionState();

    CRY_ASSERT(m_previewDock);
    m_previewDock->SaveSessionState();

    CRY_ASSERT(m_preset);
    m_preset->StoreSessionPresets();
}


void CMainWindow::StateSave_layoutMenu()
{
    if (m_layoutMenu)
    {
        QSettings settings("Amazon", "Lumberyard");
        settings.beginGroup(SETTINGS_LAYOUT_MENU_GROUP);
        {
            //REMOVE OLD SETTINGS
            settings.remove("");
            settings.sync();
            QList<QAction*> menuActions = m_layoutMenu->actions();
            int menu_size = menuActions.size();
            QString tag;
            QString path;
            settings.setValue(SETTINGS_LAYOUT_MENU_SIZE_KEY, menu_size);

            
            for (int i = 0; i < menu_size; i++)
            {
                QAction* childAction = menuActions[i];
                CRY_ASSERT(childAction);
                
                if (childAction)
                {
                    tag = QString::number(i);
                    path = childAction->text();
                    settings.setValue(tag, path);
                }
            }
        }
        settings.endGroup();
        settings.sync();
    }
}

void CMainWindow::StateLoad_layoutMenu()
{
    QSettings settings("Amazon", "Lumberyard");
    settings.beginGroup(SETTINGS_LAYOUT_MENU_GROUP);
    int menu_size = settings.value(SETTINGS_LAYOUT_MENU_SIZE_KEY, 0).toInt();
    QString tag;
    QString path;
    for (int i = 0; i < menu_size; i++)
    {
        tag = QString::number(i);
        path = settings.value(tag, "").toString();
        OnAddNewLayout(path, true);
    }
}

void CMainWindow::StateLoad()
{
    QSettings settings("Amazon", "Lumberyard");

    StateLoad_Properties();     // loads layout of the Properties screen
    StateLoad_Preview();        // loads layout of the preview window
    if (settings.contains(PARTICLE_EDITOR_LAYOUT_KEY))
    {
        restoreState(settings.value(PARTICLE_EDITOR_LAYOUT_KEY).toByteArray(), 0);
    }
    if (settings.contains(PARTICLE_EDITOR_SETTINGS_KEY))
    {
        UIFactory::GetQTUISettings()->LoadSetting(settings.value(PARTICLE_EDITOR_SETTINGS_KEY).toByteArray());
    }
    OnAddNewLayout("Last User State", false);
}

void CMainWindow::RefreshLibraries()
{
    if (m_needLibraryRefresh)
    {
        m_libraryTreeViewDock->RebuildFromEngineData(/*Expansion data is likely corrupt here, don't save it.*/true);
        m_libraryTreeViewDock->ResetSelection();
        m_attributeViewDock->CloseAllTabs();
        m_needLibraryRefresh = false;
    }
}

void CMainWindow::closeEvent(QCloseEvent* event)
{
    // NOTE: this event must be handled here and now, without being queued.
    // Otherwise, when the user closes the LY Editor, the Particle Editor will block everything closing.

    bool userDidNotCancelClose = OnCloseWindowCheck();
    if (userDidNotCancelClose)
    {
        CleanupOnClose();
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

void CMainWindow::showEvent(QShowEvent * event)
{
    if (m_requireLayoutReload)
    {
        m_requireLayoutReload = false;

        // State loading is busted for undocked windows and layout loading is about to be replaced by UI 2.0, so just restore the default layout when opened for now.
        ResetToDefaultEditorLayout();
    }

    RefreshLibraries();
}

void CMainWindow::CleanupOnClose()
{
    StateSave();
    StateSave_layoutMenu();

    QList<QDockWidget*> dockWidgets = findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly);
    for (int i = 0; i < dockWidgets.size(); ++i)
    {
        dockWidgets[i]->hide();
    }
    m_requireLayoutReload = true;

    m_undoManager->Reset();
    m_RequestedClose = false;
}


bool CMainWindow::OnCloseWindowCheck()
{
    QString unsavedLibraries = tr("Libraries with unsaved changes: \n");
    bool needToWarn = false;
    CRY_ASSERT(GetIEditor());
    IEditorParticleManager* manager = GetIEditor()->GetParticleManager();
    CRY_ASSERT(manager);
    for (unsigned int i = 0; i < manager->GetLibraryCount(); i++)
    {
        IDataBaseLibrary* lib = manager->GetLibrary(i);
        CRY_ASSERT(lib);
        if (!lib->IsLevelLibrary() && lib->IsModified())
        {
            needToWarn = true;
            unsavedLibraries.append(lib->GetName() + tr("\n"));
        }
    }

    bool canClose = false;

    if (needToWarn)
    {
        QMessageBox dlg(QMessageBox::Warning, tr("Warning"), tr("There are unsaved libraries, these will be lost upon exiting the editor.\n") + unsavedLibraries,
            QMessageBox::Close | QMessageBox::Cancel, this);
        if (dlg.exec() == QMessageBox::Button::Close)
        {
            canClose = true;
        }
    }
    else
    {
        canClose = true;
    }
    
    return canClose;
}


#pragma endregion

void CMainWindow::NotifyExceptSelf(EEditorNotifyEvent notification)
{
    CRY_ASSERT(GetIEditor());
    GetIEditor()->NotifyExcept((EEditorNotifyEvent)notification, this);
}

bool CMainWindow::ShortcutEvent(QShortcutEvent* e)
{
    bool eventHandled = false;

    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    QString pathOfShortcut = GetIEditor()->GetParticleUtils()->HotKey_GetPressedHotkey(e);
    QKeySequence hotkey = GetIEditor()->GetParticleUtils()->HotKey_GetShortcut(pathOfShortcut.toUtf8());
    if (!pathOfShortcut.isEmpty())
    {
        for (unsigned int i = 0; i < m_shortcuts.count(); i++)
        {
            if (m_shortcuts[i] && hotkey == m_shortcuts[i]->key())
            {
                m_shortcuts[i]->activated();
                e->accept();
                eventHandled = true;
            }
        }
    }

    return eventHandled;
}

void CMainWindow::keyPressEvent(QKeyEvent* event)
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    QString pathOfShortcut = GetIEditor()->GetParticleUtils()->HotKey_GetPressedHotkey(event);
    QKeySequence hotkey = GetIEditor()->GetParticleUtils()->HotKey_GetShortcut(pathOfShortcut.toUtf8());

    if (!pathOfShortcut.isEmpty())
    {
        bool handled = false;
        for (unsigned int i = 0; i < m_shortcuts.count(); i++)
        {
            if (m_shortcuts[i] && hotkey == m_shortcuts[i]->key())
            {
                m_shortcuts[i]->activated();
                handled = true;
            }
        }

        if (handled)
        {
            event->setAccepted(true);
            return;
        }
    }
    QMainWindow::keyPressEvent(event);
}

bool CMainWindow::event(QEvent* e)
{
    if (e->type() == QEvent::Shortcut)
    {
        if (ShortcutEvent((QShortcutEvent*)e))
        {
            return true;
        }
    }

    return QMainWindow::event(e);
}

void CMainWindow::Preview_PopulateTitleBarMenu(QMenu* toAddTo)
{
    CRY_ASSERT(m_libraryTreeViewDock);

    QMenu* menu = toAddTo;
    QAction* action = nullptr;

#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, &CMainWindow::callback);          \
    }

    ADD_ACTION("Close", OnActionViewHidePreview, "Close preview panel", "", QKeySequence());
#undef ADD_ACTION
}

void CMainWindow::Library_PopulateTitleBarMenu(QMenu* toAddTo)
{
    CRY_ASSERT(m_libraryTreeViewDock);

    QMenu* menu = toAddTo;

    // Set connection type to QueuedConnection to make sure the action triggered after menu get deleted, otherwise it may cause duplicate deletion.
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::ADD, tr("Add library"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::IMPORT, tr("Import..."), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::IMPORT_LEVEL_LIBRARY, tr("Import Level Library"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::SAVE_ALL, tr("Save all"), false, menu, Qt::QueuedConnection));

    QAction* action = nullptr;
#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, &CMainWindow::callback);          \
    }

    ADD_ACTION("Close", OnActionViewHideLibrary, "Close library panel", "", QKeySequence());

#undef ADD_ACTION
}

void CMainWindow::Attribute_PopulateTitleBarMenu(QMenu* toAddTo)
{
    CRY_ASSERT(m_libraryTreeViewDock);
    CRY_ASSERT(m_attributeViewDock);
    QMenu* menu = toAddTo;
    QAction* action = nullptr;

#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, &CMainWindow::callback);          \
    }

    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_ITEM, "", tr("Create new emitter"), false, menu, Qt::QueuedConnection));
    m_attributeViewDock->AddLayoutMenuItemsTo(menu); //would be better setup with GetMenuAction like requests. (see above)
    ADD_ACTION("Close", OnActionViewHideAttributes, "Close attributes panel", "", QKeySequence());
#undef ADD_ACTION
}

void CMainWindow::LOD_PopulateTitleBarMenu(QMenu* toAddTo)
{
    CRY_ASSERT(m_LoDDock);
    QMenu* menu = toAddTo;
    QAction* action = nullptr;


    #define ADD_ACTION(menuD, textD, actionEnum)                                                              \
    {                                                                                                         \
        QAction* actiond = menuD->addAction(textD);                                                           \
        connect(actiond, &QAction::triggered, this, [=](){ m_LoDDock->PerformTitleMenuAction(actionEnum); }); \
    }
    //End define

    //AddLevel
    ADD_ACTION(menu, "Add level", DockableLODPanel::TitleMenuActions::AddLevel);

    //Arrange
    QMenu* arrangeMenu = menu->addMenu("Arrange");

    //Arrange-> Move up
    ADD_ACTION(arrangeMenu, "Move Up", DockableLODPanel::TitleMenuActions::MoveUp);

    //Arrange > Move down
    ADD_ACTION(arrangeMenu, "Move Down", DockableLODPanel::TitleMenuActions::MoveDown);

    //Arrange > Move to top
    ADD_ACTION(arrangeMenu, "Move to top", DockableLODPanel::TitleMenuActions::MoveToTop);

    //Arrange > Move to bottom
    ADD_ACTION(arrangeMenu, "Move to bottom", DockableLODPanel::TitleMenuActions::MoveToBottom);

    //Jump to first
    ADD_ACTION(menu, "Jump to first", DockableLODPanel::TitleMenuActions::JumpToFirst);

    //Jump to last
    ADD_ACTION(menu, "Jump to Last", DockableLODPanel::TitleMenuActions::JumpToLast);

    //Remove
    ADD_ACTION(menu, "Remove", DockableLODPanel::TitleMenuActions::Remove);

    //Remove All
    ADD_ACTION(menu, "Remove all", DockableLODPanel::TitleMenuActions::RemoveAll);

    //Close
    action = menu->addAction("Close");
    connect(action, &QAction::triggered, this, [=](){ OnActionViewHideLOD(); });
#undef ADD_ACTION
}

void CMainWindow::Library_PopulateItemContextMenu(CLibraryTreeViewItem* focusedItem, ContextMenu* toAddTo)
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    CRY_ASSERT(m_libraryTreeViewDock);
    CRY_ASSERT(m_attributeViewDock);
    CRY_ASSERT(toAddTo);

    QAction* action = nullptr;
    QString itemName = focusedItem ? focusedItem->GetFullPath() : "";
    QVector<CLibraryTreeViewItem*> selectedItems = m_libraryTreeViewDock->GetSelectedTreeItems();

    //OPEN IN NEW TAB/////////////////////////////////////////////////////////
    action = m_libraryTreeViewDock->GetMenuAction(DockableParticleLibraryPanel::ItemActions::OPEN_IN_NEW_TAB, itemName, tr("Open in new tab"), false, toAddTo, Qt::QueuedConnection);
    toAddTo->addAction(action);
    action->setDisabled(focusedItem->IsVirtualItem());
    toAddTo->addSeparator();
    //ADD NEW EMITTER/FOLDER//////////////////////////////////////////////////
    QMenu* submenu = new ContextMenu("Add New", toAddTo);
    toAddTo->addMenu(submenu);
    action = m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_ITEM, itemName, tr("Add Particle"), false, toAddTo, Qt::QueuedConnection);
    submenu->addAction(action);
    if (focusedItem && focusedItem->IsVirtualItem() && selectedItems.count() <= 1)
    {
        action = m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_FOLDER, itemName, tr("Add Folder"), false, toAddTo, Qt::QueuedConnection);
        submenu->addAction(action);
    }
    else if (focusedItem && selectedItems.count() <= 1) //we are on an item not a folder
    {
        action = toAddTo->addAction(tr("Add LOD"));
        connect(action, &QAction::triggered, this, [=]()
            {
                m_LoDDock->OnAddLod(focusedItem);
            });
    }
    toAddTo->addSeparator();
    //COPY////////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::COPY, itemName, tr("Copy"), false, toAddTo, Qt::QueuedConnection));
    //PASTE///////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::PASTE, itemName, tr("Paste"), false, toAddTo, Qt::QueuedConnection));
    //DUPLICATE///////////////////////////////////////////////////////////////
    action = toAddTo->addAction(tr("Duplicate"));
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Duplicate"));
    if (focusedItem && !focusedItem->IsVirtualItem() && selectedItems.count() <= 1)
    {
        CParticleItem* pParticle = static_cast<CParticleItem*>(focusedItem->GetItem());
        connect(action, &QAction::triggered, this, [=]()
            {
                Library_ItemDuplicated(QString(pParticle->GetFullName()));
            }, Qt::QueuedConnection);
    }
    else
    {
        action->setEnabled(false);
    }
    //COPY PATH///////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::COPY_PATH, itemName, tr("Copy Path"), false, toAddTo, Qt::QueuedConnection));
    //RENAME//////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::RENAME, itemName, tr("Rename"), false, toAddTo, Qt::QueuedConnection));
    //DELETE//////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::DESTROY, itemName, tr("Delete"), false, toAddTo, Qt::QueuedConnection));

    //ACTIVATE TOGGLE/////////////////////////////////////////////////////////
    //If the item is a folder then it does not have concept of enable/disable
    // folders are treeview items that are not considered particles.
    if (focusedItem && !focusedItem->IsVirtualItem())
    {
        CParticleItem* pParticle = static_cast<CParticleItem*>(focusedItem->GetItem());
        //ENABLE//////////////////////////////////////////////////////////////////
        toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::ENABLE, itemName, tr("Enable"), false, toAddTo, Qt::QueuedConnection));
        //DISABLE/////////////////////////////////////////////////////////////////
        toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::DISABLE, itemName, tr("Disable"), false, toAddTo, Qt::QueuedConnection));
    }

    //ACTIVATE ALL TOGGLE/////////////////////////////////////////////////////
    if (focusedItem && focusedItem->childCount() > 0)
    {
        action = toAddTo->addAction(tr("Enable All"));
        action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("File Menu.Enable All"));
        connect(action, &QAction::triggered, this, [=]()
            {
                EditorUIPlugin::ScopedBatchUndoPtr batchUndo;
                EBUS_EVENT_RESULT(batchUndo, EditorLibraryUndoRequestsBus, AddScopedBatchUndo, "Enable All");
                QtRecurseAll(focusedItem, [=](QTreeWidgetItem* item)
                    {
                        CLibraryTreeViewItem* titem = static_cast<CLibraryTreeViewItem*>(item);
                        if (!titem->IsVirtualItem())
                        {
                            CParticleItem* pParticle = static_cast<CParticleItem*>(titem->GetItem());
                            m_attributeViewDock->SetEnabledParameter(pParticle, true);
                            titem->setCheckState(LIBRARY_TREEVIEW_INDICATOR_COLUMN, Qt::CheckState::Checked);
                        }
                    });
            });

        action = toAddTo->addAction(tr("Disable All"));
        action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("File Menu.Disable All"));
        connect(action, &QAction::triggered, this, [=]()
            {
                EditorUIPlugin::ScopedBatchUndoPtr batchUndo;
                EBUS_EVENT_RESULT(batchUndo, EditorLibraryUndoRequestsBus, AddScopedBatchUndo, "Disable All");

                QtRecurseAll(focusedItem, [=](QTreeWidgetItem* item)
                    {
                        CLibraryTreeViewItem* titem = static_cast<CLibraryTreeViewItem*>(item);
                        if (!titem->IsVirtualItem())
                        {
                            CParticleItem* pParticle = static_cast<CParticleItem*>(titem->GetItem());
                            m_attributeViewDock->SetEnabledParameter(pParticle, false);
                            titem->setCheckState(LIBRARY_TREEVIEW_INDICATOR_COLUMN, Qt::CheckState::Unchecked);
                        }
                    });
            });
    }
    toAddTo->addSeparator();
    //EXPAND ALL//////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::EXPAND_ALL, itemName, tr("Expand All"), false, toAddTo, Qt::QueuedConnection));
    //COLLAPSE ALL////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::COLLAPSE_ALL, itemName, tr("Collapse All"), false, toAddTo, Qt::QueuedConnection));
    toAddTo->addSeparator();
    //GROUP///////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::GROUP, itemName, tr("Group"), false, toAddTo, Qt::QueuedConnection));
    //UNGROUP/////////////////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::UNGROUP, itemName, tr("Ungroup"), false, toAddTo, Qt::QueuedConnection));
    //RESET TO DEFAULT////////////////////////////////////////////////////////
    action = toAddTo->addAction(tr("Reset to default"));
    if (focusedItem && !focusedItem->IsVirtualItem() && selectedItems.count() <= 1)
    {
        CParticleItem* pParticle = static_cast<CParticleItem*>(focusedItem->GetItem());
        connect(action, &QAction::triggered, this, [=]()
            {
                Library_ItemReset(pParticle);
                m_libraryTreeViewDock->ForceLibrarySync(focusedItem->GetLibraryName());
            }, Qt::QueuedConnection);
    }
    else
    {
        action->setEnabled(false);
    }
}

void CMainWindow::Attribute_PopulateTabBarContextMenu(const QString& libraryName, const QString& itemName, ContextMenu* toAddTo)
{
    QAction* action = m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_ITEM, itemName, tr("Create new emitter"), false, toAddTo, Qt::QueuedConnection);

    //DUPLICATE///////////////////////////////////////////////////////////////
    action = toAddTo->addAction(tr("Duplicate"));
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Duplicate"));
    if (!libraryName.isEmpty() && !itemName.isEmpty())
    {
        CLibraryTreeViewItem* treeItem = m_libraryTreeViewDock->GetTreeItemFromPath(libraryName + "." + itemName);
        if (treeItem && !treeItem->IsVirtualItem())
        {
            connect(action, &QAction::triggered, this, [=]()
                {
                    Library_ItemDuplicated(libraryName + "." + itemName);
                });
        }
        else
        {
            action->setEnabled(false);
        }
    }
    else
    {
        action->setEnabled(false);
    }

    toAddTo->addAction(m_attributeViewDock->GetMenuAction(DockableAttributePanel::TabActions::CLOSE, itemName, tr("Close"), toAddTo));
    toAddTo->addAction(m_attributeViewDock->GetMenuAction(DockableAttributePanel::TabActions::CLOSE_ALL, itemName, tr("Close all"), toAddTo));
    toAddTo->addAction(m_attributeViewDock->GetMenuAction(DockableAttributePanel::TabActions::CLOSE_ALL_BUT_THIS, itemName, tr("Close all but this"), toAddTo));

    //RESET TO DEFAULT////////////////////////////////////////////////////////
    action = toAddTo->addAction(tr("Reset to default"));
    if (!libraryName.isEmpty() && !itemName.isEmpty())
    {
        CLibraryTreeViewItem* treeItem = m_libraryTreeViewDock->GetTreeItemFromPath(itemName);
        if (treeItem && !treeItem->IsVirtualItem())
        {
            CParticleItem* pParticle = static_cast<CParticleItem*>(treeItem->GetItem());
            connect(action, &QAction::triggered, this, [=]()
                {
                    Library_ItemReset(pParticle, m_attributeViewDock->GetCurrentLod());
                    m_libraryTreeViewDock->ForceLibrarySync(libraryName);
                });
        }
        else
        {
            action->setEnabled(false);
        }
    }
}

//POSSIBLE LEGACY ONLY SYNC USAGE HERE
void CMainWindow::Attribute_ParameterChanged(const QString& libraryName, const QString& itemName)
{
    //Currently it is not cared what item from what library has changed as this is being done to just generically notify others that a particle has updated
    // NOTE: used for legacy editor syncronization
    NotifyExceptSelf(eNotify_OnParticleUpdate);
    m_previewDock->ForceParticleEmitterRestart();
}
//POSSIBLE LEGACY ONLY SYNC USAGE HERE


void CMainWindow::OnItemUndoPoint(const QString& itemFullName, bool selected, SLodInfo* lod)
{
    bool isSuspend = false;
    EBUS_EVENT_RESULT(isSuspend, EditorLibraryUndoRequestsBus, IsSuspend);
    if (isSuspend)
    {
        return;
    }

    //all particle item's attribute changes will landed here and need to be saved for undo
    CParticleItem* item = static_cast<CParticleItem*>(GetIEditor()->GetParticleManager()->FindItemByName(itemFullName));
    if (item)
    {
        int lodIdx = item->GetEffect()->GetLevelOfDetailIndex(lod);
        EBUS_EVENT(EditorLibraryUndoRequestsBus, AddItemUndo, itemFullName.toUtf8().data(), selected, lodIdx);
        item->SetModified(true);
    }
}

void CMainWindow::File_PopulateMenu()
{
    CRY_ASSERT(m_fileMenu);
    CRY_ASSERT(m_libraryTreeViewDock);
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    QMenu* menu = m_fileMenu;
    menu->clear();
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::ADD, tr("Add library"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::IMPORT, tr("Import..."), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::IMPORT_LEVEL_LIBRARY, tr("Import Level Library"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::SAVE_ALL, tr("Save all"), false, menu, Qt::QueuedConnection));

    QAction* action = nullptr;
#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, &CMainWindow::callback);          \
    }

    ADD_ACTION("Close", OnActionClose, "Close Editor", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("File Menu.Close"));
#undef ADD_ACTION
}

void CMainWindow::Edit_PopulateMenu()
{
    CRY_ASSERT(m_editMenu);
    CRY_ASSERT(m_libraryTreeViewDock);
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());

    QMenu* menu = m_editMenu;
    menu->clear();
    QAction* action = nullptr;
#define ADD_ACTION(text, callback, tooltip, icon, key)                               \
    {                                                                                \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text)); \
        if (key != 0) {                                                              \
            action->setShortcut(key); }                                              \
        action->setToolTip(tr(tooltip));                                             \
        connect(action, &QAction::triggered, this, &CMainWindow::callback);          \
    }

    ADD_ACTION("Undo", OnActionStandardUndo, "Undo", "standardUndo.png", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Undo"));
    m_undoAction = action;
    bool hasUndo = false;
    EBUS_EVENT_RESULT(hasUndo, EditorLibraryUndoRequestsBus, HasUndo);
    action->setEnabled(hasUndo);
    ADD_ACTION("Redo", OnActionStandardRedo, "Redo", "standardRedo.png", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Redo"));    
    m_redoAction = action;
    bool hasRedo = false;
    EBUS_EVENT_RESULT(hasRedo, EditorLibraryUndoRequestsBus, HasRedo);
    action->setEnabled(hasRedo);

    //COPY////////////////////////////////////////////////////////////////////
    m_copyAction = menu->addAction(tr("Copy"));
    m_copyAction->setIcon(QIcon("Editor/UI/Icons/toolbar/standardCopy.png"));
    m_copyAction->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Copy"));
    connect(m_copyAction, &QAction::triggered, this, [this]
        {
            QVector<CBaseLibraryItem*> selectedItems = m_libraryTreeViewDock->GetSelectedItems();
             if (!selectedItems.isEmpty() && selectedItems.first())
             {
                 Library_ItemCopied(selectedItems.first());
             }
        });

    //PASTE///////////////////////////////////////////////////////////////////
    menu->addAction(m_libraryTreeViewDock->GetParticleAction(DockableParticleLibraryPanel::ParticleActions::PASTE, "", tr("Paste"), false, menu, Qt::QueuedConnection));
    
    //DUPLICATE///////////////////////////////////////////////////////////////
    m_duplicateAction = menu->addAction(tr("Duplicate"));
    m_duplicateAction->setIcon(QIcon("Editor/UI/Icons/toolbar/standardDuplicate.png"));
    m_duplicateAction->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Duplicate"));
    connect(m_duplicateAction, &QAction::triggered, this, [this]
        {
            QVector<CBaseLibraryItem*> selectedItems = m_libraryTreeViewDock->GetSelectedItems();
            if (selectedItems.count() == 1 && selectedItems.first())
            {
                Library_ItemDuplicated("");
            }
        });

    ////////////////////////////////////////////////////
    //Action Add LOD
    m_addLODAction = menu->addAction(tr("Add LOD"));
    connect(m_addLODAction, &QAction::triggered, this, [this]
        {
            QVector<CBaseLibraryItem*> selectedItems = m_libraryTreeViewDock->GetSelectedItems();
            if (selectedItems.count() && selectedItems.first())
            {
                auto pParticle = static_cast<CParticleItem*>(selectedItems.first());
                if (pParticle->IsParticleItem)
                {
                    CLibraryTreeViewItem* treeitem = m_libraryTreeViewDock->GetTreeItemFromPath(QString(selectedItems.first()->GetFullName()));
                    if (treeitem)
                    {
                        m_LoDDock->OnAddLod(treeitem);
                    }
                }
            }
        });


    m_renameSelectedItem = m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::RENAME, "", tr("Rename"), false, menu, Qt::QueuedConnection);
    menu->addAction(m_renameSelectedItem);

    m_deleteSelectedItem = m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::DESTROY, "", tr("Delete"), false, menu, Qt::QueuedConnection);
    menu->addAction(m_deleteSelectedItem);
    menu->actions().back()->setIcon(QIcon("Editor/UI/Icons/toolbar/itemRemove.png"));

    //////////////////////////////////////////////////////////////////////////
    //Reset selected items to default state
    m_resetSelectedItem = menu->addAction(tr("Reset to default"));
    connect(m_resetSelectedItem, &QAction::triggered, this, [this]
        {
            QVector<CBaseLibraryItem*> selectedItems = m_libraryTreeViewDock->GetSelectedItems();
            if (selectedItems.count() && selectedItems.first())
            {
                QVector<CBaseLibraryItem*> tempSelectionList = m_libraryTreeViewDock->GetSelectedItems();
                while (tempSelectionList.count() > 0)
                {
                    CParticleItem* pParticle = static_cast<CParticleItem*>(tempSelectionList.takeFirst());
                    Library_ItemReset(pParticle);
                }
            }
        });
    //////////////////////////////////////////////////////////////////////////

    action = menu->addAction(tr("Edit Hotkeys"));
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Edit Hotkeys"));
    connect(action, &QAction::triggered, this, [&]()
        {
            QKeySequenceEditorDialog* dlg = UIFactory::GetHotkeyEditor();
            if (dlg->exec() == QDialog::Accepted)
            {
                GetIEditor()->GetParticleUtils()->HotKey_SaveCurrent();
                UnregisterActions();
                CreateMenuBar();
                CreateShortcuts();
                RegisterActions();
                m_libraryTreeViewDock->RemapHotKeys();
            }
        });
#undef ADD_ACTION
}

void CMainWindow::View_PopulateMenu()
{
    CRY_ASSERT(m_viewMenu);
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    QMenu* menu = m_viewMenu;
    menu->clear();
    QAction* action = nullptr;
    m_showLayoutMenu = new QMenu(tr("Show Layouts"), menu);
#define ADD_ACTION(text, callback, tooltip, icon, key)                                            \
    {                                                                                             \
        action = menu->addAction(QIcon("Editor/UI/Icons/toolbar/" icon), tr(text));              \
        if (key != 0) {                                                                           \
            action->setShortcut(key); }                                                           \
        action->setToolTip(tr(tooltip));                                                          \
        connect(action, &QAction::triggered, this, &CMainWindow::callback, Qt::QueuedConnection); \
    }

    QList<QAction*> menuActions = m_layoutMenu->actions();
    for (int i = 0, numActions = menuActions.size(); i < numActions; ++i)
    {
        action = menuActions[i];
        QFileInfo fileInfo(action->text());
        if (!fileInfo.exists())
        {
            m_layoutMenu->removeAction(action);
            action->setParent(NULL);
            delete action;
        }
    }
    StateSave_layoutMenu();


    menuActions = m_layoutMenu->actions();
    for (int i = 1, numActions = menuActions.size(); i <= MAX_NUMBER_OF_LAYOUT_MENU && i <= numActions; i++)
    {
        action = menuActions[numActions - i];
        m_showLayoutMenu->addAction(action);
    }

    //Add the "default" layout option to show layouts
    action = m_showLayoutMenu->addAction("Default Layout");
    connect(action, &QAction::triggered, this, &CMainWindow::ResetToDefaultEditorLayout);

    menu->addMenu(m_showLayoutMenu);
    ADD_ACTION("Import layout...", OnActionViewImportLayout, "Import layout", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.Import layouts"));
    ADD_ACTION("Export layout...", OnActionViewExportLayout, "Export layout", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.Export layouts"));
    ADD_ACTION((m_attributeViewDock->isHidden() ? "Show attributes" : "Hide attributes"), OnActionViewHideAttributes, "Show attribute view", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.Show attribute"));
    ADD_ACTION((m_libraryTreeViewDock->isHidden() ? "Show library" : "Hide library"), OnActionViewHideLibrary, "Show Library View", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.show library"));
    ADD_ACTION((m_previewDock->isHidden() ? "Show preview" : "Hide preview"), OnActionViewHidePreview, "Show Preview View", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.show preview"));
    ADD_ACTION((m_LoDDock->isHidden() ? "Show Level of Detail" : "Hide Level of Detail"), OnActionViewHideLOD, "Show Level of Detail View", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View.show level of detail"));

    ADD_ACTION("Reset to Default Layout", ResetToDefaultEditorLayout, "Reset layout", "", GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("View Menu.Reset Layout"));

    ADD_ACTION("Documentation", OnActionViewDocumentation, "Open Particle Editor documentation.", "", 0);

#undef ADD_ACTION
}

void CMainWindow::UpdatePalette()
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetEditorSettings());

    QString stylesheetPath = "Editor/Styles/stylesheet_Dark.qss";
    //Reload Style sheet with the new colors
    QString processedStyle = ProcessStyleSheet(stylesheetPath);

    if (processedStyle.length() > 0)
    {
        hide();
        setStyleSheet(processedStyle); // Stylesheet also propagates to the docks

        QGradientColorDialog* gdlg = UIFactory::GetGradientEditor(SCurveEditorContent(), { QGradientStop(0.0, Qt::white), QGradientStop(1.0, Qt::white) });
        gdlg->setStyleSheet(processedStyle);
        gdlg->UpdateColors(m_StyleColors);

        QKeySequenceEditorDialog* hkdlg = UIFactory::GetHotkeyEditor();
        hkdlg->setStyleSheet(processedStyle);

        QColorEyeDropper* cedp = UIFactory::GetColorEyeDropper();
        cedp->setStyleSheet(processedStyle);

        m_attributeViewDock->UpdateColors(m_StyleColors);
        m_libraryTreeViewDock->UpdateColors(m_StyleColors);
        ensurePolished();
        show();
    }
}

QString CMainWindow::ProcessStyleSheet(QString const& fileName)
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetSystem());
    CRY_ASSERT(GetIEditor()->GetSystem()->GetILog());

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly))
    {
        char buffer[512];
        sprintf(buffer, "Stylesheet '%s' is invalid.", fileName.toUtf8().data());
        GetIEditor()->GetSystem()->GetILog()->LogWarning(buffer);
        return "";
    }

    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////

    QXmlStreamReader stream(&file);
    QString qss = "";
    while (!stream.atEnd())
    {
        stream.readNext();
        if (stream.isStartElement())
        {
            //COLORDEF
            if (stream.name() == "COLORDEF")
            {
                QPair<QString, QColor> key;
                key.second = QColor(Qt::red);
                QXmlStreamAttributes att = stream.attributes();
                for (QXmlStreamAttribute attr : att)
                {
                    if (attr.name().compare(QLatin1String("name"), Qt::CaseInsensitive) == 0)
                    {
                        key.first = attr.value().toString();
                    }
                    if (attr.name().compare(QLatin1String("value"), Qt::CaseInsensitive) == 0)
                    {
                        QString fullColor = attr.value().toString();
                        QColor temp;
                        temp.setRgba(fullColor.toUInt(0, 16));
                        key.second = temp;
                    }
                }
                if (!key.first.isEmpty())
                {
                    m_StyleColors.insert(key.first, key.second);
                }
            }

            //QSS
            if (stream.name() == "QSS")
            {
                qss = stream.readElementText();
            }
        }
    }
    file.close();

    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////

    QString out = "";
    QString varName = "";
    auto i = qss.cbegin();
    //Normal State
normal:
    while (i != qss.end())
    {
        char c = i->toLatin1();
        switch (c)
        {
        case '@':
            i++;
            goto var;
        case '\0':
            goto end;
        default:
            out.append(*i);
            i++;
        }
    }
    goto end;
var:
    //Reading Var State
    while (i != qss.end())
    {
        char c = i->toLatin1();

        //All characters valid in indentifier
        if ((c >= 'a' && c <= 'z') ||
            (c >= 'A' && c <= 'Z')
            )
        {
            varName.append(*i);
            i++;
        }
        else
        {
            switch (c)
            {
            case '\0':
                goto end;
            default:
                //We are finished with reading the current varName
                out.append(GetColorStringByName(varName));
                varName.clear();
                out.append(*i);
                i++;
                goto normal;
            }
        }
    }
    goto end;
end:

    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////////////////

    return out;
}

QString CMainWindow::GetColorStringByName(QString const& name)
{
    auto it = m_StyleColors.find(name);
    if (it != m_StyleColors.end())
    {
        int r, g, b, a;
        it->getRgb(&r, &g, &b, &a);
        QString str;
        str = str.asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
        return str;
    }
    else
    {
        return "#FFFF00FF"; //Debug color
    }
}


void CMainWindow::CreateShortcuts()
{
    CRY_ASSERT(m_attributeViewDock);
    CRY_ASSERT(m_previewDock);

    while (m_shortcuts.count() > 0)
    {
        m_shortcuts.removeLast();
    }

    //File Menu
    m_shortcuts.push_back(new QShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Menus.File Menu"), this));
    connect(m_shortcuts.back(), &QShortcut::activated, this, [&]()
        {
            CRY_ASSERT(GetIEditor());
            CRY_ASSERT(GetIEditor()->GetParticleUtils());
            if (!GetIEditor()->GetParticleUtils()->HotKey_IsEnabled())
            {
                return;
            }
            if (m_fileMenu && m_menuBar)
            {
                QPoint p = m_menuBar->mapToGlobal(m_menuBar->pos());
                p.setX(m_menuBar->mapToGlobal(m_fileMenu->pos()).x());
                p.setY(p.y() + m_menuBar->height());
                m_fileMenu->exec(p);
            }
        });

    //Edit Menu
    m_shortcuts.push_back(new QShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Menus.Edit Menu"), this));
    connect(m_shortcuts.back(), &QShortcut::activated, this, [&]()
        {
            CRY_ASSERT(GetIEditor());
            CRY_ASSERT(GetIEditor()->GetParticleUtils());
            if (!GetIEditor()->GetParticleUtils()->HotKey_IsEnabled())
            {
                return;
            }
            if (m_editMenu && m_menuBar)
            {
                QPoint p = m_menuBar->mapToGlobal(m_menuBar->pos());
                p.setX(m_menuBar->mapToGlobal(m_editMenu->pos()).x());
                p.setY(p.y() + m_menuBar->height());
                m_editMenu->exec(p);
            }
        });

    //View Menu
    m_shortcuts.push_back(new QShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Menus.View Menu"), this));
    connect(m_shortcuts.back(), &QShortcut::activated, this, [&]()
        {
            CRY_ASSERT(GetIEditor());
            CRY_ASSERT(GetIEditor()->GetParticleUtils());
            if (!GetIEditor()->GetParticleUtils()->HotKey_IsEnabled())
            {
                return;
            }
            if (m_viewMenu && m_menuBar)
            {
                QPoint p = m_menuBar->mapToGlobal(m_menuBar->pos());
                p.setX(m_menuBar->mapToGlobal(m_viewMenu->pos()).x());
                p.setY(p.y() + m_menuBar->height());
                m_viewMenu->exec(p);
            }
        });

    //Insert Comment
    m_shortcuts.push_back(m_attributeViewDock->GetShortcut(DockableAttributePanel::ParamShortcuts::InsertComment));

    //Focus Camera
    m_shortcuts.push_back(m_previewDock->GetShortcut(DockablePreviewPanel::Shortcuts::FocusCameraOnEmitter));
}

void CMainWindow::Library_ItemNameValidationRequired(IDataBaseItem* item, const QString& currentName, const QString& nextName, bool& proceed)
{
    if (nextName.isEmpty() || currentName == nextName)
    {
        proceed = false;
        return; // block name change
    }

    if (nextName.toStdString().find_first_not_of(VALID_LIBRARY_ITEM_CHARACTERS) != std::string::npos)
    {
        QMessageBox::warning(this, "Warning", "Invalid Item Name.Symbols / glyphs is not allowed in the name.\n");
        proceed = false;
        return; // block name change
    }
    proceed = true;
}

void CMainWindow::Library_ItemRenamed(IDataBaseItem* item, const QString& oldName, const QString& currentName, const QString newLib)
{
    if (!item)
    {
        return; //non-database item was modified (i.e. folder)
    }
    int lastGroup = currentName.lastIndexOf('.');
    QString newParent = currentName.left(lastGroup);
    QString libName = item->GetLibrary()->GetName();

    IEditorParticleManager* pParticleMgr = GetIEditor()->GetParticleManager();
    CParticleItem* pParent = static_cast<CParticleItem*>(pParticleMgr->FindItemByName(libName + "." + newParent));
    CParticleItem* pParticle = static_cast<CParticleItem*>(item);
    if (pParent && pParent->IsParticleItem && pParent != pParticle)
    {
        pParticle->SetParent(pParent);
    }
    else
    {
        pParticle->SetParent(NULL);
    }
    NotifyExceptSelf(eNotify_OnDataBaseUpdate);
}


void CMainWindow::Library_UpdateTreeItemStyle(IDataBaseItem* item, int column)
{
    if (!item)
    {
        return;
    }

    if (item->GetType() == EDB_TYPE_PARTICLE)
    {
        CParticleItem* pParticle = static_cast<CParticleItem*>(item);
        bool isLod = pParticle->GetEffect()->HasLevelOfDetail();
        CLibraryTreeViewItem* treeItem = m_libraryTreeViewDock->GetTreeItemFromPath(tr(item->GetFullName().toUtf8().data()));
        if (!treeItem)
        {
            //when grouping/ungrouping we can sometimes attempt to update a style before the treeitem actually exists.
            //when this happens we return to prevent crashing.
            return;
        }
        CRY_ASSERT(pParticle->IsParticleItem);
        // After add in group particle. The icon should updated based on the group.
        ParticleParams params = pParticle->GetEffect()->GetParticleParams();
        if (isLod)
        {
            if (params.bGroup)
            {
                treeItem->setIcon(column, m_groupWithLodIcon);
            }
            else
            {
                treeItem->setIcon(column, m_lodIcon);
            }
        }
        else
        {
            if (params.bGroup)
            {
                treeItem->setIcon(column, m_groupIcon);
            }
            else
            {
                treeItem->setIcon(column, m_emptyIcon);
            }
        }
    }
}

void CMainWindow::Library_ItemCopied(IDataBaseItem* item)
{
    if (!item)
    {
        return;
    }
    AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
    XmlNodeRef node = GetIEditor()->GetSystem()->CreateXmlNode("Particles");
    CBaseLibraryItem::SerializeContext ctx(node, false);
    ctx.bCopyPaste = true;

    CClipboard clipboard(this);
    item->Serialize(ctx);
    clipboard.Put(node);
}

void CMainWindow::Library_ItemPasted(IDataBaseItem* target, bool overrideSafety /*= false*/)
{
    if (!target)
    {
        return;
    }
    AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);

    CClipboard clipboard(this);
    if (clipboard.IsEmpty())
    {
        return;
    }
    XmlNodeRef node = clipboard.Get();

    if (!node)
    {
        return;
    }

    CBaseLibraryManager* mngr = static_cast<CBaseLibraryManager*>(GetIEditor()->GetParticleManager());

    EditorUIPlugin::ScopedLibraryModifyUndoPtr modifyUndo;
    AZStd::string libName = target->GetLibrary()->GetName().toUtf8().data();
    EBUS_EVENT_RESULT(modifyUndo, EditorLibraryUndoRequestsBus, AddScopedLibraryModifyUndo, libName);

    // Reset selection since we will set the selection in the end.
    // This will also save time for ForceLibrarySync since it will try to restore selection if there were some.
    if (target->GetLibrary())
    {
        m_libraryTreeViewDock->SelectLibraryAndItemByName(QString(target->GetLibrary()->GetName()), "");
    }

    if (strcmp(node->getTag(), "Childs") == 0)
    {
        AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "Copy to childs");
        //copy the selected particles to the children of the selected item
        //it is not possible to only copy params as the params may overwrite eachother.
        //so we don't prompt

        //get the xml of the target
        XmlNodeRef particleNode = GetIEditor()->GetSystem()->CreateXmlNode("Particles");
        CBaseLibraryItem::SerializeContext ctx(particleNode, false);
        ctx.bCopyPaste = true;
        target->Serialize(ctx);
        //check for an existing "Child" tab if found remove ours
        XmlNodeRef childRoot = particleNode->findChild("Childs");
        if (childRoot == NULL)
        {
            particleNode->addChild(node);
        }
        else
        {
            for (int i = 0; i < node->getChildCount(); i++)
            {
                XmlNodeRef copiedEmitter = node->getChild(i);
                childRoot->addChild(copiedEmitter);
            }
        }
        GetIEditor()->GetParticleManager()->PasteToParticleItem(static_cast<CParticleItem*>(target), particleNode, true);
        if (target->GetLibrary())
        {
            AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "ForceLibrarySync");
            m_libraryTreeViewDock->ForceLibrarySync(QString(target->GetLibrary()->GetName()));
        }
    }
    else
    {
        bool replaceAll = true;
        if (!overrideSafety)
        {
            QCopyModalDialog dlg(this, QString(target->GetFullName()));
            bool result = dlg.exec(replaceAll);
            if (!result)
            {
                return;
            }
        }

        if (strcmp(node->getTag(), "Particles") == 0)
        {
            AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "Copy to particles");
            CParticleItem* pParticle = static_cast<CParticleItem*>(target);
            CRY_ASSERT(pParticle);
            node->delAttr("Name");
            node->setAttr("Name", target->GetName().toUtf8().data());
            GetIEditor()->GetParticleManager()->PasteToParticleItem(pParticle, node, replaceAll);
            if (target->GetLibrary())
            {
                AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "ForceLibrarySync");
                m_libraryTreeViewDock->ForceLibrarySync(QString(target->GetLibrary()->GetName()));
            }
        }
    }

    // set the target item as selected item
    if (target->GetLibrary())
    {
        AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::Editor, "SelectLibraryAndItemByName");
        m_libraryTreeViewDock->SelectLibraryAndItemByName(QString(target->GetLibrary()->GetName()), QString(target->GetName()));
    }
}

void CMainWindow::Library_ItemsPastedToFolder(IDataBaseLibrary* lib, const QStringList& PasteList)
{
    CClipboard clipboard(this);
    if (clipboard.IsEmpty())
    {
        return;
    }
    XmlNodeRef node = clipboard.Get();

    if (!node)
    {
        return;
    }

    CBaseLibraryManager* mngr = static_cast<CBaseLibraryManager*>(GetIEditor()->GetParticleManager());
    //This function is only for multiple selections
    if (strcmp(node->getTag(), "Childs") != 0)
    {
        return;
    }

    EditorUIPlugin::ScopedLibraryModifyUndoPtr modifyUndo;
    AZStd::string libName = lib->GetName().toUtf8().data();
    EBUS_EVENT_RESULT(modifyUndo, EditorLibraryUndoRequestsBus, AddScopedLibraryModifyUndo, libName);

    for (int i = 0; i < node->getChildCount(); i++)
    {
        //The order in the clipboard matches the order in the list so we can just loop through
        XmlNodeRef copiedEmitter = node->getChild(i);
        CParticleItem* target = static_cast<CParticleItem*>(lib->GetManager()->FindItemByName(PasteList[i]));
        if (target)
        {
            GetIEditor()->GetParticleManager()->PasteToParticleItem(target, copiedEmitter, true);
        }
    }
}

CBaseLibraryItem* CreateNewItemFrom(CBaseLibraryManager* libraryManager, const QString& sourcePath, const bool isVirtual)
{
    AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);

    QStringList pathList = sourcePath.split('.');
    QString libraryName;
    QString newItemName;
    QString origItemName;

    libraryName = pathList.takeFirst();
    origItemName = pathList.join(".");
    newItemName = origItemName;

    newItemName = libraryManager->MakeUniqueItemName(newItemName, libraryName);
    IDataBaseLibrary* library = libraryManager->FindLibrary(libraryName);

    EditorUIPlugin::ScopedLibraryModifyUndoPtr modifyUndo;
    EBUS_EVENT_RESULT(modifyUndo, EditorLibraryUndoRequestsBus, AddScopedLibraryModifyUndo, AZStd::string(libraryName.toUtf8().data()));
    CBaseLibraryItem* newitem = reinterpret_cast<CBaseLibraryItem*>(libraryManager->CreateItem(library));

    if (isVirtual)
    {
        newitem->IsParticleItem = false;
    }

    newitem->SetName(newItemName.toUtf8().data());
    return newitem;
}

void CMainWindow::Library_ItemDuplicated(const QString& itemPath)
{
    AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
    QVector<CLibraryTreeViewItem*> selectedItems;
    if (itemPath.isEmpty())
    {
        selectedItems = m_libraryTreeViewDock->GetSelectedTreeItems();
    }
    else
    {
        selectedItems = m_libraryTreeViewDock->GetActionItems(itemPath);
    }

    //duplicate function only works with one selected item. The Library_ItemPasted will modify selected item list.
    if (selectedItems.count() != 1)
    {
        return;
    }

    CBaseLibraryManager* mngr = static_cast<CBaseLibraryManager*>(GetIEditor()->GetParticleManager());
   

    CLibraryTreeViewItem* item = selectedItems.takeFirst();
    if (item->IsVirtualItem()) // Could not duplicate folders
    {
        return;
    }
    QString fullPath = item->GetFullPath();
    bool isVirtual = item->IsVirtualItem();

    //undo batch for duplicate item: add item, paste item
    EditorUIPlugin::ScopedBatchUndoPtr batchUndo;
    EBUS_EVENT_RESULT(batchUndo, EditorLibraryUndoRequestsBus, AddScopedBatchUndo, "Add Item");

    // Copy the source item data to clipboard
    Library_ItemCopied(item->GetItem());

    // Create a new library item
    CBaseLibraryItem* newItem = CreateNewItemFrom(mngr, fullPath, isVirtual);

    // Copy the data in clipboard to the new item
    if (newItem)
    {
        Library_ItemPasted(newItem, true); //don't prompt
    }
}


void CMainWindow::Library_ItemReset(IDataBaseItem* target, SLodInfo *curLod)
{
    if (!target)
    {
        return;
    }
    CParticleItem* pParticle = static_cast<CParticleItem*>(target);
    if (!pParticle)
    {
        return;
    }

    EBUS_EVENT(EditorLibraryUndoRequestsBus, Suspend, true);

    pParticle->ResetToDefault(curLod);

    // Update particles.
    pParticle->Update();
    m_attributeViewDock->RefreshParameterUI(pParticle, curLod);

    EBUS_EVENT(EditorLibraryUndoRequestsBus, Suspend, false);

    //save item undo
    OnItemUndoPoint(target->GetFullName(), false, curLod);
}

void CMainWindow::Library_TreeFilledFromLibrary(IDataBaseLibrary* lib, CLibraryTreeView* view)
{
    //set all the disabled items
    QVector<CLibraryTreeViewItem*> items = view->GetChildrenOfTreeItem(""); //"" is the root node for trees
    //start at 1 to avoid the "InvisibleRootItem"
    for (unsigned int i = 1; i < items.count(); i++)
    {
        if (items[i]->IsVirtualItem())
        {
            continue;
        }
        else if (items[i]->GetItem())
        {
            CParticleItem* pParticle = static_cast<CParticleItem*>(items[i]->GetItem());
            IParticleEffect* pEffect = pParticle->GetEffect();
            if (pEffect)
            {
                items[i]->SetEnabled(pEffect->IsEnabled());
            }
        }
    }
    m_attributeViewDock->RefreshCurrentTab();
    m_LoDDock->RefreshGUI();
}

void CMainWindow::Library_ItemDragged(CLibraryTreeViewItem* item)
{
    if (!item || item->IsVirtualItem())
    {
        return;
    }

    //if it's not top effect, return
    CParticleItem* pParticle = static_cast<CParticleItem*>(item->GetItem());
    if ((!pParticle) || (!pParticle->GetEffect()) || pParticle->GetEffect()->GetParent())
    {
        return;
    }

    GetIEditor()->GetParticleUtils()->SetViewportDragOperation([](CViewport* vp, int dpx, int dpy, void* custom)
    {
        CParticleItem* parItem = (CParticleItem*)custom;

        if (parItem->GetLibrary()->IsModified())
        {
            parItem->GetLibrary()->Save();
        }

        //create an entity with EditorParticleComponent; modified from ComponentDataModel::DragDropHandler
        AzToolsFramework::ScopedUndoBatch undo("Create entity from particle effect");

        const AZStd::string name = AZStd::string::format("Particle_%s", parItem->GetEffect()->GetFullName().c_str());

        AZ::Entity* newEntity = aznew AZ::Entity(name.c_str());
        if (newEntity)
        {
            EBUS_EVENT(AzToolsFramework::EditorEntityContextRequestBus, AddRequiredComponents, *newEntity);
            // Set initial transform.
            auto* transformComponent = newEntity->FindComponent<AzToolsFramework::Components::TransformComponent>();
            if (transformComponent)
            {
                bool collideWithTerrain = true;
                QPoint screenPoint = { dpx, dpy };
                Vec3 worldPosition = vp->ViewToWorld(screenPoint, &collideWithTerrain);
                transformComponent->SetWorldTM(AZ::Transform::CreateTranslation(LYVec3ToAZVec3(worldPosition)));
            }

            // Add the entity to the editor context, which activates it and creates the sandbox object.
            EBUS_EVENT(AzToolsFramework::EditorEntityContextRequestBus, AddEditorEntity, newEntity);

            // Create Entity metrics event
            EBUS_EVENT(AzToolsFramework::EditorMetricsEventsBus, EntityCreated, newEntity->GetId());
                        
            // Add the EditorParticleComponent to entity
            newEntity->Deactivate();
            AZ::Uuid componentType = "{0F35739E-1B40-4497-860D-D6FF5D87A9D9}"; //class id of EditorParticleComponent
            AZ::Component* newComponent = newEntity->CreateComponent(componentType);
            AZ_Assert(newComponent, "Can't create EditorParticleComponent with uuid %s", componentType.ToString<AZStd::string>().c_str());

            // Add Component metrics event
            EBUS_EVENT(AzToolsFramework::EditorMetricsEventsBus, ComponentAdded, newEntity->GetId(), componentType);

            //reactive entity with component so the component can recieve following ebus requests
            newEntity->Activate();

            //setup library and emitter
            //try find the lib item
            IDataBaseItem* libItem = GetIEditor()->GetParticleManager()->FindItemByName(parItem->GetEffect()->GetFullName().c_str());
            if (libItem != nullptr)
            {
                AZStd::string libName = libItem->GetLibrary()->GetFilename().toUtf8().data();
                EBUS_EVENT_ID(newEntity->GetId(), LmbrCentral::EditorParticleComponentRequestBus, SetEmitter, AZStd::string(parItem->GetEffect()->GetFullName()), libName);
            }
            
            // Prepare undo command last so it captures the final state of the entity.
            AzToolsFramework::EntityCreateCommand* command = aznew AzToolsFramework::EntityCreateCommand(static_cast<AZ::u64>(newEntity->GetId()));
            command->Capture(newEntity);
            command->SetParent(undo.GetUndoBatch());

            // Select the new entity (and deselect others).
            AzToolsFramework::EntityIdList selection = { newEntity->GetId() };
            EBUS_EVENT(AzToolsFramework::ToolsApplicationRequests::Bus, SetSelectedEntities, selection);
        }

    }, item->GetItem());
}

void CMainWindow::Library_ItemAdded(CBaseLibraryItem* item, const QString& name)
{
    //handle the rename to correct hierarchy of the particle system
    Library_ItemRenamed(item, "", name);
    //paste in the amazon default here
    GetIEditor()->GetParticleManager()->LoadDefaultEmitter(static_cast<CParticleItem*>(item)->GetEffect());
}

void CMainWindow::Library_DragOperationFinished()
{
    GetIEditor()->GetParticleUtils()->SetViewportDragOperation(nullptr, nullptr);
    m_attributeViewDock->RefreshCurrentTab();
    NotifyExceptSelf(eNotify_OnDataBaseUpdate);
}

void CMainWindow::Library_ItemEnabledStateChanged(CBaseLibraryItem* item, const bool& state)
{
    CParticleItem* pParticle = static_cast<CParticleItem*>(item);
    m_attributeViewDock->SetEnabledParameter(pParticle, state);
    OnItemUndoPoint(item->GetFullName(), false, nullptr);
}

void CMainWindow::Library_PopulateLibraryContextMenu(ContextMenu* toAddTo, const QString& libName)
{
    CRY_ASSERT(m_libraryTreeViewDock);

    QMenu* menu = toAddTo;
    //ADD NEW EMITTER/FOLDER//////////////////////////////////////////////////
    QMenu* submenu = new ContextMenu("Add New", toAddTo);
    toAddTo->addMenu(submenu);
    submenu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_ITEM, libName, tr("Add Particle"), false, submenu, Qt::QueuedConnection));
    submenu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_FOLDER, libName, tr("Add Folder"), false, submenu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::SAVE, tr("Save"), false, menu, Qt::QueuedConnection));
    toAddTo->addSeparator();
    //DUPLICATE//////////////////////////////////////////////////////////////////
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::DUPLICATE_LIB, tr("Duplicate"), false, menu, Qt::QueuedConnection));
    //ENABLE-DISABLE ALL//////////////////////////////////////////////////////
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::ENABLE_ALL, tr("Enable All"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::DISABLE_ALL, tr("Disable All"), false, menu, Qt::QueuedConnection));
    //EXPAND-COLLAPSE ALL/////////////////////////////////////////////////////
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::TreeActions::EXPAND_ALL, tr("Expand All"), false, toAddTo, Qt::QueuedConnection));
    toAddTo->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::TreeActions::COLLAPSE_ALL, tr("Collapse All"), false, toAddTo, Qt::QueuedConnection));
    menu->addSeparator();
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::REMOVE, tr("Remove"), false, menu, Qt::QueuedConnection));
    menu->addAction(m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::LibraryActions::RELOAD, tr("Reload"), false, menu, Qt::QueuedConnection));
}

void CMainWindow::Library_DecorateTreesDefaultView(const QString& lib, DefaultViewWidget* view)
{
    CRY_ASSERT(view);
    view->SetLabel(tr("Particle library is empty\nAdd a new folder or emitter to continue."));
    view->AddButton("Add Particle", m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_ITEM, lib, tr("Add Particle"), false, view, Qt::QueuedConnection));
    view->AddButton("Add Folder", m_libraryTreeViewDock->GetMenuAction(DockableLibraryPanel::ItemActions::ADD_FOLDER, lib, tr("Add Particle"), false, view, Qt::QueuedConnection));
}

void CMainWindow::Library_SelecteItem(const QString& fullname)
{
    if (!fullname.isNull())
    {
        if (fullname == m_attributeViewDock->GetCurrentTabText())
        {   
            //already opened, refresh tab
            m_attributeViewDock->RefreshCurrentTab();
        }
        else
        {
            //wasn't selected. select the item
            QStringList undoParticleNameList = fullname.split(".");
            QString libname = undoParticleNameList.takeFirst();
            QString name = undoParticleNameList.join(".");
            m_libraryTreeViewDock->SelectLibraryAndItemByName(libname, name);
        }
    }
}

void CMainWindow::Library_CopyTreeItems(QVector<CLibraryTreeViewItem*> items, bool copyAsChild /*= false*/)
{
    if (items.count() == 1 && !copyAsChild)
    {
        if (!items.first())
        {
            return;
        }
        return Library_ItemCopied((items.first())->GetItem());
    }

    CClipboard clipboard(this);
    XmlNodeRef node = GetIEditor()->GetSystem()->CreateXmlNode("Childs");
    while (items.count() > 0)
    {
        CBaseLibraryItem* item = items.takeFirst()->GetItem();
        if (!item)
        {
            continue;
        }
        XmlNodeRef _node = node->newChild("Particles");
        CBaseLibraryItem::SerializeContext ctx(_node, false);
        ctx.bCopyPaste = true;
        item->Serialize(ctx);
    }
    clipboard.Put(node);
}

void CMainWindow::RegisterActions()
{
    CRY_ASSERT(GetIEditor());
    CRY_ASSERT(GetIEditor()->GetParticleUtils());
    IEditorParticleUtils* utils = GetIEditor()->GetParticleUtils();

    //////////////////////////////////////////////////////////////////////////
    //General Purpose
    //Undo
    QAction* action = new QAction(this);
    action->setShortcut(utils->HotKey_GetShortcut("Edit Menu.Undo"));
    connect(action, &QAction::triggered, this, [=]() { OnActionStandardUndo(); });
    addAction(action);
    //Redo
    action = new QAction(this);
    action->setShortcut(utils->HotKey_GetShortcut("Edit Menu.Redo"));
    connect(action, &QAction::triggered, this, [=]() { OnActionStandardRedo(); });
    addAction(action);
    //DUPLICATE///////////////////////////////////////////////////////////////////
    action = new QAction(this);
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Duplicate"));
    connect(action, &QAction::triggered, this, [this]()
        {
            Library_ItemDuplicated(""); // Default duplicate. Duplicate selected item.
        });
    addAction(action);

    //CLOSE///////////////////////////////////////////////////////////////////
    action = new QAction(this);
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("File Menu.Close"));
    connect(action, &QAction::triggered, this, [this]()
        {
            m_RequestedClose = true;
        });
    addAction(action);

    //HOTKEY REMAPPING////////////////////////////////////////////////////////
    action = new QAction(this);
    action->setShortcut(GetIEditor()->GetParticleUtils()->HotKey_GetShortcut("Edit Menu.Edit Hotkeys"));
    connect(action, &QAction::triggered, this, [&]()
        {
            QKeySequenceEditorDialog* dlg = UIFactory::GetHotkeyEditor();
            if (dlg->exec() == QDialog::Accepted)
            {
                GetIEditor()->GetParticleUtils()->HotKey_SaveCurrent();
                UnregisterActions();
                CreateMenuBar();
                CreateShortcuts();
                RegisterActions();
                m_libraryTreeViewDock->RemapHotKeys();
            }
        });
    addAction(action);
}

void CMainWindow::UnregisterActions()
{
    for (QAction* action : actions())
    {
        action->setParent(nullptr);
        removeAction(action);
        SAFE_DELETE(action);
    }
}

void CMainWindow::RefreshItemUI()
{
    if (m_LoDDock)
    {
       m_LoDDock->RefreshGUI();
    }
}

void CMainWindow::UpdateItemUI(const AZStd::string& itemId, bool selected, int lodIdx)
{
    EditorUIPlugin::ScopedSuspendUndoPtr suspendUndo;
    EBUS_EVENT_RESULT(suspendUndo, EditorLibraryUndoRequestsBus, AddScopedSuspendUndo);

    CParticleItem* item = static_cast<CParticleItem*>(GetIEditor()->GetParticleManager()->FindItemByName(itemId.c_str()));
    if (item)
    {
        if (selected)
        {
            //select the lib and item
            QStringList nameList = QString(itemId.c_str()).split(".");
            QString libname = nameList.takeFirst();
            QString name = nameList.join(".");
            m_libraryTreeViewDock->SelectLibraryAndItemByName(libname, name);

            //set lod
            SLodInfo* lod = item->GetEffect()->GetLevelOfDetail(lodIdx);
            if (lod)
            {
                m_LoDDock->SelectLod(lod);
            }
        }

        //update enable/disable and lod icon
        CLibraryTreeViewItem* titem = m_libraryTreeViewDock->GetTreeItemFromPath(itemId.c_str());
        if (titem)
        {
            bool enable = item->GetEffect()->GetParticleParams().bEnabled;
            titem->setCheckState(LIBRARY_TREEVIEW_INDICATOR_COLUMN, enable? Qt::CheckState::Checked:Qt::CheckState::Unchecked);
            m_libraryTreeViewDock->UpdateIconStyle(item->GetLibrary()->GetName(), itemId.c_str());
            m_LoDDock->RefreshGUI();
        }
    }
}

void CMainWindow::LibraryChangedInManager(const char* libraryName)
{
    if (GetIEditor()->GetParticleManager()->FindLibrary(QString::fromUtf8(libraryName)))
    {
        m_needLibraryRefresh = true;
        RefreshLibraries();
        
        CParticleItem* item = static_cast<CParticleItem*>(GetIEditor()->GetParticleManager()->GetSelectedItem());
        if (item)
        {
            AZStd::string itemFullName = item->GetFullName().toUtf8().data();
            UpdateItemUI(itemFullName, true, 0);
        }
    }
}

#include <QT/MainWindow.moc>