//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/View/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/View/SampleView.h"
#include "Base/Util/Vec.h"
#include "GUI/Model/FromCore/GUIExamplesFactory.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/SamplesSet.h"
#include "GUI/View/Base/mainwindow_constants.h"
#include "GUI/View/Loader/PyImportAssistant.h"
#include "GUI/View/Modelview/SetView.h"
#include "GUI/View/Realspace/RealspacePanel.h"
#include "GUI/View/Realspace/RealspaceWidget.h"
#include "GUI/View/Sample/SampleEditor.h"
#include "GUI/View/Sample/ScriptPanel.h"
#include "GUI/View/Setup/ActionFactory.h"
#include "GUI/View/Widget/StyledToolbar.h"
#include <QBoxLayout>
#include <QMenu>
#include <QSettings>
#include <QSplitter>
#include <QToolButton>

SampleView::SampleView()
    : m_set(gDoc->samplesRW())
    , m_qlistview(new SetView(m_set))
    , m_realspace_panel(new RealspacePanel(this))
{
    auto* layout = new QVBoxLayout(this);

    //... Top toolbar with action buttons "new", "import", "example"
    auto* toolbar = new StyledToolbar;
    layout->addWidget(toolbar);
    toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    setToolbarActions(toolbar);

    //... Everything below top toolbar
    auto* hLayout = new QHBoxLayout;
    layout->addLayout(hLayout);

    //... Left margin: sample listing

    hLayout->addWidget(m_qlistview);

    //... Large block on the right: sample editor and bottom panels

    auto* vSplitter = new QSplitter;
    vSplitter->setOrientation(Qt::Vertical);
    hLayout->addWidget(vSplitter);

    hLayout->addStretch(1);

    auto* editor = new SampleEditor;
    vSplitter->addWidget(editor);

    //... Below central widget: realspace and script panels

    auto* hSplitter = new QSplitter;
    hSplitter->setOrientation(Qt::Horizontal);
    vSplitter->addWidget(hSplitter);

    hSplitter->addWidget(m_realspace_panel);

    auto* scriptPanel = new ScriptPanel(this);
    hSplitter->addWidget(scriptPanel);

    applySplitterPos();

    // unfix RealspacePanel size only on manual resize
    connect(hSplitter, &QSplitter::splitterMoved, [this] {
        m_realspace_panel->setMinimumWidth(1); // somehow with min width >0 it behaves better
        m_realspace_panel->setMaximumWidth(QWIDGETSIZE_MAX);
    });
    connect(vSplitter, &QSplitter::splitterMoved, [this] {
        m_realspace_panel->setMinimumHeight(0);
        m_realspace_panel->setMaximumHeight(QWIDGETSIZE_MAX);
    });

    //... Finish

    connect(gDoc->samples(), &SamplesSet::setChanged, [this, editor, scriptPanel] {
        SampleItem* t = gDoc->samplesRW()->currentItem();
        editor->setCurrentSample(t);
        onRequestViewInRealspace(t);
        scriptPanel->setCurrentSample(t);
        updateActions();
    });

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

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

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

    connect(editor, &SampleEditor::modified, m_realspace_panel->widget(),
            &RealspaceWidget::updateRealScene);

    connect(editor, &SampleEditor::modified, gDoc.get(), &ProjectDocument::setModified);

    updateActions();
}

SampleView::~SampleView()
{
    saveSplitterPos();
}

