//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Instrument/InstrumentListView.cpp
//! @brief     Implements class InstrumentListView
//!
//! @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/InstrumentListView.h"
#include "GUI/Model/Project/ProjectDocument.h"
#include "GUI/View/Instrument/InstrumentLibraryEditor.h"
#include "GUI/View/Instrument/InstrumentListModel.h"
#include "GUI/View/Tool/Globals.h"
#include <QAction>
#include <QMessageBox>
#include <QVBoxLayout>

InstrumentListView::InstrumentListView(ProjectDocument* document, QWidget* parent,
                                       Qt::WindowFlags f)
    : QWidget(parent, f)
    , m_document(document)
{
    m_instrumentLibrary.load();

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);

    auto* layout = new QVBoxLayout(this);
    layout->setContentsMargins(10, 10, 10, 10);

    m_listView = new QListView(this);
    m_listView->setViewMode(QListView::IconMode);
    m_listView->setIconSize(QSize(96, 84));
    m_listView->setMovement(QListView::Static);
    m_listView->setMaximumWidth(200);
    m_listView->setSpacing(12);
    m_listView->setSelectionMode(QAbstractItemView::SingleSelection);

    m_listView->setObjectName("listView");
    m_listView->setStyleSheet(
        QString::fromUtf8("QListView#listView\n"
                          "{\n"
                          "   selection-background-color : rgb(98,100,105); \n"
                          "   selection-color: rgb(255,255,255);\n"
                          "   border: 1px solid rgb(98,100,105);\n"
                          "}\n"));
    layout->addWidget(m_listView);

    m_model = new InstrumentListModel(this, m_document->multiNotifier());
    m_listView->setModel(m_model);

    m_newGisasAction = new QAction("New GISAS", this);
    m_newGisasAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    m_newGisasAction->setToolTip("Add new GISAS instrument with default settings");
    connect(m_newGisasAction, &QAction::triggered, this, &InstrumentListView::onNewGisas);
    addAction(m_newGisasAction);

    m_newOffspecAction = new QAction("New off-specular", this);
    m_newOffspecAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    m_newOffspecAction->setToolTip("Add new off-specular instrument with default settings");
    connect(m_newOffspecAction, &QAction::triggered, this, &InstrumentListView::onNewOffspec);
    addAction(m_newOffspecAction);

    m_newSpecularAction = new QAction("New specular", this);
    m_newSpecularAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    m_newSpecularAction->setToolTip("Add new specular instrument with default settings");
    connect(m_newSpecularAction, &QAction::triggered, this, &InstrumentListView::onNewSpecular);
    addAction(m_newSpecularAction);

    m_newDepthprobeAction = new QAction("New depth probe", this);
    m_newDepthprobeAction->setIcon(QIcon(":/images/shape-square-plus.svg"));
    m_newDepthprobeAction->setToolTip("Add new depth probe instrument with default settings");
    connect(m_newDepthprobeAction, &QAction::triggered, this, &InstrumentListView::onNewDepthprobe);
    addAction(m_newDepthprobeAction);

    m_separatorAction1 = new QAction(this);
    m_separatorAction1->setSeparator(true);
    addAction(m_separatorAction1);

    m_removeAction = new QAction("Remove", this);
    m_removeAction->setIcon(QIcon(":/images/delete.svg"));
    m_removeAction->setToolTip("Remove selected instrument");
    connect(m_removeAction, &QAction::triggered, this, &InstrumentListView::onRemove);
    addAction(m_removeAction);

    m_copyAction = new QAction("Copy", this);
    m_copyAction->setIcon(QIcon(":/images/content-copy.svg"));
    m_copyAction->setToolTip("Make a copy of the selected instrument");
    connect(m_copyAction, &QAction::triggered, this, &InstrumentListView::onCopy);
    addAction(m_copyAction);

    m_separatorAction2 = new QAction(this);
    m_separatorAction2->setSeparator(true);
    addAction(m_separatorAction2);

    m_storeInLibraryAction = new QAction("Store in library", this);
    m_storeInLibraryAction->setIcon(QIcon(":/images/library.svg"));
    m_storeInLibraryAction->setToolTip("Store instrument in library");
    connect(m_storeInLibraryAction, &QAction::triggered, this,
            &InstrumentListView::onStoreInLibrary);
    addAction(m_storeInLibraryAction);

    m_loadFromLibraryAction = new QAction("Choose from library", this);
    m_loadFromLibraryAction->setIcon(QIcon(":/images/library.svg"));
    m_loadFromLibraryAction->setToolTip("Load an instrument from the instrument library");
    connect(m_loadFromLibraryAction, &QAction::triggered, this,
            &InstrumentListView::onLoadFromLibrary);
    addAction(m_loadFromLibraryAction);

    setContextMenuPolicy(Qt::ActionsContextMenu);

    connect(m_document, &ProjectDocument::functionalitiesChanged, this,
            &InstrumentListView::updateFunctionalityNarrowing);

    updateFunctionalityNarrowing();

    updateActions();
    restoreSelection();

    connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
            &InstrumentListView::onItemSelectionChanged);
}

InstrumentListView::~InstrumentListView()
{
    m_instrumentLibrary.saveIfModified();
}

QSize InstrumentListView::sizeHint() const
{
    return QSize(170, 400);
}

QSize InstrumentListView::minimumSizeHint() const
{
    return QSize(96, 200);
}

