/*
* 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.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.

#include "StdAfx.h"
#include "PanelDisplayLayer.h"

#include <ISourceControl.h>
#include "ObjectLayerPropsDialog.h"
#include "Objects/ObjectLayerManager.h"
#include "Controls/LayersListBox.h"
#include "QtViewPaneManager.h"
#include <ui_PanelDisplayLayer.h>
#include "CryEditDoc.h"
#include <QToolButton>
#include <QtUtil.h>
#include <QtUtilWin.h>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QMenu>
#include <QKeyEvent>


#define ITEM_COLOR_NORMAL QColor(0, 0, 0)
#define ITEM_COLOR_SELECTED QColor(255, 0, 0)
#define ITEM_COLOR_FROZEN QColor(196, 196, 196)


enum
{
    ID_LAYERS_SETTINGS = 0,
    ID_LAYERS_DELETE,
    ID_LAYERS_EXPORT,
    ID_LAYERS_RELOAD,
    ID_LAYERS_SEPARATOR,
    ID_LAYERS_ADD,
    ID_LAYERS_IMPORT,
    ID_LAYERS_SEPARATOR2,
    ID_LAYERS_LOADVISPRESET,
    ID_LAYERS_SAVEVISPRESET,
    ID_LAYERS_SEPARATOR3,
    ID_LAYERS_EXPAND,
    ID_LAYERS_COLLAPSE,
};

//////////////////////////////////////////////////////////////////////////
// CPanelDisplayLayer dialog
//
//////////////////////////////////////////////////////////////////////////


#include <QtUtilWin.h>
//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::RegisterViewClass()
{
    AzToolsFramework::ViewPaneOptions options;
    options.canHaveMultipleInstances = true;
    options.sendViewPaneNameBackToAmazonAnalyticsServers = true;
    options.isLegacy = true;
    AzToolsFramework::RegisterViewPane<CPanelDisplayLayer>(LyViewPane::LegacyLayerEditor, LyViewPane::CategoryTools, options);
    GetIEditor()->GetSettingsManager()->AddToolVersion(LyViewPane::LegacyLayerEditor, LAYER_EDITOR_VER);
}

const GUID& CPanelDisplayLayer::GetClassID()
{
    // {A55E3527-FB72-4af4-B278-12002374DAFB}
    static const GUID guid = {
        0xa55e3527, 0xfb72, 0x4af4, { 0xb2, 0x78, 0x12, 0x0, 0x23, 0x74, 0xda, 0xfb }
    };
    return guid;
}


CPanelDisplayLayer::CPanelDisplayLayer(QWidget* pParent /*=NULL*/)
    : QWidget(pParent)
    , m_bLayersValid(false)
    , m_nPrevHeight(0)
    , ui(new Ui::PanelDisplayLayer)
{
    ui->setupUi(this);

    connect(ui->visibleStateButton, &QToolButton::clicked, this, &CPanelDisplayLayer::OnVisibleButtonClicked);
    connect(ui->usableStateButton, &QToolButton::clicked, this, &CPanelDisplayLayer::OnUsableButtonClicked);

    connect(ui->newButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedNew);
    connect(ui->renameButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedRename);
    connect(ui->deleteButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedDelete);
    connect(ui->exportButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedExport);
    connect(ui->importButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedImport);
    connect(ui->saveButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedSaveExternalLayers);
    connect(ui->freezeButton, &QAbstractButton::clicked, this, &CPanelDisplayLayer::OnBnClickedFreezeROnly);

    connect(ui->m_layersCtrl->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CPanelDisplayLayer::OnSelChanged);
    connect(ui->m_layersCtrl, &CLayersListBox::contextMenuRequested, this, &CPanelDisplayLayer::OnShowFullMenu);
    m_pLayerManager = GetIEditor()->GetObjectManager()->GetLayersManager();

    // Register callback.
    m_pLayerManager->AddUpdateListener(functor(*this, &CPanelDisplayLayer::OnLayerUpdate));

    ReloadLayers();
}

