//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Sample/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/Sample/CoreAndShellForm.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Sample/CoreAndShellItem.h"
#include "GUI/Model/Sample/FormFactorItems.h"
#include "GUI/Model/Sample/FormfactorsCatalog.h"
#include "GUI/Model/Sample/ParticleItem.h"
#include "GUI/View/Base/ActionFactory.h"
#include "GUI/View/Sample/HeinzFormLayout.h"
#include "GUI/View/Sample/MaterialInplaceForm.h"
#include "GUI/View/Sample/SampleEditorController.h"
#include <QAction>

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 : FormfactorsCatalog::types()) {
        const auto ui = FormfactorsCatalog::uiInfo(type);
        combo->addItem(QIcon(ui.iconPath), ui.menuEntry, (uint8_t)type);
    }
    combo->setMaxVisibleItems(combo->count());
    combo->setCurrentIndex(combo->findData((uint8_t)FormfactorsCatalog::type(current)));

    return combo;
}

} // namespace


CoreAndShellForm::CoreAndShellForm(QWidget* parent, CoreAndShellItem* coreShellItem,
                                   SampleEditorController* ec, bool allowRemove)
    : CollapsibleGroupBox("Core/shell particle", parent, coreShellItem->expandMain)
    , m_item(coreShellItem)
    , m_ec(ec)
{
    auto* layout = new HeinzFormLayout(ec);
    layout->addVector(coreShellItem->position(), false);
    layout->addSelection(coreShellItem->rotationSelection());
    layout->addValue(coreShellItem->abundance());
    body()->setLayout(layout);

    // - core
    {
        auto* coreParticleGroup = new CollapsibleGroupBox("Core", this, coreShellItem->expandCore);
        coreParticleGroup->setObjectName("Particle");

        core.layout = new HeinzFormLayout(ec);
        coreParticleGroup->body()->setLayout(core.layout);

        core.formfactorCombo = createFormFactorCombo(
            coreParticleGroup, coreShellItem->coreItem() != nullptr
                                   ? coreShellItem->coreItem()->formFactorItem()
                                   : nullptr);
        connect(core.formfactorCombo, &QComboBox::currentIndexChanged, this,
                &CoreAndShellForm::onCoreComboChanged);
        core.layout->addBoldRow("Form factor:", core.formfactorCombo);

        auto* showInRealspaceAction =
            ActionFactory::createShowInRealspaceAction(this, "core particle");
        connect(showInRealspaceAction, &QAction::triggered, this,
                &CoreAndShellForm::showCoreInRealspace);
        coreParticleGroup->addTitleAction(showInRealspaceAction);

        createCoreWidgets();

        layout->addRow(coreParticleGroup);
    }

    // - shell
    {
        auto* shellParticleGroup =
            new CollapsibleGroupBox("Shell", this, coreShellItem->expandShell);
        shellParticleGroup->setObjectName("Particle");

        shell.layout = new HeinzFormLayout(ec);
        shellParticleGroup->body()->setLayout(shell.layout);

        shell.formfactorCombo = createFormFactorCombo(
            shellParticleGroup, coreShellItem->shellItem() != nullptr
                                    ? coreShellItem->shellItem()->formFactorItem()
                                    : nullptr);
        connect(shell.formfactorCombo, &QComboBox::currentIndexChanged, this,
                &CoreAndShellForm::onShellComboChanged);
        shell.layout->addBoldRow("Form factor:", shell.formfactorCombo);

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

        createShellWidgets();

        layout->addRow(shellParticleGroup);
    }

    //... top right corner actions

    // show in real space
    auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
        this, "core/shell particle",
        [ec, coreShellItem] { ec->requestViewInRealspace(coreShellItem); });
    addTitleAction(showInRealspaceAction);

    // duplicate
    m_duplicate_action =
        ActionFactory::createDuplicateAction(this, "core/shell particle", [ec, coreShellItem] {
            ec->duplicateItemWithParticles(coreShellItem);
        });
    addTitleAction(m_duplicate_action);

    // remove
    m_remove_action = ActionFactory::createRemoveAction(
        this, "core/shell particle", [ec, coreShellItem] { ec->removeParticle(coreShellItem); });
    if (allowRemove)
        addTitleAction(m_remove_action);
}

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

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

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

    const auto type = (FormfactorsCatalog::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 = FormfactorsCatalog::menuEntry(particle->formFactorItem());
        groupTitle += " (" + formfactor + ")";

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

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

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

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