QList<QAction*> InstrumentListView::toolbarActions() const
{
    return {m_newGisasAction,       m_newOffspecAction, m_newSpecularAction,
            m_newDepthprobeAction,  m_separatorAction1, m_removeAction,
            m_copyAction,           m_separatorAction2, m_storeInLibraryAction,
            m_loadFromLibraryAction};
}

InstrumentItem* InstrumentListView::currentInstrumentItem() const
{
    const QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
    if (!indexes.empty())
        return m_model->instrumentItemForIndex(indexes.front());
    return nullptr;
}

void InstrumentListView::onItemSelectionChanged()
{
    updateActions();

    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
    if (!indexes.empty()) {
        QModelIndex current = indexes.front();
        m_document->instrumentModel()->setSelectedIndex(current.row());
        emit instrumentSelected(m_model->instrumentItemForIndex(current));
    } else
        emit instrumentSelected(nullptr);
}

void InstrumentListView::onNewGisas()
{
    QModelIndex idx = m_model->addNewGISASInstrument();
    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
}

void InstrumentListView::onNewOffspec()
{
    QModelIndex idx = m_model->addNewOffspecInstrument();
    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
}

void InstrumentListView::onNewSpecular()
{
    QModelIndex idx = m_model->addNewSpecularInstrument();
    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
}

void InstrumentListView::onNewDepthprobe()
{
    QModelIndex idx = m_model->addNewDepthprobeInstrument();
    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
}

//! Removes currently selected instrument.
void InstrumentListView::onRemove()
{
    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
    if (!indexes.empty()) {
        m_model->removeInstrument(indexes.front());

        ensureItemSelected();
    }
}

//! Makes a copy of the currently selected instrument.
void InstrumentListView::onCopy()
{
    QModelIndexList indexes = m_listView->selectionModel()->selectedIndexes();
    if (!indexes.empty()) {
        QModelIndex idx = m_model->copyInstrument(indexes.front());
        m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
    }
}

void InstrumentListView::onStoreInLibrary()
{
    if (!m_listView->selectionModel()->hasSelection())
        return;

    QModelIndex idx = m_listView->selectionModel()->selectedIndexes().front();
    InstrumentItem* instrument = m_model->instrumentItemForIndex(idx);

    InstrumentLibraryEditor dlg(GUI::Global::mainWindow, &m_instrumentLibrary);
    dlg.setGisasEnabled(m_document->functionalities().testFlag(ProjectDocument::Gisas));
    dlg.setOffspecEnabled(m_document->functionalities().testFlag(ProjectDocument::Offspec));
    dlg.setSpecularEnabled(m_document->functionalities().testFlag(ProjectDocument::Specular));
    dlg.setDepthprobeEnabled(m_document->functionalities().testFlag(ProjectDocument::Depthprobe));
    dlg.execAdd(*instrument);
}

void InstrumentListView::onLoadFromLibrary()
{
    if (m_instrumentLibrary.isEmpty()) {
        QMessageBox::information(GUI::Global::mainWindow, "Select from library",
                                 "The library does not contain instruments so far.");
        return;
    }

    InstrumentLibraryEditor dlg(GUI::Global::mainWindow, &m_instrumentLibrary);
    dlg.setGisasEnabled(m_document->functionalities().testFlag(ProjectDocument::Gisas));
    dlg.setOffspecEnabled(m_document->functionalities().testFlag(ProjectDocument::Offspec));
    dlg.setSpecularEnabled(m_document->functionalities().testFlag(ProjectDocument::Specular));
    dlg.setDepthprobeEnabled(m_document->functionalities().testFlag(ProjectDocument::Depthprobe));

    auto* instrumentToCopy = dlg.execChoose();
    if (instrumentToCopy == nullptr)
        return;

    QModelIndex idx = m_model->copyInstrument(instrumentToCopy);
    m_listView->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
}

void InstrumentListView::updateFunctionalityNarrowing()
{
    const auto f = m_document->functionalities();

    m_newGisasAction->setVisible(f.testFlag(ProjectDocument::Gisas));
    m_newOffspecAction->setVisible(f.testFlag(ProjectDocument::Offspec));
    m_newSpecularAction->setVisible(f.testFlag(ProjectDocument::Specular));
    m_newDepthprobeAction->setVisible(f.testFlag(ProjectDocument::Depthprobe));
    m_copyAction->setVisible(!m_document->singleInstrumentMode());
}

void InstrumentListView::updateActions()
{
    bool enabled = m_listView->selectionModel()->hasSelection();
    m_removeAction->setEnabled(enabled);
    m_copyAction->setEnabled(enabled);
    m_storeInLibraryAction->setEnabled(enabled);
}

void InstrumentListView::ensureItemSelected()
{
    if (!m_listView->selectionModel()->hasSelection() && m_model->rowCount()) {
        QModelIndex last = m_model->index(m_model->rowCount() - 1, 0, QModelIndex());
        m_listView->selectionModel()->select(last, QItemSelectionModel::ClearAndSelect);
    }
}

void InstrumentListView::restoreSelection()
{
    int lastUsed = m_document->instrumentModel()->selectedIndex();
    if (lastUsed >= 0 && lastUsed < m_model->rowCount()) {
        QModelIndex lastUsedIndex = m_model->index(lastUsed, 0, QModelIndex());
        m_listView->selectionModel()->select(lastUsedIndex, QItemSelectionModel::ClearAndSelect);
    } else
        ensureItemSelected();

    updateActions();
}