CPanelDisplayLayer::~CPanelDisplayLayer()
{
    m_pLayerManager->RemoveUpdateListener(functor(*this, &CPanelDisplayLayer::OnLayerUpdate));
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnLayerUpdate(int event, CObjectLayer* pLayer)
{
    switch (event)
    {
    case CObjectLayerManager::ON_LAYER_SELECT:
        SelectLayer(pLayer);
        break;
    case CObjectLayerManager::ON_LAYER_MODIFY:
        UpdateLayerItem(pLayer);
        break;
    default:
        m_bLayersValid = false;
        ReloadLayers();
    }

    UpdateStates();
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::UpdateLayerItem(CObjectLayer* pLayer)
{
    ui->m_layersCtrl->ReloadLayers();
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::ReloadLayers()
{
    ui->m_layersCtrl->ReloadLayers();
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::SelectLayer(CObjectLayer* pLayer)
{
    ui->m_layersCtrl->SelectLayer(pLayer->GetName());
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnSelChanged()
{
    CObjectLayer* pLayer = ui->m_layersCtrl->GetCurrentLayer();
    if (pLayer)
    {
        CUndo undo("Set Current Layer");
        m_pLayerManager->SetCurrentLayer(pLayer);
    }
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedNew()
{
    if (!GetIEditor()->GetDocument()->IsDocumentReady())
    {
        return;
    }

    // Create a new layer.
    CObjectLayerPropsDialog dlg;
    if (dlg.exec() == QDialog::Accepted)
    {
        if (dlg.m_name.isEmpty())
        {
            return;
        }
        // Check if layer with such name already exists.
        if (m_pLayerManager->FindLayerByName(dlg.m_name))
        {
            QMessageBox::warning(this, tr("Layer Exists!"), tr("Layer %1 already exist, choose different layer name").arg(dlg.m_name));
            return;
        }
        CUndo undo("New Layer");
        CObjectLayer* pLayer = m_pLayerManager->CreateLayer();
        pLayer->SetName(dlg.m_name);
        pLayer->SetExternal(dlg.m_bExternal);
        pLayer->SetExportable(dlg.m_bExportToGame);
        pLayer->SetExportLayerPak(dlg.m_bExportLayerPak);
        pLayer->SetDefaultLoaded(dlg.m_bDefaultLoaded);
        pLayer->SetFrozen(dlg.m_bFrozen);
        pLayer->SetVisible(dlg.m_bVisible);
        pLayer->SetColor(dlg.m_color);
        pLayer->SetHavePhysics(dlg.m_bHavePhysics);
        pLayer->SetSpecs(dlg.m_specs);
        pLayer->SetModified();
        ReloadLayers();
    }
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnLoadVisPreset()
{
    QString filename = GetIEditor()->GetLevelFolder();
    if (!CFileUtil::SelectFile("Layer Visible Presets (*.lvp)", filename, filename))
    {
        return;
    }

    XmlNodeRef root = XmlHelpers::LoadXmlFromFile(filename.toStdString().c_str());
    if (!root)
    {
        return;
    }

    for (int i = 0; i < root->getChildCount(); ++i)
    {
        XmlNodeRef layer = root->getChild(i);
        if (_stricmp(layer->getTag(), "Layer") != 0)
        {
            continue;
        }

        GUID guid;
        CObjectLayer* pLayer;
        if (layer->getAttr("GUID", guid) && (pLayer = m_pLayerManager->FindLayer(guid)))
        {
            bool isVisible;
            if (layer->getAttr("Visible", isVisible))
            {
                pLayer->SetVisible(isVisible);
            }
            bool isFrozen;
            if (layer->getAttr("Frozen", isFrozen))
            {
                pLayer->SetFrozen(isFrozen);
            }
        }
    }
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnSaveVisPreset()
{
    QString filename = QStringLiteral("visibilty.lvp");
    if (!CFileUtil::SelectSaveFile("Layer Visible Presets (*.lvp)", "lvp", GetIEditor()->GetLevelFolder(), filename))
    {
        return;
    }

    QWaitCursor wait;

    std::vector<CObjectLayer*> layers;
    GetIEditor()->GetObjectManager()->GetLayersManager()->GetLayers(layers);

    XmlNodeRef root = XmlHelpers::CreateXmlNode("Visibility");

    for (int i = 0; i < layers.size(); ++i)
    {
        const CObjectLayer* pLayer = layers[i];
        XmlNodeRef layer = root->newChild("Layer");
        layer->setAttr("Name", pLayer->GetName().toUtf8().data());
        layer->setAttr("GUID", pLayer->GetGUID());
        layer->setAttr("Visible", pLayer->IsVisible());
        layer->setAttr("Frozen", pLayer->IsFrozen());
    }

    XmlHelpers::SaveXmlNode(GetIEditor()->GetFileUtil(), root, filename.toStdString().c_str());
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedDelete()
{
    CObjectLayer* pLayer = m_pLayerManager->GetCurrentLayer();
    if (!pLayer)
    {
        return;
    }

    const int numObjects = pLayer->GetObjectCount();
    QString str;
    str = tr("Delete layer %1 with %2 object%3").arg(pLayer->GetName()).arg(numObjects).arg(numObjects == 1 ? "" : "s");
    if (pLayer->GetChildCount())
    {
        int numChildLayersObjects = 0;
        for (int i = 0; i < pLayer->GetChildCount(); i++)
        {
            numChildLayersObjects += pLayer->GetChild(i)->GetObjectCount();
        }
        QString strChildren;
        strChildren = tr("\r\nand all children layers with %1 object%2").arg(numChildLayersObjects).arg(numChildLayersObjects == 1 ? "" : "s");
        str += strChildren;
    }
    str += "?";

    if (QMessageBox::question(this, "Delete Layer", str, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
    {
        // Delete selected layer.
        CUndo undo("Delete Layer");
        m_pLayerManager->DeleteLayer(pLayer);
        ReloadLayers();
    }
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedExport()
{
    // Export selected layer.
    CObjectLayer* pLayer = m_pLayerManager->GetCurrentLayer();
    if (!pLayer)
    {
        return;
    }

    QString directory = QString("%1/Layers/").arg(GetIEditor()->GetLevelFolder());
    QString filename = QString("%1%2").arg(Path::CaselessPaths(pLayer->GetName()), LAYER_FILE_EXTENSION);
    if (pLayer->IsExternal())
    {
        filename = pLayer->GetExternalLayerPath();
    }

    if (CFileUtil::SelectSaveFile("Object Layer (*.lyr)", "lyr", directory, filename))
    {
        QApplication::setOverrideCursor(Qt::WaitCursor);

        GetIEditor()->Notify(eNotify_OnBeginLayerExport);

        XmlNodeRef root = XmlHelpers::CreateXmlNode("ObjectLayer");

        XmlNodeRef layerDesc = root->newChild("Layer");
        CObjectArchive ar(GetIEditor()->GetObjectManager(), layerDesc, FALSE);
        GetIEditor()->GetObjectManager()->GetLayersManager()->ExportLayer(ar, pLayer, true);
        XmlHelpers::SaveXmlNode(GetIEditor()->GetFileUtil(), root, filename.toStdString().c_str());

        GetIEditor()->Notify(eNotify_OnEndLayerExport);
        QApplication::restoreOverrideCursor();
    }
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedReload()
{
    CObjectLayer* pLayer = m_pLayerManager->GetCurrentLayer();
    if (!pLayer)
    {
        return;
    }

    if (pLayer->IsModified())
    {
        QString msg = tr("Layer %1 has been modified. All changes in this layer and related objects will be lost.\r\nReload anyway?").arg(pLayer->GetName());
        if (QMessageBox::question(this, "Reload Layer", "Confirm Reload Layer", QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
        {
            return;
        }
    }

    QApplication::setOverrideCursor(Qt::WaitCursor);
    GetIEditor()->GetObjectManager()->GetLayersManager()->ReloadLayer(pLayer);
    QApplication::restoreOverrideCursor();
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedImport()
{
    if (!GetIEditor()->GetDocument()->IsDocumentReady())
    {
        return;
    }

    QStringList files;
    if (CFileUtil::SelectFiles("Object Layer (*.lyr);;All Files (*)", QString("%1/Layers/").arg(GetIEditor()->GetLevelFolder()), files))
    {
        QApplication::setOverrideCursor(Qt::WaitCursor);

        // Start recording errors.
        CErrorsRecorder errorsRecorder(GetIEditor());

        CUndo undo("Import Layer(s)");

        GetIEditor()->Notify(eNotify_OnLayerImportBegin);

        for (QStringList::iterator pFile = files.begin(); pFile != files.end(); ++pFile)
        {
            GetIEditor()->GetObjectManager()->GetLayersManager()->ImportLayerFromFile(pFile->toStdString().c_str());
        }

        GetIEditor()->Notify(eNotify_OnLayerImportEnd);
        QApplication::restoreOverrideCursor();
    }
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedSaveExternalLayers()
{
    m_pLayerManager->SaveExternalLayers();
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedFreezeROnly()
{
    GetIEditor()->GetObjectManager()->GetLayersManager()->FreezeROnly();
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnBnClickedRename()
{
    // Export selected layer.
    CObjectLayer* pLayer = m_pLayerManager->GetCurrentLayer();
    if (!pLayer)
    {
        return;
    }

    CObjectLayerPropsDialog dlg;
    dlg.m_name = pLayer->GetName();
    dlg.m_bExportToGame = pLayer->IsExportable();
    dlg.m_bExportLayerPak = pLayer->IsExporLayerPak();
    dlg.m_bDefaultLoaded = pLayer->IsDefaultLoaded();
    dlg.m_bExternal = pLayer->IsExternal();
    dlg.m_bFrozen = pLayer->IsFrozen();
    dlg.m_bVisible = pLayer->IsVisible();
    dlg.m_bHavePhysics = pLayer->IsPhysics();
    dlg.m_specs = pLayer->GetSpecs();
    dlg.m_bMainLayer = false;
    dlg.m_color = pLayer->GetColor();
    if (dlg.exec() == QDialog::Accepted)
    {
        if (dlg.m_name.isEmpty())
        {
            return;
        }

        CUndo undo("Rename Layer");

        CObjectLayer* pOtherLayer = m_pLayerManager->FindLayerByName(dlg.m_name);
        // Check if layer with such name already exists.
        if (pOtherLayer && pOtherLayer != pLayer)
        {
            QMessageBox::warning(this, tr("Layer Exists"), tr("Layer %1 already exist, choose different layer name").arg(dlg.m_name));
            return;
        }

        pLayer->SetName(dlg.m_name, true);
        pLayer->SetExternal(dlg.m_bExternal);
        pLayer->SetExportable(dlg.m_bExportToGame);
        pLayer->SetExportLayerPak(dlg.m_bExportLayerPak);
        pLayer->SetDefaultLoaded(dlg.m_bDefaultLoaded);
        pLayer->SetFrozen(dlg.m_bFrozen);
        pLayer->SetVisible(dlg.m_bVisible);
        pLayer->SetColor(dlg.m_color);
        pLayer->SetHavePhysics(dlg.m_bHavePhysics);
        pLayer->SetSpecs(dlg.m_specs);
        pLayer->SetModified();

        GetIEditor()->GetObjectManager()->GetLayersManager()->NotifyLayerChange(pLayer);
        GetIEditor()->Notify(eNotify_OnInvalidateControls);
        ReloadLayers();
    }
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnShowFullMenu()
{
    // Assume that layer was already selected.
    QStringList eMItems;
    eMItems.append(tr("Settings"));
    eMItems.append(tr("Delete"));
    eMItems.append(tr("Export"));
    eMItems.append(tr("Reload"));
    eMItems.append("");

    ShowContextMenu(eMItems);
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::ShowContextMenu(const QStringList& extraActions)
{
    if (!GetIEditor()->GetDocument()->IsDocumentReady())
    {
        return;
    }

    CObjectLayer* pLayer = 0;

    int firstMenuID = ID_LAYERS_ADD;
    QStringList menuItems = extraActions;
    if (!extraActions.isEmpty())
    {
        firstMenuID = ID_LAYERS_SETTINGS;
    }

    if (m_pLayerManager->GetCurrentLayer() && m_pLayerManager->GetCurrentLayer()->IsExternal())
    {
        pLayer = m_pLayerManager->GetCurrentLayer();
    }

    menuItems.append(tr("Add New Layer"));
    menuItems.append(tr("Import Layers"));

    menuItems.append("");

    menuItems.append(tr("Load Visibility Preset"));
    menuItems.append(tr("Save Visibility Preset"));

    menuItems.append("");

    menuItems.append(tr("Expand All Layers"));
    menuItems.append(tr("Collapse All Layers"));

    int cmd = -1;
    if (pLayer)
    {
        QString file = pLayer->GetExternalLayerPath();
        uint32 attr = CFileUtil::GetAttributes(file.toUtf8().data());
        const QString selectedItem = CFileUtil::PopupQMenu(Path::GetFile(file), Path::GetPath(file), this, 0, menuItems);

        //find index of menu item that was selected.
        //If not found, it was something from menu in CFileUtil::PopupQMenu and was handled there.
        cmd = firstMenuID + menuItems.indexOf(selectedItem);

        uint32 newAttr = CFileUtil::GetAttributes(file.toUtf8().data());
        if ((attr & SCC_FILE_ATTRIBUTE_READONLY) && newAttr != SCC_FILE_ATTRIBUTE_INVALID && !(newAttr & SCC_FILE_ATTRIBUTE_READONLY))
        {
            pLayer->SetFrozen(false);
        }
    }
    else
    {
        QMenu menu;
        for (int i = 0; i < menuItems.size(); i++)
        {
            const QString item = menuItems.at(i);
            if (!item.isEmpty())
            {
                QAction* action = menu.addAction(item);
                action->setData(i);
            }
            else
            {
                menu.addSeparator();
            }
        }
        QAction* action = menu.exec(QCursor::pos());
        cmd = action ? action->data().toInt() : -1;
    }

    if (cmd != -1)
    {
        switch (cmd)
        {
        case ID_LAYERS_ADD:
            OnBnClickedNew();
            break;
        case ID_LAYERS_LOADVISPRESET:
            OnLoadVisPreset();
            break;
        case ID_LAYERS_SAVEVISPRESET:
            OnSaveVisPreset();
            break;
        case ID_LAYERS_EXPAND:
            ui->m_layersCtrl->ExpandAll(true);
            break;
        case ID_LAYERS_COLLAPSE:
            ui->m_layersCtrl->ExpandAll(false);
            break;
        case ID_LAYERS_SETTINGS:
            OnBnClickedRename();
            break;
        case ID_LAYERS_EXPORT:
            OnBnClickedExport();
            break;
        case ID_LAYERS_RELOAD:
            OnBnClickedReload();
            break;
        case ID_LAYERS_IMPORT:
            OnBnClickedImport();
            break;
        case ID_LAYERS_DELETE:
            OnBnClickedDelete();
            break;
        }
    }
}


//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::OnVisibleButtonClicked(bool bForceVisibleState)
{
    std::vector<CObjectLayer*> layers;
    m_pLayerManager->GetLayers(layers);
    for (int i = 0; i < layers.size(); i++)
    {
        layers[i]->SetVisible(bForceVisibleState);
    }
}

void CPanelDisplayLayer::OnUsableButtonClicked(bool bForceUsableState)
{
    std::vector<CObjectLayer*> layers;
    m_pLayerManager->GetLayers(layers);
    for (int i = 0; i < layers.size(); i++)
    {
        layers[i]->SetFrozen(!bForceUsableState);
    }
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::contextMenuEvent(QContextMenuEvent* event)
{
    if (!ui->m_layersCtrl->geometry().contains(event->pos()))
    {
        ShowContextMenu();
    }
}

//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::keyPressEvent(QKeyEvent* e)
{
    switch (e->key())
    {
    case Qt::Key_F2:
        OnBnClickedRename();
        break;
    }
    ;

    return QWidget::keyPressEvent(e);
}
//////////////////////////////////////////////////////////////////////////
void CPanelDisplayLayer::UpdateStates()
{
    if (ui->m_layersCtrl->GetCount() < 1)
    {
        return;
    }

    bool bVisibleState = true;
    bool bUsableState = true;

    std::vector<CObjectLayer*> layers;
    m_pLayerManager->GetLayers(layers);

    for (int i = 0; i < layers.size(); i++)
    {
        if (!layers[i]->IsVisible())
        {
            bVisibleState = false;
        }

        if (layers[i]->IsFrozen())
        {
            bUsableState = false;
        }
    }

    ui->visibleStateButton->setChecked(bVisibleState);
    ui->usableStateButton->setChecked(bUsableState);
}