//  ************************************************************************************************
//
//  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/Util/Assert.h"
#include "GUI/Model/Axis/AmplitudeAxisItem.h"
#include "GUI/Model/Axis/BasicAxisItem.h"
#include "GUI/Model/Data/Data1DItem.h"
#include "GUI/Model/Data/DataItem.h"
#include "GUI/Model/Descriptor/ComboProperty.h"
#include "GUI/Model/File/DatafilesSet.h"
#include "GUI/Model/Project/DataSource.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/Model/Sim/InstrumentItems.h"
#include "GUI/View/Base/LayoutUtil.h"
#include "GUI/View/Numeric/DoubleSpinBox.h"
#include "GUI/View/Numeric/NumWidgetUtil.h"
#include "GUI/View/Widget/GroupBoxes.h"
#include <QAction>
#include <QCheckBox>
#include <QFormLayout>
#include <QLineEdit>

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

    auto* main_layout = new QFormLayout(this);
    main_layout->setContentsMargins(8, 20, 8, 8);
    main_layout->setSpacing(5);

    // -- 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 dataItem()->axItemX()->min(); },
                                    [this](double newValue) {
                                        for (auto* item : m_data_source->allData1DItems())
                                            item->axItemX()->setMin(newValue);
                                        gDoc->setModified();
                                    },
                                    &m_updaters));

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

    main_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* logRangeSpinBox = GUI::Util::createDoubleSpinBox(
        [this] { return dataItem()->axItemY()->logRangeOrders(); },
        [this](double newValue) {
            for (auto* item : m_data_source->mainData1DItems()) {
                item->axItemY()->setLogRangeOrders(newValue);
                updateUIValues();
            }
            gDoc->setModified();
        },
        &m_updaters, "Dynamic range to display values", RealLimits::positive());

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

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

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

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

    main_layout->addRow(yGroup);

    connect(gDoc->datafiles(), &DatafilesSet::setChanged, this, &AxisPanel::updatePanel);
    updatePanel();
}

AxisPanel::~AxisPanel() = default;

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

void AxisPanel::updatePanel()
{
    if (dataItem()) {
        // react on external changes (e.g. zooming in customplot shall update the axis values)
        connect(dataItem(), &DataItem::itemAxesRangeChanged, this, &AxisPanel::updateUIValues,
                Qt::UniqueConnection);
        // update coordinates on axes units change
        for (auto* item : m_data_source->allData1DItems())
            connect(item, &DataItem::axesUnitsChanged, this, &AxisPanel::updateItemCoords,
                    Qt::UniqueConnection);
        updateUIValues();
        show();
    } else
        hide();
}

void AxisPanel::updateItemCoords(DataItem* item)
{
    if (!item)
        return;

    emit item->axesUnitsReplotRequested();
    updateUIValues();
}

void AxisPanel::updateUIValues()
{
    ASSERT(dataItem());
    for (const auto& updater : m_updaters)
        updater();
}
