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

#include "GUI/View/SampleDesigner/CoreAndShellForm.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Sample/CoreAndShellItem.h"
#include "GUI/Model/Sample/FormFactorItemCatalog.h"
#include "GUI/Model/Sample/FormFactorItems.h"
#include "GUI/Model/Sample/ParticleItem.h"
#include "GUI/Support/Util/ActionFactory.h"
#include "GUI/View/SampleDesigner/FormLayouter.h"
#include "GUI/View/SampleDesigner/MaterialInplaceForm.h"
#include "GUI/View/SampleDesigner/SampleEditorController.h"
#include "GUI/View/SampleDesigner/SelectionContainerForm.h"
#include "GUI/View/Tool/GroupBoxCollapser.h"
#include <QAction>
#include <QComboBox>
#include <memory>

namespace {

QComboBox* createFormFactorCombo(QWidget* parent, FormFactorItem* current)
{
    ASSERT(current);
    auto* combo = new QComboBox(parent);
    WheelEventEater::install(combo);
    combo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);

    for (const auto type : FormFactorItemCatalog::types()) {
        const auto ui = FormFactorItemCatalog::uiInfo(type);
        combo->addItem(QIcon(ui.iconPath), ui.menuEntry, (uint8_t)type);
    }
    combo->setMaxVisibleItems(combo->count());
    combo->setCurrentIndex(combo->findData((uint8_t)FormFactorItemCatalog::type(current)));

    return combo;
}

} // namespace


CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellItem,
                                   SampleEditorController* ec, bool allowRemove)
    : QGroupBox(parent)
    , m_item(coreShellItem)
    , m_ec(ec)
{
    setTitle("Core/shell particle");
    FormLayouter layouter(this, ec);
    layouter.setContentsMargins(30, 6, 0, 0);
    layouter.addVector(coreShellItem->position(), false);
    layouter.addSelection(coreShellItem->rotationSelection());
    layouter.addValue(coreShellItem->abundance());

    // - core
    {
        auto* coreParticleGroup = new QGroupBox(this);
        coreParticleGroup->setObjectName("Particle");

        core.layouter = std::make_unique<FormLayouter>(coreParticleGroup, ec);

        core.formfactorCombo = createFormFactorCombo(
            coreParticleGroup, coreShellItem->coreItem() != nullptr
                                   ? coreShellItem->coreItem()->formFactorItem()
                                   : nullptr);
        connect(core.formfactorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
                &CoreAndShellForm::onCoreComboChanged);
        core.layouter->addRow("Form factor:", core.formfactorCombo);
        core.collapser = GroupBoxCollapser::installIntoGroupBox(coreParticleGroup);

        auto* showInRealspaceAction =
            ActionFactory::createShowInRealspaceAction(this, "core particle");
        connect(showInRealspaceAction, &QAction::triggered, this,
                &CoreAndShellForm::showCoreInRealspace);
        core.collapser->addAction(showInRealspaceAction);
        core.collapser->setExpanded(coreShellItem->isExpandCore());
        connect(core.collapser, &GroupBoxCollapser::toggled, this,
                [coreShellItem](bool b) { coreShellItem->setExpandCore(b); });

        createCoreWidgets();

        layouter.addRow(coreParticleGroup);
    }

    // - shell
    {
        auto* shellParticleGroup = new QGroupBox(this);
        shellParticleGroup->setObjectName("Particle");
        shell.layouter = std::make_unique<FormLayouter>(shellParticleGroup, ec);
        shell.formfactorCombo = createFormFactorCombo(
            shellParticleGroup, coreShellItem->shellItem() != nullptr
                                    ? coreShellItem->shellItem()->formFactorItem()
                                    : nullptr);
        connect(shell.formfactorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
                &CoreAndShellForm::onShellComboChanged);
        shell.layouter->addRow("Form factor:", shell.formfactorCombo);
        shell.collapser = GroupBoxCollapser::installIntoGroupBox(shellParticleGroup);
        shell.collapser->setExpanded(coreShellItem->isExpandShell());
        connect(shell.collapser, &GroupBoxCollapser::toggled, this,
                [coreShellItem](bool b) { coreShellItem->setExpandShell(b); });

        auto* showInRealspaceAction =
            ActionFactory::createShowInRealspaceAction(this, "shell particle");
        connect(showInRealspaceAction, &QAction::triggered, this,
                &CoreAndShellForm::showShellInRealspace);
        shell.collapser->addAction(showInRealspaceAction);

        createShellWidgets();

        layouter.addRow(shellParticleGroup);
    }

    auto* mainCollapser = GroupBoxCollapser::installIntoGroupBox(this);
    mainCollapser->setExpanded(coreShellItem->isExpandMain());
    connect(mainCollapser, &GroupBoxCollapser::toggled, this,
            [coreShellItem](bool b) { coreShellItem->setExpandMain(b); });

    // top right corner actions
    // show in real space
    {
        auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
            this, "core/shell particle",
            [ec, coreShellItem] { ec->requestViewInRealspace(coreShellItem); });
        mainCollapser->addAction(showInRealspaceAction);
    }
    // duplicate
    {
        m_duplicateAction =
            ActionFactory::createDuplicateAction(this, "core/shell particle", [ec, coreShellItem] {
                ec->duplicateItemWithParticles(coreShellItem);
            });
        mainCollapser->addAction(m_duplicateAction);
    }
    // remove
    {
        m_removeAction =
            ActionFactory::createRemoveAction(this, "core/shell particle", [ec, coreShellItem] {
                ec->removeParticle(coreShellItem);
            });
        if (allowRemove)
            mainCollapser->addAction(m_removeAction);
    }
}

