//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/SampleDesigner/SampleView.cpp
//! @brief     Implements class SampleView
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/View/SampleDesigner/SampleView.h"
#include "GUI/Model/Group/FilterPropertyProxy.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sample/MultiLayerItem.h"
#include "GUI/Model/Sample/SampleModel.h"
#include "GUI/View/Common/DocksController.h"
#include "GUI/View/Realspace/RealSpaceCanvas.h"
#include "GUI/View/SampleDesigner/DesignerScene.h"
#include "GUI/View/SampleDesigner/DesignerView.h"
#include "GUI/View/SampleDesigner/LayerOrientedSampleEditor.h"
#include "GUI/View/SampleDesigner/RealSpacePanel.h"
#include "GUI/View/SampleDesigner/SampleListView.h"
#include "GUI/View/SampleDesigner/SamplePropertyWidget.h"
#include "GUI/View/SampleDesigner/SampleToolBar.h"
#include "GUI/View/SampleDesigner/SampleToolBox.h"
#include "GUI/View/SampleDesigner/SampleTreeWidget.h"
#include "GUI/View/SampleDesigner/ScriptPanel.h"

#include <QBoxLayout>
#include <QDockWidget>
#include <QMenu>
#include <QToolButton>

SampleView::SampleView(QWidget* parent, ProjectDocument* document, bool initAsLayerOrientedEditor)
    : QMainWindow(parent), m_document(document)
{
    setObjectName("SampleView");

    connect(m_document, &ProjectDocument::modified, this, &SampleView::updateFunctionalities);

    if (initAsLayerOrientedEditor)
        initLayerOrientedEditor();
    else
        initNetOrientedEditor();
}

void SampleView::initNetOrientedEditor()
{
    const bool wasVisible = isVisible();
    hide(); // important; otherwise the dockwidget layout is messed up
    deleteEditor();

    m_docks = new DocksController(this);

    auto* designerScene = new DesignerScene(this, m_document);
    auto* designerView = new DesignerView(designerScene, this);

    auto* toolBox = new SampleToolBox(this);
    auto* treeWidget = new SampleTreeWidget(this, m_document->sampleModel());
    auto* propertyWidget = new SamplePropertyWidget(treeWidget->treeView()->selectionModel(), this);
    auto* scriptPanel = new ScriptPanel(m_document->sampleModel(), this);
    m_realSpacePanel = new RealSpacePanel(m_document->sampleModel(), this);

    m_docks->addWidget(TOOLBOX, toolBox, Qt::LeftDockWidgetArea);
    m_docks->addWidget(SAMPLE_TREE, treeWidget, Qt::RightDockWidgetArea);
    m_docks->addWidget(PROPERTY_EDITOR, propertyWidget, Qt::RightDockWidgetArea);
    m_docks->addWidget(INFO, scriptPanel, Qt::BottomDockWidgetArea);
    m_docks->addWidget(REALSPACEPANEL, m_realSpacePanel, Qt::BottomDockWidgetArea);

    connect(scriptPanel, &ScriptPanel::widgetHeightRequest, m_docks,
            &DocksController::setDockHeightForWidget);

    designerScene->setSelectionModel(
        treeWidget->treeView()->selectionModel(),
        dynamic_cast<FilterPropertyProxy*>(
            const_cast<QAbstractItemModel*>(treeWidget->treeView()->model())));

    setCentralWidget(designerView);
    resetLayout();

    // m_toolBar should be initialized after MaterialBrowser
    m_toolBar = new SampleToolBar(this);
    connect(m_toolBar, &SampleToolBar::deleteItems, designerView,
            &DesignerView::deleteSelectedItems);
    connect(m_toolBar, &SampleToolBar::selectionMode, designerView, &DesignerView::onSelectionMode);
    connect(designerView, &DesignerView::selectionModeChanged, m_toolBar,
            &SampleToolBar::onViewSelectionMode);
    connect(m_toolBar, &SampleToolBar::centerView, designerView, &DesignerView::onCenterView);
    connect(m_toolBar, &SampleToolBar::changeScale, designerView, &DesignerView::onChangeScale);
    connect(designerScene, &DesignerScene::selectionModeChangeRequest, designerView,
            &DesignerView::onSelectionMode);
    connect(treeWidget->treeView()->selectionModel(), &QItemSelectionModel::selectionChanged, this,
            &SampleView::onSampleTreeViewSelectionChanged);

    addToolBar(m_toolBar);

    m_useLayerOrientedEditor = false;

    if (wasVisible)
        show();
}

