//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Instrument/InstrumentListModel.cpp
//! @brief     Implements class InstrumentListModel
//!
//! @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/InstrumentListModel.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/Model/Device/MultiInstrumentNotifier.h"

namespace {

/// Get the default name for an instrument (i.e. the name a newly created instrument should get)
template <typename T>
QString defaultInstrumentName()
{
    return "Untitled";
}

template <>
QString defaultInstrumentName<SpecularInstrumentItem>()
{
    return "Specular";
}

template <>
QString defaultInstrumentName<DepthprobeInstrumentItem>()
{
    return "Depthprobe";
}

template <>
QString defaultInstrumentName<GISASInstrumentItem>()
{
    return "GISAS";
}

template <>
QString defaultInstrumentName<OffspecInstrumentItem>()
{
    return "Offspec";
}

} // namespace


InstrumentListModel::InstrumentListModel(QObject* parent, MultiInstrumentNotifier* ec)
    : QAbstractListModel(parent)
    , m_ec(ec)
{
    m_gisasIcon.addPixmap(QPixmap(":/images/gisas_instrument.svg"), QIcon::Selected);
    m_gisasIcon.addPixmap(QPixmap(":/images/gisas_instrument_shaded.svg"), QIcon::Normal);
    m_offspecIcon.addPixmap(QPixmap(":/images/offspec_instrument.svg"), QIcon::Selected);
    m_offspecIcon.addPixmap(QPixmap(":/images/offspec_instrument_shaded.svg"), QIcon::Normal);
    m_specularIcon.addPixmap(QPixmap(":/images/specular_instrument.svg"), QIcon::Selected);
    m_specularIcon.addPixmap(QPixmap(":/images/specular_instrument_shaded.svg"), QIcon::Normal);
    m_depthProbeIcon.addPixmap(QPixmap(":/images/depth_instrument.svg"), QIcon::Selected);
    m_depthProbeIcon.addPixmap(QPixmap(":/images/depth_instrument_shaded.svg"), QIcon::Normal);

    connect(ec, &MultiInstrumentNotifier::instrumentNameChanged, this,
            &InstrumentListModel::onInstrumentNameChanged);
}

int InstrumentListModel::rowCount(const QModelIndex&) const
{
    return m_ec->instrumentModel()->instrumentItems().size();
}

QVariant InstrumentListModel::data(const QModelIndex& index, int role) const
{
    QVector<InstrumentItem*> instruments = m_ec->instrumentModel()->instrumentItems();
    if (!index.isValid() || index.row() >= instruments.size() || index.row() < 0)
        return {};

    const InstrumentItem* item = instruments[index.row()];
    if (role == Qt::DecorationRole) {
        if (dynamic_cast<const GISASInstrumentItem*>(item))
            return m_gisasIcon;
        if (dynamic_cast<const OffspecInstrumentItem*>(item))
            return m_offspecIcon;
        if (dynamic_cast<const SpecularInstrumentItem*>(item))
            return m_specularIcon;
        if (dynamic_cast<const DepthprobeInstrumentItem*>(item))
            return m_depthProbeIcon;
        return {};
    }

    if (role == Qt::DisplayRole)
        return item->instrumentName();

    return {};
}

InstrumentItem* InstrumentListModel::instrumentItemForIndex(const QModelIndex& index) const
{
    if (!index.isValid())
        return nullptr;

    QVector<InstrumentItem*> instruments = m_ec->instrumentModel()->instrumentItems();
    if (index.row() >= 0 && index.row() < instruments.size())
        return instruments[index.row()];
    return nullptr;
}

QModelIndex InstrumentListModel::addNewGISASInstrument()
{
    return addNewInstrument<GISASInstrumentItem>();
}

QModelIndex InstrumentListModel::addNewOffspecInstrument()
{
    return addNewInstrument<OffspecInstrumentItem>();
}

QModelIndex InstrumentListModel::addNewSpecularInstrument()
{
    return addNewInstrument<SpecularInstrumentItem>();
}

QModelIndex InstrumentListModel::addNewDepthprobeInstrument()
{
    return addNewInstrument<DepthprobeInstrumentItem>();
}

void InstrumentListModel::removeInstrument(const QModelIndex& index)
{
    beginRemoveRows(QModelIndex(), index.row(), index.row());
    InstrumentItem* instrument = instrumentItemForIndex(index);
    m_ec->removeInstrument(instrument);
    endRemoveRows();
}

QModelIndex InstrumentListModel::copyInstrument(const QModelIndex& source)
{
    const InstrumentItem* srcInstr = instrumentItemForIndex(source);
    ASSERT(srcInstr);

    return copyInstrument(srcInstr);
}

QModelIndex InstrumentListModel::copyInstrument(const InstrumentItem* source)
{
    const QString copyName =
        m_ec->instrumentModel()->suggestInstrumentName(source->instrumentName());
    const int row = m_ec->instrumentModel()->instrumentItems().size();

    beginInsertRows(QModelIndex(), row, row);
    m_ec->addInstrumentItemCopy(source, copyName);
    endInsertRows();

    return createIndex(row, 0);
}

template <class Instrument>
QModelIndex InstrumentListModel::addNewInstrument()
{
    const QString name =
        m_ec->instrumentModel()->suggestInstrumentName(defaultInstrumentName<Instrument>());
    const int row = m_ec->instrumentModel()->instrumentItems().size();

    beginInsertRows(QModelIndex(), row, row);
    auto* instrument = m_ec->addInstrumentItem<Instrument>();
    m_ec->setInstrumentName(instrument, name);
    endInsertRows();

    return createIndex(row, 0);
}

void InstrumentListModel::onInstrumentNameChanged(const InstrumentItem* instrument)
{
    const auto instruments = m_ec->instrumentModel()->instrumentItems();
    if (const auto row = instruments.indexOf(instrument); row != -1)
        emit dataChanged(index(row, 0), index(row, 0));
}
