//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Setup/AxisPanel.cpp
//! @brief     Implements class AxisPanel.
//!
//! @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/AxisPanel.h"
#include "Base/Axis/Frame.h"
#include "Base/Math/Numeric.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Axis/AmplitudeAxisItem.h"
#include "GUI/Model/Data/Data1DItem.h"
#include "GUI/Model/Data/DataItem.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/Base/LayoutUtil.h"
#include "GUI/View/Numeric/DSpinBox.h"
#include "GUI/View/Numeric/NumWidgetUtil.h"
#include "GUI/View/Widget/GroupBoxes.h"
#include <QAction>
#include <QCheckBox>
#include <QLineEdit>

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

    m_editor_layout = new QFormLayout(this);
    m_editor_layout->setContentsMargins(8, 20, 8, 8);
    m_editor_layout->setSpacing(5);

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

AxisPanel::~AxisPanel() = default;

void AxisPanel::updatePanel()
{
    if (!d1Item())
        return;

    updateAxesProperties();

    GUI::Util::Layout::clearLayout(m_editor_layout);

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

    auto* x_min_sb = GUI::Util::addDoubleSpinBoxRow(xFormLayout, d1Item()->axItemX()->min());
    connect(x_min_sb, &DSpinBox::valueChanged, [this](double newValue) {
        for (auto* item : m_data_source->allData1DItems())
            item->axItemX()->setMin(newValue);
    });

    auto* x_max_sb = GUI::Util::addDoubleSpinBoxRow(xFormLayout, d1Item()->axItemX()->max());
    connect(x_max_sb, &DSpinBox::valueChanged, [this](double newValue) {
        for (auto* item : m_data_source->allData1DItems())
            item->axItemX()->setMax(newValue);
    });

    xFormLayout->addRow(GUI::Util::createCheckBox(
        "log10", [this] { return d1Item()->axItemX()->isLogScale(); },
        [this](bool b) {
            for (auto* item : m_data_source->allData1DItems())
                item->axItemX()->setLogScale(b);
            gDoc->setModified();
        }));

    m_editor_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);

    auto* y_min_sb = GUI::Util::addDoubleSpinBoxRow(yFormLayout, d1Item()->axItemY()->min());
    connect(y_min_sb, &DSpinBox::valueChanged, [this](double newValue) {
        for (auto* item : m_data_source->mainData1DItems()) {
            item->axItemY()->setMin(newValue);
            item->axItemY()->adjustLogRangeOrders();
        }
    });

    auto* y_max_sb = GUI::Util::addDoubleSpinBoxRow(yFormLayout, d1Item()->axItemY()->max());
    connect(y_max_sb, &DSpinBox::valueChanged, [this](double newValue) {
        for (auto* item : m_data_source->mainData1DItems()) {
            item->axItemY()->setMax(newValue);
            item->axItemY()->adjustLogRangeOrders();
        }
    });

    auto* logRange_sb = new DSpinBox(&d1Item()->axItemY()->logRangeOrders());
    connect(logRange_sb, &DSpinBox::valueChanged, [this](double newValue) {
        for (auto* item : m_data_source->mainData1DItems())
            item->axItemY()->setLogRangeOrders(newValue);
    });

    yFormLayout->addRow(GUI::Util::createCheckBox(
        "log10", [this] { return d1Item()->axItemY()->isLogScale(); },
        [this, logRange_sb](bool b) {
            logRange_sb->setEnabled(b);
            for (auto* item : m_data_source->allData1DItems())
                item->axItemY()->setLogScale(b);
            gDoc->setModified();
        }));

    yFormLayout->addRow(d1Item()->axItemY()->logRangeOrders().label() + ":", logRange_sb);

    m_editor_layout->addRow(yGroup);
}

void AxisPanel::updateAxesProperties()
{
    ASSERT(d1Item());

    const double step_factor = 0.005;
    const int start_decimals = 4;

    // x axis
    auto* aX = d1Item()->axItemX();
    const double x_range = std::abs(aX->max().dVal() - aX->min().dVal());
    const int x_decimals = start_decimals - Numeric::orderOfMagnitude(x_range);
    const double x_step = x_range * step_factor;

    aX->min().setStepAndDecimals(true, x_step, x_decimals);
    aX->max().setStepAndDecimals(true, x_step, x_decimals);
}

//  ************************************************************************************************
//  shortcuts
//  ************************************************************************************************

Data1DItem* AxisPanel::d1Item()
{
    return m_data_source->currentData1DItem();
}
