//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Sample/LayerForm.cpp
//! @brief     Implements class LayerForm.
//!
//! @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/LayerForm.h"
#include "Base/Util/Assert.h"
#include "Base/Util/Vec.h"
#include "GUI/Model/Material/MaterialItem.h"
#include "GUI/Model/Material/MaterialsSet.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sample/LayerItem.h"
#include "GUI/Model/Sample/RoughnessItems.h"
#include "GUI/Model/Sample/SampleItem.h"
#include "GUI/View/Numeric/NumWidgetUtil.h"
#include "GUI/View/Sample/HeinzFormLayout.h"
#include "GUI/View/Sample/MaterialInplaceForm.h"
#include "GUI/View/Sample/ParticleLayoutForm.h"
#include "GUI/View/Sample/RoughnessForm.h"
#include "GUI/View/Widget/WidgetMoverButton.h"
#include <QLineEdit>
#include <QPushButton>

LayerForm::LayerForm(QWidget* parent, LayerItem* layer, SampleEditorController* ec)
    : LayerContainerForm(parent, layer, ec, "layer")
{
    m_layout->addBoldRow("Material:", new MaterialInplaceForm(layer, ec));
    m_layout->addValue(layer->thickness());
    m_thickness_row = m_layout->rowCount() - 1;

    m_layout->addBoldRow("Number of slices:", GUI::Util::createIntSpinBox(
                                                  [this] { return layerItem()->numSlices(); },
                                                  [this](int v) {
                                                      layerItem()->setNumSlices(v);
                                                      emit gDoc->sampleChanged();
                                                  },
                                                  RealLimits::lowerLimited(1),
                                                  "Number of horizontal slices.\n"
                                                  "Used for Average Layer Material calculations \n"
                                                  "when corresponding simulation option is set."));

    m_roughnessForm =
        new RoughnessForm(this, layer->roughnessSelection(), layer->expandRoughness, m_ec);
    m_layout->addRow(m_roughnessForm);
    m_roughness_row = m_layout->rowCount() - 1;

    // -- layouts
    for (auto* layout : layer->layoutItems())
        m_layout->addRow(new ParticleLayoutForm(this, layout, ec));

    // -- Button for adding a new layout
    auto* btn = new QPushButton("Add particle layout", this);
    connect(btn, &QPushButton::clicked, [this, ec] { ec->addLayoutItem(this); });
    m_layout->addStructureEditingRow(btn);

    // listen to changes in materials to update the title (contains the material name). Necessary
    // to reflect e.g. a name change done in the material editor.
    connect(ec->materialModel(), &MaterialsSet::materialChanged, this, &LayerForm::updateTitle);

    updatePositionDependentElements();
}

LayerItem* LayerForm::layerItem() const
{
    return dynamic_cast<LayerItem*>(m_item);
}

void LayerForm::updateTitle()
{
    QString material_text = "     Material: " + layerItem()->materialName();
    if (layerItem()->isAmbient())
        setTitle("Ambient" + material_text);

    else if (layerItem()->isSubstrate())
        setTitle("Substrate" + material_text);

    else {
        int i = Vec::indexOfPtr(layerItem(), m_ec->sampleItem()->uniqueLayerItems());
        setTitle("Layer " + QString::number(i) + material_text);
    }
}

void LayerForm::updatePositionDependentElements()
{
    LayerContainerForm::updatePositionDependentElements();

    if (m_roughness_row == -1)
        return;

    updateTitle();

    const auto* sample = m_ec->sampleItem();
    const bool isAmbient = layerItem()->isAmbient();
    const bool isSubstrate = layerItem()->isSubstrate();
    const bool thicknessIsSemiInfinite =
        (isAmbient || isSubstrate) && (sample->uniqueLayerItems().size() > 1);
    const bool thicknessIsInfinite = isAmbient && (sample->uniqueLayerItems().size() == 1);

    m_roughnessForm->setVisible(!isAmbient);

    if (m_thickness_row == -1)
        return;

    m_roughnessForm->markLayerAsSubstrate(isSubstrate);

    QWidget* w = m_layout->itemAt(m_thickness_row, QFormLayout::FieldRole)->widget();
    if (thicknessIsSemiInfinite || thicknessIsInfinite) {
        auto* info = qobject_cast<QLineEdit*>(w);
        if (info == nullptr) {
            m_layout->removeRow(m_thickness_row);
            info = new QLineEdit(this);
            info->setEnabled(false);
            info->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
            m_layout->insertRow(m_thickness_row, layerItem()->thickness().label(), info);
        }
        info->setText(thicknessIsSemiInfinite ? "Semi-infinite" : "Infinite");
    } else if (!thicknessIsSemiInfinite && !thicknessIsInfinite
               && (qobject_cast<QLineEdit*>(w) != nullptr)) {
        m_layout->removeRow(m_thickness_row);
        m_layout->insertValue(m_thickness_row, layerItem()->thickness());
    }
}

void LayerForm::onLayoutAdded(ParticleLayoutItem* t)
{
    int index = Vec::indexOfPtr(t, layerItem()->layoutItems());
    const int rowInLayout = m_layout->rowCount() - layerItem()->layoutItems().size() + index;

    QWidget* w = new ParticleLayoutForm(this, t, m_ec);
    m_layout->insertRow(rowInLayout, w);
}

void LayerForm::onAboutToRemoveLayout(ParticleLayoutItem* t)
{
    int index = Vec::indexOfPtr(t, layerItem()->layoutItems());
    const int rowInLayout =
        m_layout->rowCount() - layerItem()->layoutItems().size() - 1 + index; // -1: btn

    m_layout->removeRow(rowInLayout);
}
