//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/coregui/Views/SpecularDataWidgets/SpecularDataImportWidget.cpp
//! @brief     Implements class SpecularDataImportWidget
//!
//! @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/coregui/Views/SpecularDataWidgets/SpecularDataImportWidget.h"
#include "GUI/coregui/DataLoaders/AbstractDataLoader.h"
#include "GUI/coregui/DataLoaders/AbstractDataLoader1D.h"
#include "GUI/coregui/DataLoaders/AbstractDataLoaderResultModel.h"
#include "GUI/coregui/DataLoaders/DataLoaders1D.h"
#include "GUI/coregui/DataLoaders/QREDataLoader.h"
#include "GUI/coregui/Models/DataItemUtils.h"
#include "GUI/coregui/Models/InstrumentItems.h"
#include "GUI/coregui/Models/RealDataItem.h"
#include "GUI/coregui/Models/SpecularDataItem.h"
#include "GUI/coregui/mainwindow/AppSvc.h"
#include "GUI/coregui/mainwindow/mainwindow.h"
#include "GUI/coregui/mainwindow/projectmanager.h"
#include "ui_SpecularDataImportWidget.h"
#include <QAction>
#include <QBoxLayout>
#include <QFileDialog>
#include <QMenu>
#include <QStringListModel>
#include <QTextStream>
#include <QTimer>

SpecularDataImportWidget::SpecularDataImportWidget(QWidget* parent)
    : SessionItemWidget(parent), m_ui(new Ui::SpecularDataImportWidget), m_loader(nullptr)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    m_ui->setupUi(this);
    m_ui->warningsIcon->setFixedSize(20, 20);
    m_ui->warningsIcon->setPixmap(QPixmap(":/images/warning_16x16.png"));

    m_ui->linkedInstrumentGroup->hide(); // #baimport - remove from UI if not used in the future

    // #baUserDefLoaders - remove next line when implementation is complete
    m_ui->createNewFormatButton->hide();

    fillLoaderCombo();
    updatePropertiesEdits();

    connect(m_ui->formatSelectionComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
            this, &SpecularDataImportWidget::onFormatSelectionChanged);
    connect(m_ui->createNewFormatButton, &QPushButton::clicked, this,
            &SpecularDataImportWidget::onCreateNewFormatButton);

    connect(m_ui->originalRowCheckBox, &QCheckBox::stateChanged, this,
            &SpecularDataImportWidget::updatePreview);

    connect(m_ui->rawDataCheckBox, &QCheckBox::stateChanged, this,
            &SpecularDataImportWidget::updatePreview);

    connect(m_ui->calculatedDataCheckBox, &QCheckBox::stateChanged, this,
            &SpecularDataImportWidget::updatePreview);

    connect(m_ui->specularDataCanvas->customPlot(), &QCustomPlot::axisClick, this,
            &SpecularDataImportWidget::onPlotAxisClicked);

    m_ui->specularDataCanvas->enableDeprecatedOnMousePress(false); // we have an own handler

    m_ui->plotToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    for (auto action : m_ui->specularDataCanvas->actionList())
        m_ui->plotToolBar->addAction(action);
}

void SpecularDataImportWidget::setItem(SessionItem* _realDataItem)
{
    SessionItemWidget::setItem(_realDataItem);
    m_ui->specularDataCanvas->setItem(specularDataItem());

    m_loader = dynamic_cast<AbstractDataLoader1D*>(realDataItem()->dataLoader());
    ASSERT(m_loader); // only items which have a loader are allowed for this widget. Every other
                      // items do not support this widget

    fillLoaderCombo();

    QSignalBlocker b(m_ui->formatSelectionComboBox);
    m_ui->formatSelectionComboBox->setCurrentText(m_loader->name());

    updatePropertiesEdits();
    updatePreview();
    connect(m_loader, &AbstractDataLoader::importSettingsChanged, this,
            &SpecularDataImportWidget::onPropertiesChanged);
}

QList<QAction*> SpecularDataImportWidget::actionList()
{
    return {};
}

void SpecularDataImportWidget::onContextMenuRequest(const QPoint& point)
{
    QMenu menu;
    for (auto action : actionList())
        menu.addAction(action);
    menu.exec(point);
}

