//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/SampleDesigner/FormLayouter.cpp
//! @brief     Implements class FormLayouter
//!
//! @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/FormLayouter.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Descriptor/VectorProperty.h"
#include "GUI/View/Numeric/DoubleSpinBox.h"
#include "GUI/View/SampleDesigner/LayerEditorUtil.h"
#include "GUI/View/SampleDesigner/SampleEditorController.h"
#include "GUI/View/Tool/GroupBoxCollapser.h"
#include <QFormLayout>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>

using std::function;

FormLayouter::FormLayouter(QWidget* parent, SampleEditorController* ec)
    : m_ec(ec)
{
    if (parent->layout() != nullptr) {
        m_formLayout = dynamic_cast<QFormLayout*>(parent->layout());
        if (m_formLayout == nullptr) {
            auto* collapser =
                GroupBoxCollapser::findInstalledCollapser(dynamic_cast<QGroupBox*>(parent));
            if (collapser)
                m_formLayout = dynamic_cast<QFormLayout*>(collapser->contentArea()->layout());
        }
        ASSERT(m_formLayout);
    } else {
        m_formLayout = new QFormLayout(parent);
        m_formLayout->setFormAlignment(Qt::AlignLeft | Qt::AlignBottom);
        m_formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
    }
}

QFormLayout* FormLayouter::layout()
{
    return m_formLayout;
}

void FormLayouter::setContentsMargins(int left, int top, int right, int bottom)
{
    m_formLayout->setContentsMargins(left, top, right, bottom);
}

int FormLayouter::addRow(const QString& label, QWidget* w)
{
    insertRow(m_formLayout->rowCount(), label, w);
    return m_formLayout->rowCount() - 1;
}

int FormLayouter::addRow(const QString& label, QSpinBox* w)
{
    return addRow(label, qobject_cast<QWidget*>(w));
}

void FormLayouter::insertRow(int row, QString label, QWidget* w)
{
    if (!label.endsWith(":"))
        label += ":";
    m_formLayout->insertRow(row, LayerEditorUtil::createBoldLabel(label), w);
}

int FormLayouter::addGroupOfValues(const QString& labelText, const DoubleProperties& values)
{
    auto* w = new QWidget(m_formLayout->parentWidget());
    w->setObjectName("PropertyBaseWidget");
    w->setAttribute(Qt::WA_StyledBackground, true);
    w->setStyleSheet("#PropertyBaseWidget {background-color: transparent}");

    auto* gridLayout = new QGridLayout(w);
    gridLayout->setContentsMargins(0, 0, 0, 0);
    gridLayout->setSpacing(6);

    LayerEditorUtil::addMultiPropertyToGrid(gridLayout, 0, values, m_ec, true);

    return addRow(labelText, w);
}

int FormLayouter::addVector(VectorProperty& d, bool vertically /*= true*/)
{
    auto* w = new QWidget(m_formLayout->parentWidget());
    w->setObjectName("PropertyBaseWidget");
    w->setAttribute(Qt::WA_StyledBackground, true);
    w->setStyleSheet("#PropertyBaseWidget {background-color: transparent}");

    auto* gridLayout = new QGridLayout(w);
    gridLayout->setContentsMargins(0, 0, 0, 0);
    gridLayout->setSpacing(6);

    LayerEditorUtil::addMultiPropertyToGrid(gridLayout, 0, {&d.x(), &d.y(), &d.z()}, m_ec,
                                            vertically, true);

    return addRow(d.label(), w);
}

void FormLayouter::setRowVisible(int row, bool visible)
{
    m_formLayout->itemAt(row, QFormLayout::LabelRole)->widget()->setVisible(visible);
    m_formLayout->itemAt(row, QFormLayout::FieldRole)->widget()->setVisible(visible);
}

void FormLayouter::removeRow(int row)
{
    m_formLayout->removeRow(row);
}

void FormLayouter::addStructureEditingRow(QPushButton* button)
{
    auto* w = new QWidget(m_formLayout->parentWidget());
    auto* l = new QHBoxLayout(w);
    l->setContentsMargins(0, 0, 0, 0);
    l->setAlignment(Qt::AlignLeft);
    l->setSizeConstraint(QLayout::SetMinimumSize);
    l->addWidget(button);
    l->addStretch();
    addRow(w);
}

int FormLayouter::addValue(DoubleProperty& d)
{
    insertValue(m_formLayout->rowCount(), d);
    return m_formLayout->rowCount() - 1;
}

int FormLayouter::addValue(DoubleProperty& d, function<void(double)> onValueChange)
{
    insertValue(m_formLayout->rowCount(), d, onValueChange);
    return m_formLayout->rowCount() - 1;
}

void FormLayouter::insertValue(int row, DoubleProperty& d)
{
    auto* ec = m_ec; // to allow copy-capture in the following lambda
    insertValue(row, d, [ec, &d](double newValue) { ec->setDouble(newValue, d); });
}

void FormLayouter::insertValue(int row, DoubleProperty& d, function<void(double)> onValueChange)
{
    auto labelText = d.label();
    if (!labelText.endsWith(":"))
        labelText += ":";

    auto* label = LayerEditorUtil::createBoldLabel(labelText);
    label->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
    label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);

    auto* editor = new DoubleSpinBox(d);
    QObject::connect(editor, &DoubleSpinBox::baseValueChanged, onValueChange);

    label->setBuddy(editor);

    LayerEditorUtil::updateLabelUnit(label, editor);
    m_formLayout->insertRow(row, label, editor);
}

int FormLayouter::addRow(QWidget* w)
{
    m_formLayout->addRow(w);
    return m_formLayout->rowCount() - 1;
}

void FormLayouter::insertRow(int row, QWidget* w)
{
    m_formLayout->insertRow(row, w);
}
