//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Loader/ImportDialogs.cpp
//! @brief     Implements class ImportDialog and its children.
//!
//! @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/Loader/ImportDialogs.h"
#include "Base/Util/Assert.h"
#include "GUI/View/Loader/DatafileInspector.h"
#include <QButtonGroup>
#include <QCheckBox>
#include <QComboBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QtGui>

ImportSettings1D Import1dDialog::Msettings("q (1/nm)", "#", "", 1, 2);
ImportSettings2D Import2dDialog::Msettings("qy (1/nm)", "qz (1/nm)");

ImportDialog::ImportDialog(QWidget* parent, QString fname)
    : QDialog(parent)
{
    setWindowTitle("Specify input format");
    setWindowFlags(Qt::Dialog);

    auto* viewer = new DatafileInspector(this, fname);
    viewer->show();
}

Import1dDialog::Import1dDialog(QWidget* parent, QString fname)
    : ImportDialog(parent, fname)
{
    auto* vbox = new QVBoxLayout;
    setLayout(vbox);

    auto* gridLayout = new QGridLayout;
    vbox->addLayout(gridLayout);

    auto* have_sR = new QCheckBox;
    gridLayout->addWidget(have_sR, 2, 0);
    have_sR->setCheckState(Msettings.col_sR == 0 ? Qt::Unchecked : Qt::Checked);
    // connect statement is below, as it needs access to other widgets

    gridLayout->addWidget(new QLabel("Read Q"), 0, 1);
    gridLayout->addWidget(new QLabel("Read R"), 1, 1);
    gridLayout->addWidget(new QLabel("Read σR"), 2, 1);

    gridLayout->addWidget(new QLabel("from  column"), 0, 2);
    gridLayout->addWidget(new QLabel("from  column"), 1, 2);

    auto* from_sR = new QLabel("from  column"); // this one can be made invisible
    gridLayout->addWidget(from_sR, 2, 2);
    from_sR->setVisible(Msettings.col_sR != 0);

    auto* sb_Q = new QSpinBox;
    gridLayout->addWidget(sb_Q, 0, 3);
    sb_Q->setMinimum(1);
    sb_Q->setValue(Msettings.col_Q);
    connect(sb_Q, &QSpinBox::valueChanged, [&p = Msettings](int i) { p.col_Q = i; });

    auto* sb_R = new QSpinBox;
    gridLayout->addWidget(sb_R, 1, 3);
    sb_R->setMinimum(1);
    sb_R->setValue(Msettings.col_R);
    connect(sb_R, &QSpinBox::valueChanged, [&p = Msettings](int i) { p.col_R = i; });

    auto* sb_sR = new QSpinBox;
    gridLayout->addWidget(sb_sR, 2, 3);
    sb_sR->setVisible(Msettings.col_sR != 0);
    sb_sR->setMinimum(1);
    sb_sR->setValue(Msettings.col_sR);
    connect(sb_sR, &QSpinBox::valueChanged, [&p = Msettings](int i) { p.col_sR = i; });

    connect(have_sR, &QCheckBox::stateChanged, [&p = Msettings, from_sR, sb_sR](int i) {
        from_sR->setVisible(i != 0);
        sb_sR->setVisible(i != 0);
        if (i == 0)
            Msettings.col_sR = 0;
        else
            sb_sR->setValue(p.col_R + 1);
    });

    auto* w21 = new QWidget;
    vbox->addWidget(w21);

    auto* form211 = new QFormLayout;
    w21->setLayout(form211);

    auto* linesToSkipEdit = new QLineEdit;
    form211->addRow("Ignore line numbers:", linesToSkipEdit);
    if (Msettings.linesToSkip.empty())
        linesToSkipEdit->setPlaceholderText("Example: 1-5, 12");
    else
        linesToSkipEdit->setText(QString::fromStdString(Msettings.linesToSkip));
    connect(linesToSkipEdit, &QLineEdit::textEdited,
            [&p = Msettings](const QString& text) { p.linesToSkip = text.toStdString(); });

    auto* headerPrefixEdit = new QLineEdit;
    form211->addRow("Ignore lines starting with:", headerPrefixEdit);
    if (Msettings.headerPrefix.empty())
        headerPrefixEdit->setPlaceholderText("#,//");
    else
        headerPrefixEdit->setText(QString::fromStdString(Msettings.headerPrefix));
    connect(headerPrefixEdit, &QLineEdit::textEdited,
            [&p = Msettings](const QString& text) { p.headerPrefix = text.toStdString(); });

    const std::vector<std::string> qCoords{"q (1/nm)",    "q (1/angstrom)", "alpha (rad)",
                                           "alpha (deg)", "2alpha (rad)",   "2alpha (deg)"};
    auto* qUnitCombo = new QComboBox;
    form211->addRow("Ordinate given as:", qUnitCombo);
    qUnitCombo->addItem("q (1/nm)");
    qUnitCombo->addItem("q (1/Å)");
    qUnitCombo->addItem("α (rad)");
    qUnitCombo->addItem("α (deg)");
    qUnitCombo->addItem("2α (rad)");
    qUnitCombo->addItem("2α (deg)");
    ASSERT(qUnitCombo->count() == (int)qCoords.size());
    for (int i = 0; i < qUnitCombo->count(); ++i)
        if (Msettings.xCoord == qCoords[i]) {
            qUnitCombo->setCurrentIndex(i);
            break;
        }
    connect(qUnitCombo, &QComboBox::currentIndexChanged,
            [&p = Msettings, qCoords](int i) { p.xCoord = qCoords[i]; });

    auto* sortCheckbox = new QCheckBox;
    sortCheckbox->setChecked(Msettings.sort);
    form211->addRow("Sort by Q", sortCheckbox);
    connect(sortCheckbox, &QCheckBox::toggled,
            [&p = Msettings](bool checked) { p.sort = checked; });

    auto* negativeCheckbox = new QCheckBox;
    negativeCheckbox->setChecked(Msettings.rm_negative);
    form211->addRow("Discard negative Q", negativeCheckbox);
    connect(negativeCheckbox, &QCheckBox::toggled,
            [&p = Msettings](bool checked) { p.rm_negative = checked; });

    auto* duplicateCheckbox = new QCheckBox;
    duplicateCheckbox->setChecked(Msettings.rm_duplications);
    form211->addRow("Discard Q duplications", duplicateCheckbox);
    connect(duplicateCheckbox, &QCheckBox::toggled,
            [&p = Msettings](bool checked) { p.rm_duplications = checked; });

    auto* buttonline = new QHBoxLayout;
    vbox->addLayout(buttonline);

    auto* okButton = new QPushButton("OK");
    buttonline->addWidget(okButton);
    okButton->setDefault(true);
    connect(okButton, &QPushButton::clicked, this, &Import1dDialog::accept);
}