void SampleView::initLayerOrientedEditor()
{
    const bool wasVisible = isVisible();
    hide(); // important; otherwise the dockwidget layout is messed up
    deleteEditor();

    m_docks = new DocksController(this);

    auto* editor = new LayerOrientedSampleEditor(this);

    auto* sampleSelectionPane = new QWidget(this);
    auto* sampleSelectionLayout = new QVBoxLayout(sampleSelectionPane);
    sampleSelectionLayout->setContentsMargins(0, 0, 0, 0);
    sampleSelectionLayout->setSpacing(0);

    auto* sampleSelectionToolbar = new StyledToolBar(sampleSelectionPane);
    auto* sampleSelectionView = new SampleListView(this, m_document->sampleModel());
    sampleSelectionToolbar->addAction(sampleSelectionView->newSampleAction());
    sampleSelectionToolbar->addAction(sampleSelectionView->chooseFromLibraryAction());
    if (auto* btn = dynamic_cast<QToolButton*>(sampleSelectionToolbar->widgetForAction(
            sampleSelectionView->chooseFromLibraryAction())))
        btn->setPopupMode(QToolButton::InstantPopup);

    sampleSelectionToolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    sampleSelectionLayout->addWidget(sampleSelectionToolbar);
    sampleSelectionLayout->addWidget(sampleSelectionView);
    sampleSelectionView->setSizePolicy(QSizePolicy::MinimumExpanding,
                                       QSizePolicy::MinimumExpanding);

    auto* scriptPanel = new ScriptPanel(m_document->sampleModel(), this);
    m_realSpacePanel = new RealSpacePanel(m_document->sampleModel(), this);
    m_realSpacePanel->setLockViewActionVisible(false); // lock not used in layer oriented editor

    sampleSelectionPane->setWindowTitle("Samples");

    m_docks->addWidget(SAMPLE_LIST, sampleSelectionPane, Qt::LeftDockWidgetArea);
    m_docks->addWidget(REALSPACEPANEL, m_realSpacePanel, Qt::BottomDockWidgetArea);
    m_docks->addWidget(INFO, scriptPanel, Qt::BottomDockWidgetArea);

    connect(scriptPanel, &ScriptPanel::widgetHeightRequest, m_docks,
            &DocksController::setDockHeightForWidget);

    connect(sampleSelectionView, &SampleListView::currentSampleChanged, this,
            &SampleView::onCurrentSampleChanged);

    connect(sampleSelectionView, &SampleListView::currentSampleChanged, editor,
            &LayerOrientedSampleEditor::setCurrentSample);

    connect(editor, &LayerOrientedSampleEditor::requestViewInRealSpace, this,
            &SampleView::onRequestViewInRealSpace);

    setCentralWidget(editor);
    resetLayout();

    m_useLayerOrientedEditor = true;
    if (wasVisible)
        show();
}

void SampleView::deleteEditor()
{
    delete m_toolBar;
    m_toolBar = nullptr;

    if (m_docks != nullptr) {
        auto dockWidgets = m_docks->dockWidgets();
        delete m_docks;
        m_docks = nullptr;
        qDeleteAll(dockWidgets);
    }

    if (auto* w = dynamic_cast<DesignerView*>(centralWidget()))
        delete w->scene();

    delete centralWidget();
}

void SampleView::updateFunctionalities()
{
    if (auto* d = m_docks->findDock(SAMPLE_LIST)) {
        if (m_document->singleSampleMode())
            d->hide();
        else
            d->show();
    }
}

void SampleView::onSampleTreeViewSelectionChanged(const QItemSelection& selected,
                                                  const QItemSelection&)
{
    if (m_realSpacePanel->isViewLocked())
        return;

    if (selected.indexes().empty())
        return;

    const QModelIndex currentIndex = FilterPropertyProxy::toSourceIndex(selected.indexes().back());
    auto* item = m_document->sampleModel()->itemForIndex(currentIndex);
    if (item)
        m_realSpacePanel->canvas()->setCurrentItem(item);
}

void SampleView::onCurrentSampleChanged(MultiLayerItem* current)
{
    if (m_layerOrientedSampleEditor)
        m_layerOrientedSampleEditor->setCurrentSample(current);
    m_realSpacePanel->canvas()->setCurrentItem(current);
}

void SampleView::onRequestViewInRealSpace(SessionItem* itemToView)
{
    if (!itemToView)
        return;

    m_docks->setDockVisible(REALSPACEPANEL);
    m_realSpacePanel->canvas()->setCurrentItem(itemToView);
}

void SampleView::toggleRealSpaceView()
{
    m_docks->toggleDock(REALSPACEPANEL);
}

void SampleView::fillViewMenu(QMenu* menu)
{
    auto* editorSelectionActions = new QActionGroup(this);

    auto* action = menu->addAction("Layer oriented sample editor");
    action->setCheckable(true);
    action->setChecked(m_useLayerOrientedEditor);
    connect(action, &QAction::triggered, this, &SampleView::initLayerOrientedEditor,
            Qt::QueuedConnection);
    editorSelectionActions->addAction(action);

    action = menu->addAction("Net oriented sample editor");
    action->setCheckable(true);
    action->setChecked(!m_useLayerOrientedEditor);
    connect(action, &QAction::triggered, this, &SampleView::initNetOrientedEditor,
            Qt::QueuedConnection);
    editorSelectionActions->addAction(action);

    menu->addSeparator();

    m_docks->addDockActionsToMenu(menu);

    menu->addSeparator();

    action = new QAction(menu);
    action->setText("Reset to default layout");
    connect(action, &QAction::triggered, this, &SampleView::resetLayout);
    menu->addAction(action);
}

void SampleView::resetLayout()
{
    m_docks->resetLayout();

    tabifyDockWidget(m_docks->findDock(REALSPACEPANEL), m_docks->findDock(INFO));
    m_docks->findDock(REALSPACEPANEL)->raise(); // makes first tab active

    m_docks->findDock(REALSPACEPANEL)->hide();
    m_docks->findDock(INFO)->hide();

    if (auto* d = m_docks->findDock(SAMPLE_LIST)) {
        if (m_document->singleSampleMode())
            d->hide();
        else
            d->show();
    }
}