void SpecularDataImportWidget::onPlotAxisClicked(QCPAxis* axis, QCPAxis::SelectablePart /*part*/,
                                                 QMouseEvent* event)
{
    if (event->button() == Qt::RightButton && axis->axisType() == QCPAxis::atLeft) {
        QMenu menu;

        QAction* lin = new QAction("Linear");
        connect(lin, &QAction::triggered, [=]() { specularDataItem()->setLog(false); });
        lin->setCheckable(true);
        lin->setChecked(!specularDataItem()->isLog());

        QAction* log = new QAction("Logarithmic");
        connect(log, &QAction::triggered, [=]() { specularDataItem()->setLog(true); });
        log->setCheckable(true);
        log->setChecked(specularDataItem()->isLog());

        auto ag = new QActionGroup(&menu);
        ag->addAction(lin);
        ag->addAction(log);

        menu.addAction(lin);
        menu.addAction(log);

        menu.exec(event->globalPos());
    }
}

SpecularDataItem* SpecularDataImportWidget::specularDataItem()
{
    return DataItemUtils::specularDataItem(currentItem());
}

const RealDataItem* SpecularDataImportWidget::realDataItem() const
{
    return dynamic_cast<const RealDataItem*>(currentItem());
}

RealDataItem* SpecularDataImportWidget::realDataItem()
{
    return dynamic_cast<RealDataItem*>(currentItem());
}

void SpecularDataImportWidget::fillLoaderCombo()
{
    QSignalBlocker b(m_ui->formatSelectionComboBox);
    m_ui->formatSelectionComboBox->clear();
    for (auto loader : DataLoaders1D::instance().recentlyUsedLoaders()) {
        m_ui->formatSelectionComboBox->addItem(loader->name());
    }
    for (auto loader : DataLoaders1D::instance().loaders()) {
        m_ui->formatSelectionComboBox->addItem(loader->name());
    }

    // e.g. legacy loader is not present in the combo by default. Add it here so it can be selected
    if (m_loader != nullptr)
        if (m_ui->formatSelectionComboBox->findText(m_loader->name()) < 0)
            m_ui->formatSelectionComboBox->addItem(m_loader->name());
}

void SpecularDataImportWidget::updatePropertiesEdits()
{
    for (auto child : m_ui->propertiesWidget->children()) {
        delete child;
    }

    if (m_ui->propertiesWidget->layout())
        delete m_ui->propertiesWidget->layout();

    if (m_loader) {
        m_loader->populateImportSettingsWidget(m_ui->propertiesWidget);
    }

    const bool hasChildren = !m_ui->propertiesWidget->children().empty();

    m_ui->propertiesWidget->setVisible(hasChildren);
}

AbstractDataLoader* SpecularDataImportWidget::selectedLoader()
{
    const QString name = m_ui->formatSelectionComboBox->currentText();

    for (auto loader : DataLoaders1D::instance().loaders())
        if (name == loader->name())
            return loader;

    return nullptr;
}

void SpecularDataImportWidget::onFormatSelectionChanged()
{
    if (m_loader && m_loader->fileContent().isEmpty()) {

        QSignalBlocker b(m_ui->formatSelectionComboBox);
        m_ui->formatSelectionComboBox->setCurrentText(m_loader->name());

        QMessageBox::information(MainWindow::instance(), "Information",
                                 "Changing the loader is not possible because the original file "
                                 "contents are not available any more.");

        return;
    }

    if (m_loader)
        m_loader->disconnect(this);

    m_loader = dynamic_cast<AbstractDataLoader1D*>(selectedLoader()->clone());
    m_loader->initWithDefaultImportSettings();
    m_loader->setFileContents(realDataItem()->dataLoader()->fileContent());
    realDataItem()->setDataLoader(m_loader);
    m_loader->setRealDataItem(realDataItem());
    QApplication::setOverrideCursor(Qt::WaitCursor);
    m_loader->guessSettings();
    m_loader->processContents();
    QApplication::restoreOverrideCursor();

    updatePropertiesEdits();
    updatePreview();
    connect(m_loader, &AbstractDataLoader::importSettingsChanged, this,
            &SpecularDataImportWidget::onPropertiesChanged);
}

