//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Setup/AxesPanel.cpp
//! @brief     Implements class AxesPanel.
//!
//! @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/Setup/AxesPanel.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Axis/AmplitudeAxisItem.h"
#include "GUI/Model/Data/Data2DItem.h"
#include "GUI/Model/File/DatafilesSet.h"
#include "GUI/Model/Job/JobsSet.h"
#include "GUI/Model/Project/DataSource.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/View/Numeric/NumWidgetUtil.h"
#include "GUI/View/Widget/AppConfig.h"
#include "GUI/View/Widget/GroupBoxes.h"
#include <QCheckBox>
#include <QFormLayout>
#include <QLineEdit>

AxesPanel::AxesPanel(const DataSource* ds)
    : m_data_source(ds)
{
    setWindowTitle("Properties");
    setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);

    auto* layout = new QFormLayout(this);

    layout->setContentsMargins(8, 20, 8, 8);
    layout->setSpacing(5);

    layout->addRow(GUI::Util::createCheckBox(
        "Interpolate", [this] { return m_data_source->currentData2DItem()->isInterpolated(); },
        [this](bool b) {
            for (auto* item : m_data_source->allData2DItems())
                item->setInterpolated(b);
            gDoc->setModified();
        },
        &m_updaters));

    // -- x-axis
    auto* xGroup = new StaticGroupBox("X axis", this);
    auto* xFormLayout = new QFormLayout(xGroup->body());
    xFormLayout->setContentsMargins(0, 0, 0, 0);
    xFormLayout->setSpacing(5);

    xFormLayout->addRow("Min:",
                        GUI::Util::createDoubleSpinBox(
                            [this] { return m_data_source->currentData2DItem()->axItemX()->min(); },
                            [this](double newValue) {
                                for (auto* item : m_data_source->allData2DItems())
                                    item->axItemX()->setMin(newValue);
                                gDoc->setModified();
                            },
                            &m_updaters));

    xFormLayout->addRow("Max:",
                        GUI::Util::createDoubleSpinBox(
                            [this] { return m_data_source->currentData2DItem()->axItemX()->max(); },
                            [this](double newValue) {
                                for (auto* item : m_data_source->allData2DItems())
                                    item->axItemX()->setMax(newValue);
                                gDoc->setModified();
                            },
                            &m_updaters));

    layout->addRow(xGroup);

    // -- y-axis
    auto* yGroup = new StaticGroupBox("Y axis", this);
    auto* yFormLayout = new QFormLayout(yGroup->body());
    yFormLayout->setContentsMargins(0, 0, 0, 0);
    yFormLayout->setSpacing(5);

    yFormLayout->addRow("Min:",
                        GUI::Util::createDoubleSpinBox(
                            [this] { return m_data_source->currentData2DItem()->axItemY()->min(); },
                            [this](double newValue) {
                                for (auto* item : m_data_source->allData2DItems())
                                    item->axItemY()->setMin(newValue);
                                gDoc->setModified();
                            },
                            &m_updaters));

    yFormLayout->addRow("Max:",
                        GUI::Util::createDoubleSpinBox(
                            [this] { return m_data_source->currentData2DItem()->axItemY()->max(); },
                            [this](double newValue) {
                                for (auto* item : m_data_source->allData2DItems())
                                    item->axItemY()->setMax(newValue);
                                gDoc->setModified();
                            },
                            &m_updaters));

    layout->addRow(yGroup);

    // -- color-axis
    auto* zGroup = new StaticGroupBox("Color legend", this);
    auto* zFormLayout = new QFormLayout(zGroup->body());
    zFormLayout->setContentsMargins(0, 0, 0, 0);
    zFormLayout->setSpacing(5);

    auto* logRangeSpinBox = GUI::Util::createDoubleSpinBox(
        [this] { return m_data_source->currentData2DItem()->zAxisItem()->logRangeOrders(); },
        [this](double newValue) {
            for (auto* item : m_data_source->mainData2DItems()) {
                item->zAxisItem()->setLogRangeOrders(newValue);
                updateUIValues();
            }
            gDoc->setModified();
        },
        &m_updaters, "Dynamic range to display values", RealLimits::positive());

    zFormLayout->addRow(
        "Min:", GUI::Util::createDoubleSpinBox(
                    [this] { return m_data_source->currentData2DItem()->zAxisItem()->min(); },
                    [this](double newValue) {
                        for (auto* item : m_data_source->allData2DItems()) {
                            item->zAxisItem()->setMin(newValue);
                            item->zAxisItem()->adjustLogRangeOrders();
                            updateUIValues();
                        }
                        gDoc->setModified();
                    },
                    &m_updaters));

    zFormLayout->addRow(
        "Max:", GUI::Util::createDoubleSpinBox(
                    [this] { return m_data_source->currentData2DItem()->zAxisItem()->max(); },
                    [this](double newValue) {
                        for (auto* item : m_data_source->mainData2DItems()) {
                            item->zAxisItem()->setMax(newValue);
                            item->zAxisItem()->adjustLogRangeOrders();
                            updateUIValues();
                        }
                        gDoc->setModified();
                    },
                    &m_updaters));

    zFormLayout->addRow(GUI::Util::createCheckBox(
        "log10", [this] { return m_data_source->currentData2DItem()->zAxisItem()->isLogScale(); },
        [this, logRangeSpinBox](bool b) {
            logRangeSpinBox->setEnabled(b);
            for (auto* item : m_data_source->allData2DItems())
                item->zAxisItem()->setLogScale(b);
            gDoc->setModified();
        },
        &m_updaters));

    zFormLayout->addRow("Log range:", logRangeSpinBox);

    zFormLayout->addRow(GUI::Util::createCheckBox(
        "Visible", [this] { return m_data_source->currentData2DItem()->zAxisItem()->isVisible(); },
        [this](bool b) {
            for (auto* item : m_data_source->allData2DItems())
                item->zAxisItem()->setVisible(b);
            gDoc->setModified();
        },
        &m_updaters));

    layout->addRow(zGroup);

    // subscription type depends on the context
    if (m_data_source->isFromData())
        connect(gDoc->datafiles(), &DatafilesSet::setChanged, this, &AxesPanel::updatePanel);
    else
        connect(gDoc->jobs(), &JobsSet::jobPlotContextChanged, this, &AxesPanel::updatePanel);

    updatePanel();
}

AxesPanel::~AxesPanel() = default;

Data2DItem* AxesPanel::d2Item()
{
    return m_data_source->currentData2DItem();
}

void AxesPanel::updatePanel()
{
    if (d2Item()) {
        // react on external changes (e.g. zooming in customplot shall update the axis values)
        connect(m_data_source->currentData2DItem(), &DataItem::itemAxesRangeChanged, this,
                &AxesPanel::updateUIValues, Qt::UniqueConnection);
        updateUIValues();
        show();
    } else
        hide();
}

void AxesPanel::updateUIValues()
{
    ASSERT(d2Item());
    for (const auto& updater : m_updaters)
        updater();
}
