//  ************************************************************************************************
//
//  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/Application/ApplicationSettings.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sample/ItemWithParticles.h"
#include "GUI/Model/Sample/LayerItem.h"
#include "GUI/Model/Sample/ParticleLayoutItem.h"
#include "GUI/Model/Sample/SampleItem.h"
#include "GUI/View/SampleDesigner/LayerOrientedSampleEditor.h"
#include "GUI/View/SampleDesigner/SampleListView.h"
#include "GUI/View/SampleDesigner/ScriptPanel.h"
#include "GUI/View/SampleView/RealspacePanel.h"
#include "GUI/View/SampleView/RealspaceWidget.h"
#include "GUI/View/Widget/DocksController.h"
#include "GUI/View/Widget/StyledToolbar.h"

#include <QBoxLayout>
#include <QCheckBox>
#include <QDockWidget>
#include <QMenu>
#include <QMessageBox>
#include <QToolButton>
#include <QWidgetAction>

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

    connect(m_document, &ProjectDocument::singleSampleModeChanged, this,
            &SampleView::updateSingleSampleMode);

    m_docks = new DocksController(this);

    auto* editor = new LayerOrientedSampleEditor(this, document);

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

    auto* sampleSelectionToolbar = new StyledToolbar(sampleSelectionPane);
    m_sampleSelectionView = new SampleListView(this, m_document);
    sampleSelectionToolbar->addAction(m_sampleSelectionView->newSampleAction());
#ifdef BORNAGAIN_PYTHON
    sampleSelectionToolbar->addAction(m_sampleSelectionView->importSampleAction());
#endif
    sampleSelectionToolbar->addAction(m_sampleSelectionView->chooseFromLibraryAction());
    if (auto* btn = dynamic_cast<QToolButton*>(sampleSelectionToolbar->widgetForAction(
            m_sampleSelectionView->chooseFromLibraryAction())))
        btn->setPopupMode(QToolButton::InstantPopup);

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

    auto* scriptPanel = new ScriptPanel(this);
    m_realspacePanel = new RealspacePanel(this);

    sampleSelectionPane->setWindowTitle("Samples");

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

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

    connect(m_sampleSelectionView, &SampleListView::currentSampleChanged,
            [&](SampleItem* m) { onRequestViewInRealspace(m); });

    connect(m_sampleSelectionView, &SampleListView::currentSampleChanged, scriptPanel,
            &ScriptPanel::setCurrentSample);

    connect(editor, &LayerOrientedSampleEditor::modified, scriptPanel,
            &ScriptPanel::onSampleModified);

    connect(editor, &LayerOrientedSampleEditor::modified, m_sampleSelectionView,
            &SampleListView::onSampleModified);

    connect(editor, &LayerOrientedSampleEditor::requestViewInRealspace, this,
            &SampleView::onRequestViewInRealspace);

    connect(editor, &LayerOrientedSampleEditor::aboutToRemoveItem, this,
            &SampleView::onAboutToRemoveItem);

    connect(editor, &LayerOrientedSampleEditor::requestCreateNewSample,
            m_sampleSelectionView->newSampleAction(), &QAction::trigger, Qt::QueuedConnection);

    connect(editor, &LayerOrientedSampleEditor::modified, m_realspacePanel->widget(),
            &RealspaceWidget::updateScene);

    connect(editor, &LayerOrientedSampleEditor::modified, m_document, &ProjectDocument::setModified,
            Qt::UniqueConnection);

    setCentralWidget(editor);
    resetLayout();
}

void SampleView::updateSingleSampleMode()
{
    m_docks->setDockVisible(SAMPLE_LIST, !m_document->singleSampleMode());
}

void SampleView::onRequestViewInRealspace(Item3D* item)
{
    if (!item) {
        m_realspacePanel->widget()->clearDisplay();
        return;
    }

    m_docks->setDockVisible(REALSPACEPANEL);
    m_realspacePanel->widget()->setDisplayedItem(m_sampleSelectionView->currentSampleItem(), item);
}

void SampleView::onAboutToRemoveItem(Item3D* item)
{
    // If an item shall be deleted from the sample, check whether Realspace is affected.
    // Its current item could be item itself, or it could be a child of item.

    // -- for convenience access later on
    auto* widget = m_realspacePanel->widget();
    Item3D* displayed = widget->displayedItem();

    if (dynamic_cast<SampleItem*>(item)) {
        // It is a sample. In this case: always reset
        widget->resetScene();

    } else if (auto* i = dynamic_cast<LayerItem*>(item)) {
        if (item == widget->displayedItem() || i->layoutItems().contains(displayed)
            || i->itemsWithParticles().contains(displayed))
            widget->resetScene();

    } else if (auto* i = dynamic_cast<ParticleLayoutItem*>(item)) {
        if (item == widget->displayedItem() || i->containedItemsWithParticles().contains(displayed))
            widget->resetScene();

    } else if (auto* i = dynamic_cast<ItemWithParticles*>(item)) {
        if (item == displayed || i->containedItemsWithParticles().contains(displayed))
            widget->resetScene();
    }
}

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

void SampleView::fillViewMenu(QMenu* menu)
{
    m_docks->addDockActionsToMenu(menu);

    menu->addSeparator();

    auto* actionSampleMode = new QWidgetAction(menu);
    actionSampleMode->setText("Use single sample mode");
    auto* sampleModeWidget = new QCheckBox("Use single sample mode", menu);
    sampleModeWidget->setChecked(m_document->singleSampleMode());
    connect(sampleModeWidget, &QCheckBox::toggled, this, &SampleView::onSingleSampleModeChanged);
    actionSampleMode->setCheckable(true);
    actionSampleMode->setDefaultWidget(sampleModeWidget);
    menu->addAction(actionSampleMode);

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

void SampleView::showEvent(QShowEvent*)
{
    if (!m_sampleSelectionView->currentSampleItem())
        m_sampleSelectionView->restoreSelection();
}

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

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

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

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

void SampleView::onSingleSampleModeChanged(bool newState)
{
    if (newState) {
        if (m_document->sampleModel()->sampleItems().size() > 1) {
            QMessageBox::warning(this, "Select single sample mode",
                                 "This project already contains more than one sample. Changing "
                                 "this setting is not possible.");
            return;
        }

        m_document->setSingleSampleMode(true);
    } else
        m_document->setSingleSampleMode(false);
    if (gProjectDocument.has_value()) {
        appSettings->setDefaultIsSingleSampleMode(gProjectDocument.value()->singleSampleMode());
    }
}
