//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Instrument/InstrumentLibraryEditor.cpp
//! @brief     Implements class InstrumentLibraryEditor
//!
//! @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/Instrument/InstrumentLibraryEditor.h"
#include "GUI/Application/ApplicationSettings.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/Model/Device/InstrumentLibrary.h"
#include "GUI/Support/Util/Style.h"
#include "GUI/View/Instrument/DepthprobeInstrumentEditor.h"
#include "GUI/View/Instrument/GISASInstrumentEditor.h"
#include "GUI/View/Instrument/OffspecInstrumentEditor.h"
#include "GUI/View/Instrument/SpecularInstrumentEditor.h"
#include "GUI/View/Item/ItemViewOverlayButtons.h"
#include "GUI/View/Tool/GroupBoxCollapser.h"
#include "GUI/View/Tool/ItemDelegateForHTML.h"
#include "ui_InstrumentLibraryEditor.h"
#include <QAction>
#include <QFormLayout>
#include <QGroupBox>
#include <QInputDialog>
#include <QPushButton>
#include <QTextEdit>

InstrumentLibraryEditor::InstrumentLibraryEditor(QWidget* parent,
                                                 InstrumentLibrary* instrumentLibrary)
    : QDialog(parent)
    , m_instrumentLibrary(instrumentLibrary)
    , m_ui(new Ui::InstrumentLibraryEditor)
    , m_treeModel(new TreeModel(this, instrumentLibrary->instrumentModel()))
    , m_chosenItem(nullptr)
{
    m_ui->setupUi(this);

    setWindowIcon(QIcon(":/images/library.svg"));
    setWindowFlag(Qt::WindowContextHelpButtonHint, false);

    m_treeModel->enableEmptyHeadlines(false);

    m_ui->treeView->setItemsExpandable(false);
    m_ui->treeView->setRootIsDecorated(false);
    m_ui->treeView->setHeaderHidden(true);
    m_ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
    m_ui->treeView->setModel(m_treeModel);
    m_ui->treeView->expandAll();
    m_ui->treeView->setVerticalScrollMode(QTreeView::ScrollPerPixel);
    m_ui->treeView->setIndentation(0);
    m_ui->treeView->setItemDelegate(new ItemDelegateForHTML(this));
    m_ui->treeView->setIconSize(QSize(128, 128));

    connect(m_treeModel, &QAbstractItemModel::modelReset,
            [this]() { m_ui->treeView->expandAll(); });

    // ensure a current item when widget is shown
    // setCurrentItem(m_treeModel->topMostItem());
    GUI::Style::setResizable(this);
    appSettings->loadWindowSizeAndPos(this);
}

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

void InstrumentLibraryEditor::setGisasEnabled(bool b)
{
    m_treeModel->setTypeEnabled(InstrumentsTreeModel::Gisas, b);
}

void InstrumentLibraryEditor::setOffspecEnabled(bool b)
{
    m_treeModel->setTypeEnabled(InstrumentsTreeModel::Offspec, b);
}

void InstrumentLibraryEditor::setSpecularEnabled(bool b)
{
    m_treeModel->setTypeEnabled(InstrumentsTreeModel::Specular, b);
}

void InstrumentLibraryEditor::setDepthprobeEnabled(bool b)
{
    m_treeModel->setTypeEnabled(InstrumentsTreeModel::Depthprobe, b);
}