void CoreAndShellForm::onCoreComboChanged()
{
    while (core.layouter->layout()->rowCount() > 1)
        core.layouter->layout()->removeRow(1);

    const auto type = (FormFactorItemCatalog::Type)core.formfactorCombo->currentData().toUInt();
    m_ec->setCoreFormFactor(this, type);
}

void CoreAndShellForm::onShellComboChanged()
{
    while (shell.layouter->layout()->rowCount() > 1)
        shell.layouter->layout()->removeRow(1);

    const auto type = (FormFactorItemCatalog::Type)shell.formfactorCombo->currentData().toUInt();
    m_ec->setShellFormFactor(this, type);
}

void CoreAndShellForm::showCoreInRealspace()
{
    if (m_item->coreItem())
        m_ec->requestViewInRealspace(m_item->coreItem());
}

void CoreAndShellForm::showShellInRealspace()
{
    if (m_item->shellItem())
        m_ec->requestViewInRealspace(m_item->shellItem());
}

void CoreAndShellForm::createCoreWidgets()
{
    QString groupTitle = "Core";

    if (ParticleItem* particle = m_item->coreItem()) {
        const QString formfactor = FormFactorItemCatalog::menuEntry(particle->formFactorItem());
        groupTitle += " (" + formfactor + ")";

        core.layouter->addRow("Material", new MaterialInplaceForm(this, particle, m_ec));
        core.layouter->addGroupOfValues("Geometry",
                                        particle->formFactorItem()->geometryProperties());
        core.layouter->addVector(particle->position(), false);
        core.layouter->addSelection(particle->rotationSelection());
        // no abundance since this is handled in CoreShell itself!
    }

    core.collapser->setTitle(groupTitle);
}

void CoreAndShellForm::createShellWidgets()
{
    QString groupTitle = "Shell";

    if (ParticleItem* particle = m_item->shellItem()) {
        const QString formfactor = FormFactorItemCatalog::menuEntry(particle->formFactorItem());
        groupTitle += " (" + formfactor + ")";

        shell.layouter->addRow("Material", new MaterialInplaceForm(this, particle, m_ec));
        shell.layouter->addGroupOfValues("Geometry",
                                         particle->formFactorItem()->geometryProperties());
        shell.layouter->addSelection(particle->rotationSelection());
        // no position vector - not allowed in CoreShell
        // no abundance since this is handled in CoreShell itself!
    }

    shell.collapser->setTitle(groupTitle);
}

void CoreAndShellForm::enableStructureEditing(bool b)
{
    m_removeAction->setVisible(b);
    m_duplicateAction->setVisible(b);
}

CoreAndShellItem* CoreAndShellForm::coreShellItem() const
{
    return m_item;
}
