//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/MaterialEditor/MaterialEditorDialog.cpp
//! @brief     Implements class MaterialEditorDialog
//!
//! @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/MaterialEditor/MaterialEditorDialog.h"
#include "GUI/Application/ApplicationSettings.h"
#include "GUI/Model/Sample/ItemWithMaterial.h"
#include "GUI/Model/Sample/MaterialItem.h"
#include "GUI/Model/Sample/MaterialModel.h"
#include "GUI/Model/Sample/SampleItem.h"
#include "GUI/Support/Util/Style.h"
#include "GUI/View/MaterialEditor/MaterialEditorModel.h"
#include "GUI/View/Numeric/NumberUtil.h"
#include "GUI/View/Tool/mainwindow_constants.h"
#include "GUI/View/Widget/StyledToolbar.h"
#include "ui_MaterialEditorDialog.h"
#include <QAction>
#include <QColorDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QSettings>
#include <QVBoxLayout>

MaterialEditorDialog::MaterialEditorDialog(SampleItem* sample, QWidget* parent)
    : QDialog(parent)
    , m_ui(new Ui::MaterialEditorDialog)
    , m_sample(sample)
{
    m_tmpMaterialModel.initFrom(m_sample->materialModel());

    m_model = new MaterialEditorModel(&m_tmpMaterialModel);

    m_ui->setupUi(this);

    using GUI::View::NumberUtil::configScientificDoubleEdit;
    configScientificDoubleEdit(m_ui->deltaEdit, RealLimits::limitless());
    configScientificDoubleEdit(m_ui->betaEdit, RealLimits::limitless());
    configScientificDoubleEdit(m_ui->realEdit, RealLimits::limitless());
    configScientificDoubleEdit(m_ui->imaginaryEdit, RealLimits::limitless());

    using GUI::View::NumberUtil::configSpinbox;
    configSpinbox(m_ui->xSpinBox, 3, RealLimits::limitless());
    configSpinbox(m_ui->ySpinBox, 3, RealLimits::limitless());
    configSpinbox(m_ui->zSpinBox, 3, RealLimits::limitless());

    // Setting z-component is temporary disabled (see issue #654)
    // When implemented, rm disabling
    m_ui->zSpinBox->setDisabled(true);

    auto* addRefractiveMaterialAction = new QAction("Add material (refractive index)", parent);
    addRefractiveMaterialAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    addRefractiveMaterialAction->setToolTip("Add new material");
    connect(addRefractiveMaterialAction, &QAction::triggered, this,
            &MaterialEditorDialog::addRefractiveMaterial);

    auto* addSldMaterialAction = new QAction("Add material (SLD)", parent);
    addSldMaterialAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    addSldMaterialAction->setToolTip("Add new material");
    connect(addSldMaterialAction, &QAction::triggered, this, &MaterialEditorDialog::addSldMaterial);

    m_cloneMaterialAction = new QAction("Copy", parent);
    m_cloneMaterialAction->setIcon(QIcon(":/images/content-copy.svg"));
    m_cloneMaterialAction->setToolTip("Copy selected material");
    connect(m_cloneMaterialAction, &QAction::triggered, this,
            &MaterialEditorDialog::cloneCurrentMaterial);

    m_removeMaterialAction = new QAction("Remove", parent);
    m_removeMaterialAction->setIcon(QIcon(":/images/delete.svg"));
    m_removeMaterialAction->setToolTip("Remove selected material");
    connect(m_removeMaterialAction, &QAction::triggered, this,
            &MaterialEditorDialog::removeCurrentMaterial);

    m_ui->treeView->addAction(addRefractiveMaterialAction);
    m_ui->treeView->addAction(addSldMaterialAction);
    m_ui->treeView->addAction(m_cloneMaterialAction);
    auto* separator = new QAction(this);
    separator->setSeparator(true);
    m_ui->treeView->addAction(separator);
    m_ui->treeView->addAction(m_removeMaterialAction);
    m_ui->treeView->setModel(m_model);
    m_ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);

    auto* toolbar = new StyledToolbar(this);
    toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    toolbar->addAction(addRefractiveMaterialAction);
    toolbar->addAction(addSldMaterialAction);
    toolbar->addAction(m_cloneMaterialAction);
    toolbar->addAction(m_removeMaterialAction);
    m_ui->mainLayout->insertWidget(0, toolbar);

    GUI::Style::setResizable(this);
    appSettings->loadWindowSizeAndPos(this);

    connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
            &MaterialEditorDialog::fill);

    connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
            &MaterialEditorDialog::updateActionEnabling);

    connect(m_ui->selectColorButton, &QPushButton::clicked, this,
            &MaterialEditorDialog::onSelectColor);

    connect(m_ui->nameEdit, &QLineEdit::textEdited,
            [&](const QString& t) { m_model->setMaterialItemName(currentIndex(), t); });

    connect(m_ui->xSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
            [&](double value) { m_model->setX(currentIndex(), value); });

    connect(m_ui->ySpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
            [&](double value) { m_model->setY(currentIndex(), value); });

    connect(m_ui->zSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged),
            [&](double value) { m_model->setZ(currentIndex(), value); });

    connect(m_ui->deltaEdit, &QLineEdit::editingFinished,
            [&]() { m_model->setDelta(currentIndex(), m_ui->deltaEdit->text().toDouble()); });

    connect(m_ui->betaEdit, &QLineEdit::editingFinished,
            [&]() { m_model->setBeta(currentIndex(), m_ui->betaEdit->text().toDouble()); });

    connect(m_ui->realEdit, &QLineEdit::editingFinished,
            [&]() { m_model->setRe(currentIndex(), m_ui->realEdit->text().toDouble()); });

    connect(m_ui->imaginaryEdit, &QLineEdit::editingFinished,
            [&]() { m_model->setIm(currentIndex(), m_ui->imaginaryEdit->text().toDouble()); });


    if (m_model->rowCount() > 0)
        m_ui->treeView->setCurrentIndex(m_model->first());
    else {
        m_ui->sldGroupBox->hide();
        m_ui->propertiesWidget->setEnabled(false);
    }
}

