// SPDX-FileCopyrightText: 2020 - 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause

#include "selectgroupmodel.h"

#include <QVariant>
#include <QJsonValue>
#include <QJsonObject>
#include <QDebug>

const auto s_selectedRoleName = QByteArrayLiteral("selected");
const auto s_valueRoleName = QByteArrayLiteral("value");
const auto s_nameRoleName = QByteArrayLiteral("name");
const auto s_descriptionRoleName = QByteArrayLiteral("description");
const auto s_iconRoleName = QByteArrayLiteral("icon");

SelectGroupModel::SelectGroupModel(QObject *parent) : QAbstractListModel(parent), _exclusive(true)
{
}

int SelectGroupModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return _items.count();
}

QHash<int, QByteArray> SelectGroupModel::roleNames() const
{
    return { { SelectedRole, s_selectedRoleName },
             { ValueRole, s_valueRoleName },
             { NameRole, s_nameRoleName },
             { DescriptionRole, s_descriptionRoleName },
             { IconRole, s_iconRoleName } };
}

QVariant SelectGroupModel::data(const QModelIndex &index, int role) const
{
    auto iItem = index.row();
    if (iItem < 0 || iItem >= _items.count())
        return QVariant();
    switch (role) {
    case SelectedRole:
        return _selectedItems.contains(iItem);
    case ValueRole:
        return _items[iItem].value().toVariant();
    case NameRole:
        return _items[iItem].name();
    case DescriptionRole:
        return _items[iItem].description();
    case IconRole:
        return _items[iItem].icon();
    default:
        return QVariant();
    }
}

bool SelectGroupModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    auto iItem = index.row();
    if (iItem < 0 || iItem >= _items.count())
        return false;
    switch (role) {
    case SelectedRole:
        return updateSelected(iItem, value.toBool());
    case ValueRole:
        if (!_items[iItem].setValue(value.toJsonValue()))
            return true;
        break;
    case NameRole:
        if (!_items[iItem].setName(value.toString()))
            return true;
        break;
    case DescriptionRole:
        if (!_items[iItem].setDescription(value.toString()))
            return true;
        break;
    case IconRole:
        if (!_items[iItem].setIcon(value.toString()))
            return true;
        break;
    default:
        return false;
    }
    emit dataChanged(index, index, { role });
    return true;
}

Qt::ItemFlags SelectGroupModel::flags(const QModelIndex &index) const
{
    return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
}

bool SelectGroupModel::exclusive() const
{
    return _exclusive;
}

void SelectGroupModel::setExclusive(bool exclusive)
{
    if (_exclusive == exclusive)
        return;
    _exclusive = exclusive;
    if (_exclusive && _selectedItems.count() > 1) {
        while (_selectedItems.count() > 1) {
            auto iSelectedItem = *_selectedItems.begin();
            _selectedItems.remove(iSelectedItem);
            auto modelIndex = QAbstractItemModel::createIndex(iSelectedItem, 0);
            emit dataChanged(modelIndex, modelIndex, { SelectedRole });
        }
        emit selectedValuesChanged();
    }
    emit exclusiveChanged();
}

QJsonArray SelectGroupModel::selectedValues() const
{
    QJsonArray selectedValues;
    for (int iItem = 0; iItem < _items.count(); ++iItem) {
        if (_selectedItems.contains(iItem))
            selectedValues.append(_items[iItem].value());
        else
            selectedValues.append(QJsonValue(QJsonValue::Undefined));
    }
    return selectedValues;
}

int SelectGroupModel::selectedCount() const
{
    return _selectedItems.count();
}

void SelectGroupModel::clear()
{
    beginResetModel();
    _items.clear();
    endResetModel();
}

void SelectGroupModel::append(const QJsonValue &value)
{
    beginInsertRows(QModelIndex(), _items.count(), _items.count());
    auto item = Item(value);
    _items.append(item);
    if (item.selected())
        updateSelected(_items.count() - 1, true);
    endInsertRows();
}

bool SelectGroupModel::updateSelected(int iItem, bool selected)
{
    if (iItem < 0 || iItem >= _items.count())
        return false;
    if (selected) {
        if (_selectedItems.contains(iItem))
            return true;
        if (_exclusive) {
            auto oldSelectedItems = _selectedItems;
            _selectedItems.clear();
            for (auto oldSelectedItem : oldSelectedItems) {
                auto modelIndex = QAbstractItemModel::createIndex(oldSelectedItem, 0);
                emit dataChanged(modelIndex, modelIndex, { SelectedRole });
            }
        }
        _selectedItems.insert(iItem);
    } else {
        if (!_selectedItems.contains(iItem))
            return true;
        if (_exclusive && _selectedItems.count() <= 1)
            return true;
        _selectedItems.remove(iItem);
    }
    auto modelIndex = QAbstractItemModel::createIndex(iItem, 0);
    emit dataChanged(modelIndex, modelIndex, { SelectedRole });
    emit selectedValuesChanged();
    return true;
}

SelectGroupModel::Item::Item(const QJsonValue &value)
{
    if (value.isObject()) {
        auto object = value.toObject();
        _selected = object.value(s_selectedRoleName).toBool();
        _value = object.value(s_valueRoleName);
        _name = object.value(s_nameRoleName).toString();
        _description = object.value(s_descriptionRoleName).toString();
        _icon = object.value(s_iconRoleName).toString();
    } else {
        _selected = false;
        _value = value;
    }
}

bool SelectGroupModel::Item::selected() const
{
    return _selected;
}

const QJsonValue &SelectGroupModel::Item::value() const
{
    return _value;
}

bool SelectGroupModel::Item::setValue(const QJsonValue &value)
{
    if (_value == value)
        return false;
    _value = value;
    return true;
}

QString SelectGroupModel::Item::name() const
{
    return _name.isEmpty() ? _value.toVariant().toString() : _name;
}

bool SelectGroupModel::Item::setName(const QString &name)
{
    if (_name == name)
        return false;
    _name = name;
    return true;
}

QString SelectGroupModel::Item::description() const
{
    return _description;
}

bool SelectGroupModel::Item::setDescription(const QString &description)
{
    if (_description == description)
        return false;
    _description = description;
    return true;
}

QUrl SelectGroupModel::Item::icon() const
{
    return _icon;
}

bool SelectGroupModel::Item::setIcon(const QUrl &icon)
{
    if (_icon == icon)
        return false;
    _icon = icon;
    return true;
}
