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

#include "GUI/View/Device/DetectorAlignmentForm.h"
#include "GUI/Model/Detector/RectangularDetectorItem.h"
#include "GUI/View/Numeric/DoubleSpinBox.h"
#include "GUI/View/Numeric/NumWidgetUtil.h"
#include <QLabel>

namespace {
QString alignmentDescription(RectangularDetector::EDetectorArrangement a)
{
    switch (a) {
    case RectangularDetector::GENERIC:
        return "Intersection of normal and detector";
    case RectangularDetector::PERPENDICULAR_TO_SAMPLE:
        return "Intersection of sample x-axis and detector";
    case RectangularDetector::PERPENDICULAR_TO_DIRECT_BEAM:
        return "Intersection of direct beam and detector";
    case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM:
        return "Intersection of reflected beam and detector";
    default:
        ASSERT(false);
    }
}

} // namespace

DetectorAlignmentForm::DetectorAlignmentForm(QWidget* parent, RectangularDetectorItem* item)
    : QWidget(parent)
    , m_item(item)
{
    ASSERT(m_item);
    m_formLayout = new QFormLayout(this);
    m_formLayout->setContentsMargins(0, 15, 0, 0);
    m_formLayout->setSpacing(8);

    m_formLayout->addRow("Alignment:", GUI::Util::createSafeComboBox(
                                           [item] { return item->detectorAlignmentCombo(); },
                                           [this, item](const QString& t) {
                                               item->setDetectorAlignment(t);
                                               createAligmentWidgets();
                                               emit dataChanged();
                                           }));

    createAligmentWidgets();
}

DoubleSpinBox* DetectorAlignmentForm::createSpinBox(QFormLayout* parentFormLayout,
                                                    DoubleProperty& d)
{
    auto* sb = GUI::Util::createDoubleSpinBoxRow(parentFormLayout, d);
    connect(sb, &DoubleSpinBox::baseValueChanged, [this, &d](double v) {
        if (d.value() != v) {
            d.setValue(v);
            emit dataChanged();
        }
    });
    return sb;
}

DoubleSpinBox* DetectorAlignmentForm::createSpinBox(DoubleProperty& d)
{
    auto* sb = new DoubleSpinBox(d);
    connect(sb, &DoubleSpinBox::baseValueChanged, [this, &d](double v) {
        if (d.value() != v) {
            d.setValue(v);
            emit dataChanged();
        }
    });
    return sb;
}

void DetectorAlignmentForm::addVector(QFormLayout* parentLayout, VectorProperty& d)
{
    auto* layout = new QHBoxLayout;

    const auto add = [this, layout](DoubleProperty& d) {
        layout->addWidget(new QLabel(GUI::Util::labelWithUnit(d) + ":"));
        layout->addWidget(createSpinBox(d));
    };

    add(d.x());
    add(d.y());
    add(d.z());

    layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));

    parentLayout->addRow(d.label() + ":", layout);
}

void DetectorAlignmentForm::createAligmentWidgets()
{
    while (m_formLayout->rowCount() > 1)
        m_formLayout->removeRow(1);

    const QString descr = alignmentDescription(m_item->detectorAlignment());
    auto* layout = new QFormLayout;
    layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
    layout->setContentsMargins(0, 0, 0, 0);
    m_formLayout->addRow("", layout);

    if (m_item->detectorAlignment() == RectangularDetector::GENERIC) {
        addVector(layout, m_item->normalVector());
        addVector(layout, m_item->directionVector());
        auto* layoutUV = new QFormLayout;
        createSpinBox(layoutUV, m_item->u0());
        createSpinBox(layoutUV, m_item->v0());
        layout->addRow(descr + ":", layoutUV);
    } else {
        auto* layoutUVD = new QFormLayout;
        createSpinBox(layoutUVD, m_item->u0());
        createSpinBox(layoutUVD, m_item->v0());
        createSpinBox(layoutUVD, m_item->distance());
        layout->addRow(descr + ":", layoutUVD);
    }
}