MaterialEditorDialog::~MaterialEditorDialog()
{
    appSettings->saveWindowSizeAndPos(this);
}

void MaterialEditorDialog::accept()
{
    m_sample->materialModel().initFrom(m_tmpMaterialModel);
    QDialog::accept();
}

QString MaterialEditorDialog::chooseMaterial(QWidget* parent, SampleItem* sample,
                                             const QString& identifierOfPreviousMaterial)
{
    MaterialEditorDialog dialog(sample, parent);
    dialog.setCurrentMaterial(identifierOfPreviousMaterial);
    if (dialog.exec() == QDialog::Accepted)
        if (MaterialItem* material = dialog.currentMaterialItem())
            return material->identifier();

    return QString();
}

void MaterialEditorDialog::editMaterials(QWidget* parent, SampleItem* sample)
{
    MaterialEditorDialog dialog(sample, parent);
    dialog.exec();
}

void MaterialEditorDialog::addRefractiveMaterial()
{
    setCurrentMaterial(m_model->addRefractiveMaterialItem("unnamed", 0.0, 0.0));
}

void MaterialEditorDialog::addSldMaterial()
{
    setCurrentMaterial(m_model->addSLDMaterialItem("unnamed", 0.0, 0.0));
}

void MaterialEditorDialog::cloneCurrentMaterial()
{
    if (currentIndex().isValid())
        setCurrentMaterial(m_model->cloneMaterialItem(currentIndex()));
}

void MaterialEditorDialog::removeCurrentMaterial()
{
    MaterialItem* material = currentMaterialItem();
    if (material == nullptr)
        return;

    if (identifiersOfUsedMaterials().contains(material->identifier())) {
        QMessageBox::warning(this, "Remove material",
                             "This material cannot be removed - it is used in the current sample.");
        return;
    }

    m_model->removeMaterial(currentIndex());
}

void MaterialEditorDialog::onSelectColor()
{
    if (auto newColor = QColorDialog::getColor(currentMaterialItem()->color()); newColor.isValid())
        m_model->setColor(currentIndex(), newColor);

    fill();
}

void MaterialEditorDialog::updateActionEnabling()
{
    m_removeMaterialAction->setEnabled(currentIndex().isValid());
    m_cloneMaterialAction->setEnabled(currentIndex().isValid());
}

MaterialItem* MaterialEditorDialog::currentMaterialItem()
{
    return currentIndex().isValid() ? m_model->materialItemFromIndex(currentIndex()) : nullptr;
}

void MaterialEditorDialog::fill()
{
    auto* materialItem = currentMaterialItem();

    m_ui->propertiesWidget->setEnabled(materialItem != nullptr);
    if (materialItem == nullptr) {
        m_ui->refractiveGroupBox->show();
        m_ui->sldGroupBox->hide();
        for (auto* lineEdit : m_ui->propertiesWidget->findChildren<QLineEdit*>())
            lineEdit->clear();
        for (auto* spinBox : m_ui->propertiesWidget->findChildren<QDoubleSpinBox*>())
            spinBox->clear();
        return;
    }

    m_ui->refractiveGroupBox->setVisible(materialItem->hasRefractiveIndex());
    m_ui->sldGroupBox->setVisible(!materialItem->hasRefractiveIndex());

    m_ui->nameEdit->setText(materialItem->matItemName());
    m_ui->colorInfo->setText(QString("[%1, %2, %3] (%4)")
                                 .arg(materialItem->color().red())
                                 .arg(materialItem->color().green())
                                 .arg(materialItem->color().blue())
                                 .arg(materialItem->color().alpha()));
    QPixmap pixmap(m_ui->selectColorButton->iconSize());
    pixmap.fill(materialItem->color());
    m_ui->selectColorButton->setIcon(pixmap);

    if (materialItem->hasRefractiveIndex()) {
        m_ui->deltaEdit->setText(QString::number(materialItem->delta().value(), 'g'));
        m_ui->betaEdit->setText(QString::number(materialItem->beta().value(), 'g'));
    } else {
        m_ui->realEdit->setText(QString::number(materialItem->sldRe().value(), 'g'));
        m_ui->imaginaryEdit->setText(QString::number(materialItem->sldIm().value(), 'g'));
    }

    m_ui->xSpinBox->setValue(materialItem->magnetization().r3().x());
    m_ui->ySpinBox->setValue(materialItem->magnetization().r3().y());
    m_ui->zSpinBox->setValue(materialItem->magnetization().r3().z());
}

void MaterialEditorDialog::setCurrentMaterial(const MaterialItem* m)
{
    m_ui->treeView->setCurrentIndex(m_model->indexFromMaterial(m));
}

void MaterialEditorDialog::setCurrentMaterial(const QString& identifier)
{
    m_ui->treeView->setCurrentIndex(m_model->indexFromMaterial(identifier));
}

QModelIndex MaterialEditorDialog::currentIndex() const
{
    return m_ui->treeView->currentIndex();
}

QStringList MaterialEditorDialog::identifiersOfUsedMaterials() const
{
    QStringList result;
    for (auto* p : m_sample->itemsWithMaterial())
        result << p->materialIdentifier();
    return result;
}
