//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Sample/SampleForm.cpp
//! @brief     Implements class SampleForm.
//!
//! @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/SampleForm.h"
#include "Base/Util/Vec.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sample/SamplesSet.h"
#include "GUI/View/Base/ActionFactory.h"
#include "GUI/View/Base/LayoutUtil.h"
#include "GUI/View/Sample/CompoundForm.h"
#include "GUI/View/Sample/CoreAndShellForm.h"
#include "GUI/View/Sample/HeinzFormLayout.h"
#include "GUI/View/Sample/LayerForm.h"
#include "GUI/View/Sample/MesocrystalForm.h"
#include "GUI/View/Sample/ParticleForm.h"
#include "GUI/View/Sample/ParticleLayoutForm.h"
#include "GUI/View/Sample/SampleEditorController.h"
#include "GUI/View/Widget/AppConfig.h"
#include "GUI/View/Widget/GroupBoxes.h"
#include <QBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTextEdit>

namespace {

//! Widget with a button to add a layer (the "add layer" buttons shown between layers)
class AddLayerWidget : public QWidget {
public:
    AddLayerWidget(QWidget* parent, LayerItem* layer, SampleEditorController* ec)
        : QWidget(parent)
        , m_layer(layer)
    {
        auto* l = new QHBoxLayout(this);
        l->setContentsMargins(0, 0, 0, 0);
        auto* btn = new QPushButton("Add layer", this);
        l->addStretch();
        l->addWidget(btn);
        l->addStretch();
        connect(btn, &QPushButton::clicked, [=] { ec->addLayerItem(layer); });
    }

    LayerItem* m_layer;
};

} // namespace


SampleForm::SampleForm(SampleItem* sampleItem, SampleEditorController* ec)
    : m_layout(new QVBoxLayout(this))
    , m_sample_item(sampleItem)
    , m_ec(ec)
{
    setAttribute(Qt::WA_StyledBackground, true);

    m_layout->setAlignment(Qt::AlignTop);

    auto* summary = new CollapsibleGroupBox("Summary and layer-independent parameters", this,
                                            sampleItem->expandInfo);
    m_layout->addWidget(summary, 0, Qt::AlignTop);
    summary->setObjectName("SampleSummary");

    auto* gLayout = new HeinzFormLayout(ec);
    summary->body()->setLayout(gLayout);

    auto* nameEdit = new QLineEdit;
    gLayout->addBoldRow("Name:", nameEdit);
    nameEdit->setText(sampleItem->name());
    nameEdit->setFixedWidth(585);
    connect(nameEdit, &QLineEdit::textEdited,
            [](const QString& s) { gDoc->samplesRW()->setCurrentName(s); });

    auto* descriptionEdit = new QTextEdit;
    gLayout->addBoldRow("Description:", descriptionEdit);
    descriptionEdit->setFixedWidth(585);
    descriptionEdit->setFixedHeight(60);
    descriptionEdit->setAcceptRichText(false);
    descriptionEdit->setTabChangesFocus(true);
    descriptionEdit->setPlainText(sampleItem->description());
    connect(descriptionEdit, &QTextEdit::textChanged, [descriptionEdit] {
        gDoc->samplesRW()->setCurrentDescription(descriptionEdit->toPlainText());
    });

    gLayout->addValue(sampleItem->crossCorrLength());

    // Processing external field is not implemented yet, so temporary disable it (see issue #654)
    // m_layout->addVector(sampleItem->externalField(), false);

    auto* showInRealspaceAction = ActionFactory::createShowInRealspaceAction(
        this, "sample", [this] { m_ec->requestViewInRealspace(m_sample_item); });

    summary->addTitleAction(showInRealspaceAction);

    for (auto* layer : sampleItem->layerItems()) {
        m_layout->addWidget(new AddLayerWidget(this, layer, m_ec), 0, Qt::AlignTop);
        m_layout->addWidget(new LayerForm(this, layer, m_ec), 0, Qt::AlignTop);
    }
    m_layout->addWidget(new AddLayerWidget(this, nullptr, m_ec), 0, Qt::AlignTop);
    m_layout->setSizeConstraint(QLayout::SetMinimumSize);
}

void SampleForm::onLayerAdded(LayerItem* layerItem)
{
    const int rowInMultiLayer = Vec::indexOfPtr(layerItem, m_sample_item->layerItems());

    const int rowInLayout = rowInMultiLayer * 2 + 1;

    m_layout->insertWidget(rowInLayout, new LayerForm(this, layerItem, m_ec), 0, Qt::AlignTop);

    // same row => button is above!
    m_layout->insertWidget(rowInLayout, new AddLayerWidget(this, layerItem, m_ec), 0, Qt::AlignTop);

    updateRowVisibilities();
}

void SampleForm::onLayerMoved(LayerItem* layerItem)
{
    LayerForm* wl = nullptr;
    AddLayerWidget* al = nullptr;
    for (int index = 0; index < m_layout->count(); index++) {
        if (auto* w = dynamic_cast<AddLayerWidget*>(m_layout->itemAt(index)->widget()))
            if (w->m_layer == layerItem) {
                al = w;
                m_layout->takeAt(index);
                break;
            }
    }

    for (int index = 0; index < m_layout->count(); index++) {
        if (auto* w = dynamic_cast<LayerForm*>(m_layout->itemAt(index)->widget()))
            if (w->layerItem() == layerItem) {
                wl = w;
                m_layout->takeAt(index);
                break;
            }
    }

    const int rowInMultiLayer = Vec::indexOfPtr(layerItem, m_sample_item->layerItems());
    const int rowInLayout = rowInMultiLayer * 2 + 1;

    m_layout->insertWidget(rowInLayout, wl, 0, Qt::AlignTop);

    // same row => button is above!
    m_layout->insertWidget(rowInLayout, al, 0, Qt::AlignTop);

    updateRowVisibilities();
}

void SampleForm::onAboutToRemoveLayer(LayerItem* layerItem)
{
    LayerForm* layerForm = nullptr;
    AddLayerWidget* addLayerWidget = nullptr;
    for (auto* c : findChildren<QWidget*>()) {
        if (auto* w = dynamic_cast<AddLayerWidget*>(c))
            if (w->m_layer == layerItem)
                addLayerWidget = w;

        if (auto* w = dynamic_cast<LayerForm*>(c)) {
            if (w->layerItem() == layerItem)
                layerForm = w;
        }
    }

    if (layerForm) {
        // delete editors which are subscribed to SessionItems
        GUI::Util::Layout::clearLayout(layerForm->layout());
        layerForm->hide();
        layerForm->setParent(nullptr); // so it is not findable in update routines
        layerForm->deleteLater();      // delete later (this is the sender)
    }

    delete addLayerWidget;
}

void SampleForm::updateRowVisibilities()
{
    for (auto* c : findChildren<LayerForm*>())
        c->updateLayerPositionDependentElements();
}

LayerForm* SampleForm::findNextLayerForm(QWidget* w)
{
    while (w != nullptr && dynamic_cast<LayerForm*>(w) == nullptr) {
        const auto index = m_layout->indexOf(w);
        if (index + 1 < m_layout->count())
            w = m_layout->itemAt(index + 1)->widget();
        else
            return nullptr;
    }

    return dynamic_cast<LayerForm*>(w);
}