Import2dDialog::Import2dDialog(QWidget* parent, QString fname)
    : ImportDialog(parent, fname)
{
    auto vbox = new QVBoxLayout;
    setLayout(vbox);

    auto gridLayout = new QGridLayout;
    vbox->addLayout(gridLayout);

    auto has_axes = new QCheckBox("Table contains axes");
    has_axes->setChecked(Msettings.has_axes);
    gridLayout->addWidget(has_axes, 0, 0);
    // connect statement is below, as it needs access to other widgets

    auto swap_axes = new QCheckBox("Swap axes");
    swap_axes->setChecked(Msettings.swap_axes);
    gridLayout->addWidget(swap_axes, 1, 0);

    // row

    auto row_label = new QLabel("Row:");
    gridLayout->addWidget(row_label, 2, 0);

    auto row_first = new QRadioButton("first");
    row_first->setChecked(Msettings.first_row);
    gridLayout->addWidget(row_first, 2, 1);
    connect(row_first, &QRadioButton::toggled, [](bool b) { Msettings.first_row = b; });

    auto row_last = new QRadioButton("last");
    row_last->setChecked(!Msettings.first_row);
    gridLayout->addWidget(row_last, 2, 2);
    connect(row_last, &QRadioButton::toggled, [](bool b) { Msettings.first_row = !b; });

    auto row_group = new QButtonGroup;
    row_group->addButton(row_first);
    row_group->addButton(row_last);

    // column

    auto col_label = new QLabel("Column:");
    gridLayout->addWidget(col_label, 3, 0);

    auto col_first = new QRadioButton("first");
    col_first->setChecked(Msettings.first_col);
    gridLayout->addWidget(col_first, 3, 1);
    connect(col_first, &QRadioButton::toggled, [](bool b) { Msettings.first_col = b; });

    auto col_last = new QRadioButton("last");
    col_last->setChecked(!Msettings.first_col);
    gridLayout->addWidget(col_last, 3, 2);
    connect(col_last, &QRadioButton::toggled, [](bool b) { Msettings.first_col = !b; });

    auto col_group = new QButtonGroup;
    col_group->addButton(col_first);
    col_group->addButton(col_last);

    // Q units
    auto form_l = new QFormLayout;
    vbox->addLayout(form_l);

    auto unit_label = new QLabel("Axes are given in units of:");

    const std::vector<std::string> xCoords{"qy (1/nm)", "qy (1/angstrom)", "phi_i (rad)",
                                           "phi_i (deg)"};
    const std::vector<std::string> yCoords{"qz (1/nm)", "qz (1/angstrom)", "alpha_i (rad)",
                                           "alpha_i (deg)"};
    ASSERT(xCoords.size() == yCoords.size());

    auto qUnitCombo = new QComboBox;
    form_l->addRow(unit_label, qUnitCombo);
    qUnitCombo->addItem("1/nm");
    qUnitCombo->addItem("1/Å");
    qUnitCombo->addItem("deg");
    qUnitCombo->addItem("rad");
    ASSERT(qUnitCombo->count() == (int)xCoords.size());
    for (int i = 0; i < qUnitCombo->count(); ++i)
        if (Msettings.xCoord == xCoords[i]) {
            qUnitCombo->setCurrentIndex(i);
            break;
        }
    connect(qUnitCombo, &QComboBox::currentIndexChanged, [=](int i) {
        Msettings.xCoord = xCoords[i];
        Msettings.yCoord = yCoords[i];

        // upd labels via toggling
        swap_axes->toggled(Msettings.swap_axes);
    });

    connect(swap_axes, &QCheckBox::toggled, [=](bool b) {
        Msettings.swap_axes = b;

        QString xName = QString::fromStdString(Msettings.xCoord.name());
        QString yName = QString::fromStdString(Msettings.yCoord.name());

        if (b) {
            row_label->setText("Row (" + yName + "):");
            col_label->setText("Column (" + xName + "):");
        } else {
            row_label->setText("Row (" + xName + "):");
            col_label->setText("Column (" + yName + ")");
        }
    });
    swap_axes->toggled(Msettings.swap_axes);

    connect(has_axes, &QCheckBox::toggled, [=](bool b) {
        Msettings.has_axes = b;

        swap_axes->setEnabled(b);

        row_label->setEnabled(b);
        row_first->setEnabled(b);
        row_last->setEnabled(b);

        col_label->setEnabled(b);
        col_first->setEnabled(b);
        col_last->setEnabled(b);

        unit_label->setEnabled(b);
        qUnitCombo->setEnabled(b);
    });
    has_axes->toggled(Msettings.has_axes);

    auto buttonline = new QHBoxLayout;
    vbox->addLayout(buttonline);

    auto okButton = new QPushButton("OK");
    buttonline->addWidget(okButton);
    okButton->setDefault(true);
    connect(okButton, &QPushButton::clicked, this, &Import2dDialog::accept);
}
