//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Model/ParameterTuningModel.cpp
//! @brief     Implements class ParameterTuningModel
//!
//! @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/Model/Model/ParameterTuningModel.h"
#include "GUI/Model/Job/ParameterTreeItems.h"
#include "GUI/Support/XML/UtilXML.h"
#include <QMimeData>

ParameterTuningModel::ParameterTuningModel(QObject* rootObject, QObject* parent)
    : QAbstractItemModel(parent)
    , m_rootObject(rootObject)
{
}

QVariant ParameterTuningModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
        return {};
    return (section == 0) ? "Name" : "Value";
}

QVariant ParameterTuningModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
        return {};

    if (const auto* label = toParameterLabelItem(index)) {
        if (role == Qt::DisplayRole && index.column() == 0)
            return label->title();
        return {};
    }

    if (auto var = toParameterItem(index)) {
        if (role == Qt::DisplayRole || role == Qt::EditRole) {
            if (index.column() == 0)
                return var->title();
            return var->valueOfLink();
        }
        return {};
    }

    return {};
}

Qt::ItemFlags ParameterTuningModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags result = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    if (toParameterItem(index)) {
        result |= Qt::ItemIsDragEnabled;
        if (index.column() == 1)
            result |= Qt::ItemIsEditable;
    }
    return result;
}

QModelIndex ParameterTuningModel::index(int row, int column, const QModelIndex& parent) const
{
    if (!hasIndex(row, column, parent))
        return {};

    if (!parent.isValid())
        return createIndex(row, column, m_rootObject->children()[row]);

    if (auto label = toParameterLabelItem(parent))
        return createIndex(row, column, label->children()[row]);

    return {};
}

QModelIndex ParameterTuningModel::parent(const QModelIndex& index) const
{
    if (!index.isValid())
        return {};

    QObject* item = static_cast<QObject*>(index.internalPointer());
    if (item->parent() == m_rootObject)
        return {};

    const int row = item->parent()->parent()->children().indexOf(item->parent());
    return createIndex(row, 0, item->parent());
}

int ParameterTuningModel::rowCount(const QModelIndex& index) const
{
    if (index.column() > 0)
        return 0;

    if (!index.isValid())
        return m_rootObject->children().size();

    QObject* item = static_cast<QObject*>(index.internalPointer());
    return item->children().size();
}

int ParameterTuningModel::columnCount(const QModelIndex&) const
{
    return 2;
}

QMimeData* ParameterTuningModel::mimeData(const QModelIndexList& indexes) const
{
    auto* mimeData = new QMimeData();

    for (auto index : indexes) {
        if (ParameterItem* parameterItem = toParameterItem(index)) {
            QByteArray data;
            data.setNum(reinterpret_cast<qlonglong>(parameterItem));
            mimeData->setData(XML::LinkMimeType, data);
            break;
        }
    }
    return mimeData;
}

Qt::DropActions ParameterTuningModel::supportedDragActions() const
{
    return Qt::CopyAction;
}

Qt::DropActions ParameterTuningModel::supportedDropActions() const
{
    return Qt::IgnoreAction;
}

ParameterItem* ParameterTuningModel::getParameterItem(const QModelIndex& index) const
{
    return toParameterItem(index);
}

QModelIndex ParameterTuningModel::indexForItem(ParameterItem* item) const
{
    if (item == nullptr)
        return {};

    if (item->parent()) {
        const int row = item->parent()->children().indexOf(item);
        return createIndex(row, 0, item);
    }

    return {};
}

ParameterItem* ParameterTuningModel::toParameterItem(const QModelIndex& index)
{
    QObject* item = static_cast<QObject*>(index.internalPointer());
    return dynamic_cast<ParameterItem*>(item);
}

ParameterLabelItem* ParameterTuningModel::toParameterLabelItem(const QModelIndex& index)
{
    QObject* item = static_cast<QObject*>(index.internalPointer());
    return dynamic_cast<ParameterLabelItem*>(item);
}