void SampleView::setToolbarActions(QToolBar* toolbar)
{
    //... New sample action

    auto* new_sample_action = new QAction("New", this);
    new_sample_action->setIcon(QIcon(":/images/shape-square-plus.svg"));
    new_sample_action->setToolTip("Create new sample");
    toolbar->addAction(new_sample_action);

    connect(new_sample_action, &QAction::triggered, [this] {
        m_set->add_item(new SampleItem);
        // TODO call new_item->addStandardMaterials() ??
    });

    //... Load and save actions

#ifdef BORNAGAIN_PYTHON
    auto* import_sample_action = new QAction("Import Py", this);
    import_sample_action->setIcon(QIcon(":/images/import.svg"));
    import_sample_action->setToolTip(
        "Import sample from a Python script that contains a function that returns a multi-layer");
    toolbar->addAction(import_sample_action);
    connect(import_sample_action, &QAction::triggered,
            [this] { m_set->add_item(PyImportAssistant::importMultiLayer()); });
#endif

    auto* choose_from_library_action = new QAction("Examples", this);
    toolbar->addAction(choose_from_library_action);
    if (auto* btn =
            dynamic_cast<QToolButton*>(toolbar->widgetForAction(choose_from_library_action)))
        btn->setPopupMode(QToolButton::InstantPopup);
    choose_from_library_action->setIcon(QIcon(":/images/library.svg"));
    choose_from_library_action->setToolTip("Choose from sample examples");
    auto* import_menu = new QMenu(this);
    choose_from_library_action->setMenu(import_menu);
    for (const auto& exampleName : GUI::ExamplesFactory::exampleNames()) {
        QString title, description;
        std::tie(title, description) = GUI::ExamplesFactory::exampleInfo(exampleName);
        auto icon = QIcon(":/images/sample_layers2.png");
        auto* action = import_menu->addAction(icon, title);
        action->setToolTip(description);
        connect(action, &QAction::triggered, [this, exampleName, title, description] {
            SampleItem* t = GUI::ExamplesFactory::itemizeSample(exampleName);
            if (!t)
                return;
            t->setName(title);
            t->setDescription(description);
            m_set->add_item(t);
        });
    }

    //... Copy and remove actions

    m_cp_action = ActionFactory::createCopyAction("sample", this);
    toolbar->addAction(m_cp_action);
    connect(m_cp_action, &QAction::triggered,
            [this] { m_set->add_item(m_set->currentItem()->clone()); });

    m_rm_action = ActionFactory::createRemoveAction("sample", this);
    toolbar->addAction(m_rm_action);
    connect(m_rm_action, &QAction::triggered, m_set, &SamplesSet::delete_current);
}

void SampleView::updateActions()
{
    bool enabled = m_set->currentIndex() != size_t(-1);
    m_rm_action->setEnabled(enabled);
    m_cp_action->setEnabled(enabled);
}

void SampleView::applySplitterPos()
{
    QSettings settings;
    if (settings.childGroups().contains(GUI::Style::S_SAMPLE_3DVIEW_WIDGET)) {
        settings.beginGroup(GUI::Style::S_SAMPLE_3DVIEW_WIDGET);
        m_realspace_panel->setFixedSize(
            settings.value(GUI::Style::S_SAMPLE_3DVIEW_WIDGET_SIZE).toSize());
        settings.endGroup();
    } else
        m_realspace_panel->setFixedSize(GUI::Style::SAMPLE_3DVIEW_WIDGET_WIDTH,
                                        GUI::Style::SAMPLE_3DVIEW_WIDGET_HEIGHT);
}

void SampleView::saveSplitterPos()
{
    QSettings settings;
    settings.beginGroup(GUI::Style::S_SAMPLE_3DVIEW_WIDGET);
    settings.setValue(GUI::Style::S_SAMPLE_3DVIEW_WIDGET_SIZE, m_realspace_panel->size());
    settings.endGroup();
    settings.sync();
}

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

    m_realspace_panel->widget()->setDisplayedItem(gDoc->samplesRW()->currentItem(), 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_realspace_panel->widget();
    Item3D* displayed = widget->displayedItem();

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

    } else if (auto* i = dynamic_cast<LayerItem*>(item)) {
        if (item == widget->displayedItem() || Vec::containsPtr(displayed, i->layoutItems())
            || Vec::containsPtr(displayed, i->itemsWithParticles()))
            widget->resetRealScene();

    } else if (auto* i = dynamic_cast<ParticleLayoutItem*>(item)) {
        if (item == widget->displayedItem()
            || Vec::containsPtr(displayed, i->containedItemsWithParticles()))
            widget->resetRealScene();

    } else if (auto* i = dynamic_cast<ItemWithParticles*>(item)) {
        if (item == displayed || Vec::containsPtr(displayed, i->containedItemsWithParticles()))
            widget->resetRealScene();
    }
}