InstrumentItem* InstrumentLibraryEditor::execChoose()
{
    setWindowTitle("Instrument Library - Choose instrument");

    ItemViewOverlayButtons::install(
        m_ui->treeView, [this](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
    m_ui->treeView->setItemDelegate(new ItemDelegateForHTML(this));

    connect(m_ui->treeView, &QTreeView::doubleClicked, this,
            &InstrumentLibraryEditor::onItemDoubleClickedForChoose);
    connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
            &InstrumentLibraryEditor::onCurrentChangedForChoose);
    onCurrentChangedForChoose();

    if (exec() == QDialog::Accepted)
        return m_chosenItem;

    return nullptr;
}

void InstrumentLibraryEditor::execAdd(const InstrumentItem& instrumentToAdd)
{
    const QString& newName = m_instrumentLibrary->suggestName(instrumentToAdd.instrumentName());
    auto* addedInstrument = m_instrumentLibrary->addItemCopy(newName, instrumentToAdd);

    setWindowTitle("Instrument Library - Add instrument");

    m_treeModel->setNewInstrument(addedInstrument);
    m_treeModel->setTypeEnabled(TreeModel::instrumentType(addedInstrument), true);

    ItemViewOverlayButtons::install(
        m_ui->treeView, [this](const QModelIndex& i, bool h) { return getOverlayActions(i, h); });
    m_ui->treeView->setItemDelegate(new ItemDelegateForHTML(this));
    connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
            &InstrumentLibraryEditor::createWidgetsForCurrentInstrument);

    m_ui->buttonBox->addButton(QDialogButtonBox::Close);
    m_ui->buttonBox->button(QDialogButtonBox::Ok)->hide();
    m_ui->buttonBox->button(QDialogButtonBox::Cancel)->hide();

    QModelIndex index = m_treeModel->indexForItem(addedInstrument);
    m_ui->treeView->expandAll();
    m_ui->treeView->setCurrentIndex(index);
    m_ui->treeView->scrollTo(index, QAbstractItemView::PositionAtTop);
    createWidgetsForCurrentInstrument();
    exec();
}

void InstrumentLibraryEditor::onItemDoubleClickedForChoose(const QModelIndex& index)
{
    m_chosenItem = m_treeModel->itemForIndex(index);
    if (m_chosenItem != nullptr)
        accept();
}

void InstrumentLibraryEditor::onCurrentChangedForChoose()
{
    m_chosenItem = m_treeModel->itemForIndex(m_ui->treeView->currentIndex());
    m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_chosenItem != nullptr);
    createWidgetsForCurrentInstrument();
}

QList<QAction*> InstrumentLibraryEditor::getOverlayActions(const QModelIndex& index, bool asHover)
{
    if (m_treeModel->isHeadline(index))
        return {};

    // -- index belongs to item
    if (!asHover)
        return {};

    auto* item = m_treeModel->itemForIndex(index);
    if (item == nullptr)
        return {};

    auto* removeAction = new QAction(this);
    removeAction->setText("Remove");
    removeAction->setIcon(QIcon(":/images/delete.svg"));
    removeAction->setIconText("Remove");
    removeAction->setToolTip("Remove this instrument");
    connect(removeAction, &QAction::triggered, [this, item]() { m_treeModel->removeItem(item); });

    return {removeAction};
}

void InstrumentLibraryEditor::createWidgetsForCurrentInstrument()
{
    auto* currentInstrument = m_treeModel->itemForIndex(m_ui->treeView->currentIndex());
    if (!currentInstrument) {
        m_ui->scrollArea->setWidget(new QWidget(m_ui->scrollArea)); // blank widget
        return;
    }

    QWidget* w = new QWidget(m_ui->scrollArea);
    auto* layout = new QVBoxLayout(w);

    auto* g = new QGroupBox(m_ui->scrollArea);
    g->setTitle(QString("Information (%1 instrument)").arg(currentInstrument->instrumentType()));

    auto* formLayout = new QFormLayout(g);
    formLayout->setContentsMargins(17, 17, 17, 17);
    formLayout->setSpacing(8);
    layout->addWidget(g);

    auto* nameEdit = new QLineEdit(g);
    formLayout->addRow("Name:", nameEdit);
    nameEdit->setText(currentInstrument->instrumentName());
    connect(nameEdit, &QLineEdit::textEdited, this,
            &InstrumentLibraryEditor::onInstrumentNameEdited);

    auto* descriptionEdit = new QTextEdit(g);
    descriptionEdit->setMinimumWidth(300);
    descriptionEdit->setMaximumHeight(100);
    descriptionEdit->setAcceptRichText(false);
    descriptionEdit->setTabChangesFocus(true);
    descriptionEdit->setPlainText(currentInstrument->description());
    formLayout->addRow("Description:", descriptionEdit);
    connect(descriptionEdit, &QTextEdit::textChanged, [this, descriptionEdit]() {
        onInstrumentDescriptionEdited(descriptionEdit->toPlainText());
    });

    GroupBoxCollapser::installIntoGroupBox(g);

    auto* ec = m_instrumentLibrary->editController();
    if (auto* sp = dynamic_cast<SpecularInstrumentItem*>(currentInstrument)) {
        auto* editor = new SpecularInstrumentEditor(m_ui->scrollArea, sp, ec);
        connect(editor, &SpecularInstrumentEditor::dataChanged, this,
                &InstrumentLibraryEditor::onInstrumentChangedByEditor);
        layout->addWidget(editor);
    } else if (auto* os = dynamic_cast<OffspecInstrumentItem*>(currentInstrument)) {
        auto* editor = new OffspecInstrumentEditor(m_ui->scrollArea, os, ec);
        connect(editor, &OffspecInstrumentEditor::dataChanged, this,
                &InstrumentLibraryEditor::onInstrumentChangedByEditor);
        layout->addWidget(editor);
    } else if (auto* gisas = dynamic_cast<GISASInstrumentItem*>(currentInstrument)) {
        auto* editor = new GISASInstrumentEditor(m_ui->scrollArea, gisas);
        connect(editor, &GISASInstrumentEditor::dataChanged, this,
                &InstrumentLibraryEditor::onInstrumentChangedByEditor);
        layout->addWidget(editor);
    } else if (auto* dp = dynamic_cast<DepthprobeInstrumentItem*>(currentInstrument)) {
        auto* editor = new DepthprobeInstrumentEditor(m_ui->scrollArea, dp, ec);
        connect(editor, &DepthprobeInstrumentEditor::dataChanged, this,
                &InstrumentLibraryEditor::onInstrumentChangedByEditor);
        layout->addWidget(editor);
    } else
        ASSERT(false);

    m_ui->scrollArea->setWidget(w);
}

