//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Tuning/PartunerQModel.cpp
//! @brief     Implements class PartunerQModel.
//!
//! @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/View/Tuning/PartunerQModel.h"
#include "GUI/Model/Par/ParameterTreeItems.h"
#include "GUI/Model/Util/UtilXML.h"
#include <QFont>
#include <QMimeData>

PartunerQModel::PartunerQModel(QObject* rootObject, QObject* parent)
    : QAbstractItemModel(parent)
    , m_root_object(rootObject)
{
}

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

QVariant PartunerQModel::parentColor(const QModelIndex& index) const
{
    if (!index.isValid())
        return {};

    if (const auto* label = toParameterLabelItem(index))
        if (label->color().isValid())
            return QVariant(label->color());

    return parentColor(index.parent());
}

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

    if (role == Qt::BackgroundRole)
        return parentColor(index);

    if (const auto* label = toParameterLabelItem(index)) {
        if (role == Qt::FontRole) {
            QFont boldFont;
            boldFont.setBold(true);
            return boldFont;
        }
        if (role == Qt::DisplayRole && index.column() == 0)
            return label->title();
        if (role == Qt::UserRole) {
            return label->collapsed();
        }
        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 PartunerQModel::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 PartunerQModel::index(int row, int column, const QModelIndex& parent) const
{
    if (!hasIndex(row, column, parent))
        return {};

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

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

    return {};
}

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

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

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

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

    if (!parent.isValid())
        return m_root_object->children().size();

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

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

QMimeData* PartunerQModel::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 PartunerQModel::supportedDragActions() const
{
    return Qt::CopyAction;
}

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

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

QModelIndex PartunerQModel::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* PartunerQModel::toParameterItem(const QModelIndex& index)
{
    auto* item = static_cast<QObject*>(index.internalPointer());
    return dynamic_cast<ParameterItem*>(item);
}

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

void PartunerQModel::setExpanded(const QModelIndex& index) const
{
    if (auto* label = toParameterLabelItem(index))
        label->setCollapsed(false);
}

void PartunerQModel::setCollapsed(const QModelIndex& index) const
{
    if (auto* label = toParameterLabelItem(index))
        label->setCollapsed(true);
}
