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

namespace {

namespace Column {
enum Columns { Name, Value };
}
const QString ColumnNames[] = {"Name", "Value"};
const int NumColumns = (int)std::size(ColumnNames);

namespace Row {
enum Rows { Name, Sample, Instrument, Status, Begin, End, Duration };
}
const QString RowNames[] = {"Name", "Sample", "Instrument", "Status", "Begin", "End", "Duration"};
const int NumRows = (int)std::size(RowNames);

const QString ModelDateShortFormat = "yyyy.MM.dd hh:mm:ss";

} // namespace

JobPropertiesTableModel::JobPropertiesTableModel(QObject* parent)
    : QAbstractTableModel(parent)
    , m_item(nullptr)
{
}

JobPropertiesTableModel::~JobPropertiesTableModel()
{
    if (m_item)
        disconnect(m_item, nullptr, this, nullptr);
}

int JobPropertiesTableModel::rowCount(const QModelIndex& parent) const
{
    if (!parent.isValid() && m_item)
        return NumRows;
    return 0;
}

int JobPropertiesTableModel::columnCount(const QModelIndex& parent) const
{
    if (!parent.isValid() && m_item)
        return NumColumns;
    return 0;
}

QVariant JobPropertiesTableModel::data(const QModelIndex& index, int role) const
{
    if ((role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::ToolTipRole)
        || index.column() < 0 || index.column() >= NumColumns || index.row() < 0
        || index.row() >= NumRows || !m_item)
        return QVariant();

    switch (index.column()) {
    case Column::Name:
        return RowNames[index.row()];
    case Column::Value: {
        switch (index.row()) {
        case Row::Name:
            return m_item->jobName();
        case Row::Sample:
            return m_item->sampleItem()->sampleName();
        case Row::Instrument:
            return m_item->instrumentItem()->instrumentName();
        case Row::Status:
            return jobStatusToString(m_item->status());
        case Row::Begin:
            if (role == Qt::ToolTipRole)
                return QLocale().toString(m_item->beginTime(), QLocale::LongFormat);
            return m_item->beginTime().toString(ModelDateShortFormat);
        case Row::End:
            if (role == Qt::ToolTipRole)
                return QLocale().toString(m_item->endTime(), QLocale::LongFormat);
            return m_item->endTime().toString(ModelDateShortFormat);
        case Row::Duration: {
            std::optional<size_t> duration = m_item->duration();
            if (duration)
                return QString("%1 s").arg(duration.value() / 1000., 0, 'f', 3);
            return QVariant();
        }
        default:
            return QVariant();
        }
    }
    default:
        return QVariant();
    }
}

QVariant JobPropertiesTableModel::headerData(int section, Qt::Orientation orientation,
                                             int role) const
{
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section >= 0
        && section < NumColumns)
        return ColumnNames[section];
    return QVariant();
}

Qt::ItemFlags JobPropertiesTableModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags f = QAbstractTableModel::flags(index);
    if (index.column() == Column::Value && index.row() == Row::Name && m_item)
        f.setFlag(Qt::ItemIsEditable);
    return f;
}

bool JobPropertiesTableModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (role != Qt::EditRole || index.column() != Column::Value || index.row() != Row::Name
        || !m_item)
        return false;
    m_item->setJobName(value.toString());
    return true;
}

void JobPropertiesTableModel::setJobItem(JobItem* jobItem)
{
    beginResetModel();
    if (m_item)
        disconnect(m_item, nullptr, this, nullptr);
    m_item = jobItem;
    if (m_item)
        notifyJobPropertyChange();
    endResetModel();
}

void JobPropertiesTableModel::notifyJobPropertyChange()
{
    // name
    connect(m_item, &JobItem::jobNameChanged, this, [this](const QString&) {
        emit dataChanged(index(Row::Name, Column::Value), index(Row::Name, Column::Value),
                         {Qt::DisplayRole, Qt::EditRole});
    });

    // status
    connect(m_item, &JobItem::jobStatusChanged, this, [this](const JobStatus) {
        emit dataChanged(index(Row::Status, Column::Value), index(Row::Status, Column::Value),
                         {Qt::DisplayRole, Qt::EditRole});
    });

    // begin time
    connect(m_item, &JobItem::jobBeginTimeChanged, this, [this](const QDateTime&) {
        emit dataChanged(index(Row::Begin, Column::Value), index(Row::Begin, Column::Value),
                         {Qt::DisplayRole, Qt::EditRole});
    });

    // end time and duration
    connect(m_item, &JobItem::jobEndTimeChanged, this, [this](const QDateTime&) {
        emit dataChanged(index(Row::End, Column::Value), index(Row::Duration, Column::Value),
                         {Qt::DisplayRole, Qt::EditRole});
    });
}