void InstrumentLibraryEditor::onInstrumentNameEdited(const QString& newName)
{
    QModelIndex index = m_ui->treeView->currentIndex();
    m_treeModel->setData(index, newName, Qt::EditRole);
}

void InstrumentLibraryEditor::onInstrumentDescriptionEdited(const QString& t)
{
    QModelIndex index = m_ui->treeView->currentIndex();
    m_treeModel->setData(index, t, Qt::ToolTipRole);
}

void InstrumentLibraryEditor::onInstrumentChangedByEditor()
{
    // uses 'MultiInstrumentNotifier::instrumentChanged' signal to set
    // 'InstrumentLibrary:m_modified' flag to true ==> save library on close.
    auto* currentInstrument = m_treeModel->itemForIndex(m_ui->treeView->currentIndex());
    m_instrumentLibrary->editController()->notifyInstrumentChanged(currentInstrument);
}

/*********************************************************************************************/

InstrumentLibraryEditor::TreeModel::TreeModel(QObject* parent, InstrumentModel* model)
    : InstrumentsTreeModel(parent, model)
    , m_newInstrument(nullptr)
{
}

void InstrumentLibraryEditor::TreeModel::setNewInstrument(InstrumentItem* addedInstrument)
{
    m_newInstrument = addedInstrument;
}

QVariant InstrumentLibraryEditor::TreeModel::data(const QModelIndex& index, int role) const
{
    if (isHeadline(index))
        return InstrumentsTreeModel::data(index, role);

    auto* const item = itemForIndex(index);

    if (role == Qt::DisplayRole) {
        auto descr = item->description();
        if (!descr.isEmpty()) {
            descr.prepend("<br><br>");
            // max 4 lines
            while (descr.count("\n") > 3) {
                descr.truncate(descr.lastIndexOf("\n"));
                descr += " [...]";
            }
            descr.replace("\n", "<br>");
        }
        return "<b>" + item->instrumentName() + "</b>" + descr;
    }

    if (role == Qt::DecorationRole && (item == m_newInstrument)) {
        if (role == Qt::DecorationRole)
            switch (instrumentType(item)) {
            case Gisas:
                return QIcon(":/images/gisas_instrument_new.svg");
            case Offspec:
                return QIcon(":/images/offspec_instrument_new.svg");
            case Specular:
                return QIcon(":/images/specular_instrument_new.svg");
            case Depthprobe:
                return QIcon(":/images/depth_instrument_new.svg");
            default:
                break;
            }
    }
    return InstrumentsTreeModel::data(index, role);
}