void SpecularDataImportWidget::updatePreview()
{
    QApplication::setOverrideCursor(Qt::WaitCursor);

    if (m_loader) {
        auto oldModel = m_ui->dataResultView->selectionModel(); // sic!! according to Qt docu
                                                                // of QAbstractItemView::setModel
        auto resultModel = m_loader->createResultModel();
        if (resultModel != nullptr) {

            const auto originalSections = resultModel->sectionsOfColumnType(
                AbstractDataLoaderResultModel::ColumnType::fileContent);

            const auto rawSections =
                resultModel->sectionsOfColumnType(AbstractDataLoaderResultModel::ColumnType::raw);

            const auto processedSections = resultModel->sectionsOfColumnType(
                AbstractDataLoaderResultModel::ColumnType::processed);

            QSignalBlocker b1(m_ui->originalRowCheckBox);
            QSignalBlocker b2(m_ui->rawDataCheckBox);
            QSignalBlocker b3(m_ui->calculatedDataCheckBox);

            if (originalSections.isEmpty()) {
                m_ui->originalRowCheckBox->setChecked(false);
                m_ui->originalRowCheckBox->setEnabled(false);
            } else
                m_ui->originalRowCheckBox->setEnabled(true);

            if (rawSections.isEmpty()) {
                m_ui->rawDataCheckBox->setChecked(false);
                m_ui->rawDataCheckBox->setEnabled(false);
            } else
                m_ui->rawDataCheckBox->setEnabled(true);

            if (processedSections.isEmpty()) {
                m_ui->calculatedDataCheckBox->setChecked(false);
                m_ui->calculatedDataCheckBox->setEnabled(false);
            } else
                m_ui->calculatedDataCheckBox->setEnabled(true);

            m_ui->dataResultView->setModel(resultModel);
            auto horHeader = m_ui->dataResultView->horizontalHeader();

            for (int section : originalSections)
                horHeader->setSectionHidden(section, !m_ui->originalRowCheckBox->isChecked());

            for (int section : rawSections)
                horHeader->setSectionHidden(section, !m_ui->rawDataCheckBox->isChecked());

            for (int section : processedSections)
                horHeader->setSectionHidden(section, !m_ui->calculatedDataCheckBox->isChecked());

            // if the result model has a line column, then do not show the vertical header view
            const bool hasLinesColumn =
                !resultModel->sectionsOfColumnType(AbstractDataLoaderResultModel::ColumnType::line)
                     .isEmpty();
            m_ui->dataResultView->verticalHeader()->setHidden(hasLinesColumn);

            m_ui->dataResultView->resizeColumnsToContents();
        } else
            m_ui->dataResultView->setModel(nullptr);

        delete oldModel;
    }

    if (m_loader && m_loader->numErrors() > 0) {
        m_ui->warningsIcon->show();
        m_ui->warningsLabel->show();
        m_ui->warningsListWidget->show();
        m_ui->warningsListWidget->clear();

        auto warnings = m_loader->lineUnrelatedErrors();
        const int nLineRelatedWarnings = m_loader->numErrors() - warnings.size();

        if (nLineRelatedWarnings == 1)
            warnings << "1 line related warning. Please check the data tab on the right for more "
                        "information.";
        else if (nLineRelatedWarnings > 1)
            warnings
                << QString(
                       "%1 line related warnings. Please check the data tab on the right for more "
                       "information.")
                       .arg(nLineRelatedWarnings);

        if (warnings.size() > 1)
            for (auto& w : warnings)
                w.prepend("* ");

        for (auto& w : warnings)
            new QListWidgetItem(w, m_ui->warningsListWidget);

    } else {
        m_ui->warningsIcon->hide();
        m_ui->warningsLabel->hide();
        m_ui->warningsListWidget->hide();
    }

    QApplication::restoreOverrideCursor();
}

void SpecularDataImportWidget::onCreateNewFormatButton()
{
    bool ok;
    QString name = QInputDialog::getText(
        this, "New format", "Please enter a name for the new format", QLineEdit::Normal, "", &ok);
    if (!ok || name.isEmpty())
        return;

    DataLoaders1D::instance().cloneAsUserDefinedLoader(m_loader, name);

    fillLoaderCombo();
    m_ui->formatSelectionComboBox->setCurrentText(name);
    onFormatSelectionChanged();
}

void SpecularDataImportWidget::onPropertiesChanged()
{
    m_loader->applyImportSettings(); // #baimport: may be duplicate
    QApplication::setOverrideCursor(Qt::WaitCursor);
    m_loader->processContents();
    QApplication::restoreOverrideCursor();

    // If there is a linked instrument, any change in import settings can break the compatibility.
    // Therefore check the compatibility and break the link if necessary
    if (realDataItem()->linkedInstrument() != nullptr) {
        if (!realDataItem()->linkedInstrument()->alignedWith(realDataItem()))
            realDataItem()->clearInstrumentId();
    }

    updatePreview();
}

QString SpecularDataImportWidget::currentFileName() const
{
    return realDataItem()->nativeFileName();
}