// SPDX-FileCopyrightText: 2024 Manuel Alcaraz Zambrano <manuel@alcarazzam.dev>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "playersmodel.h"

#include <KCountry>
#include <KLocalizedString>

#include "tournament/federations.h"
#include "tournament/tournament.h"

PlayersModel::PlayersModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

int PlayersModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return static_cast<int>(m_players.size());
}

int PlayersModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return static_cast<int>(m_columns.size());
}

QVariant PlayersModel::data(const QModelIndex &index, int role) const
{
    Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid));

    const auto player = m_players.at(index.row());
    const int column = m_columns.at(index.column());

    if (role == Qt::DisplayRole) {
        switch (column) {
        case PlayersModel::Columns::StartingRank:
            return player->startingRank();
        case PlayersModel::Columns::Title:
            return player->title();
        case PlayersModel::Columns::Name:
            return player->name();
        case PlayersModel::Columns::Rating:
            return QLocale::system().toString(player->rating());
        case PlayersModel::Columns::NationalRating:
            return QLocale::system().toString(player->nationalRating());
        case PlayersModel::Columns::PlayerId:
            return player->playerId();
        case PlayersModel::Columns::NationalId:
            return player->nationalId();
        case PlayersModel::Columns::BirthDate:
            return player->birthDate();
        case PlayersModel::Columns::Federation: {
            const auto federationCode = player->federation().trimmed().toUpper();
            if (FEDERATIONS.contains(federationCode)) {
                const auto federation = FEDERATIONS.value(federationCode);
                if (std::holds_alternative<QLocale::Territory>(federation)) {
                    const auto country = KCountry::fromQLocale(std::get<QLocale::Territory>(federation));
                    return country.name();
                }
                return std::get<KLazyLocalizedString>(federation).toString();
            }
            return player->federation();
        }
        case PlayersModel::Columns::Origin:
            return player->origin();
        case PlayersModel::Columns::Gender:
            return player->gender();
        }
    } else if (role == Qt::EditRole) {
        switch (column) {
        case PlayersModel::Columns::StartingRank:
            return player->startingRank();
        case PlayersModel::Columns::Title:
            return player->title();
        case PlayersModel::Columns::Name:
            return player->name();
        case PlayersModel::Columns::Rating:
            return player->rating();
        case PlayersModel::Columns::NationalRating:
            return player->nationalRating();
        case PlayersModel::Columns::PlayerId:
            return player->playerId();
        case PlayersModel::Columns::NationalId:
            return player->nationalId();
        case PlayersModel::Columns::BirthDate:
            return player->birthDate();
        case PlayersModel::Columns::Federation:
            return player->federation();
        case PlayersModel::Columns::Origin:
            return player->origin();
        case PlayersModel::Columns::Gender:
            return player->gender();
        }
    } else if (role == Qt::TextAlignmentRole) {
        switch (column) {
        case PlayersModel::Columns::StartingRank:
        case PlayersModel::Columns::Rating:
        case PlayersModel::Columns::NationalRating:
        case PlayersModel::Columns::PlayerId:
        case PlayersModel::Columns::NationalId:
            return Qt::AlignTrailing;
        default:
            return Qt::AlignLeading;
        }
    } else if (role == Roles::PlayerRole) {
        QQmlEngine::setObjectOwnership(player, QJSEngine::CppOwnership);
        return QVariant::fromValue(player);
    } else if (role == Roles::IconRole) {
        switch (column) {
        case PlayersModel::Columns::Federation:
            return u"qrc:/flags/%1.svg"_s.arg(player->federation().trimmed().toUpper());
        default:
            return QString();
        }
    }

    return {};
}

bool PlayersModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid));
    Q_ASSERT(role == Qt::EditRole);

    const auto player = m_players.at(index.row());
    const auto field = static_cast<Columns>(m_columns.at(index.column()));

    switch (field) {
    case PlayersModel::Columns::StartingRank: {
        auto rank = value.toInt();
        if (rank == player->startingRank() || rank <= 0) {
            return false;
        }
        m_tournament->changePlayerStartingRank(player, rank);
        break;
    }
    case PlayersModel::Columns::Title:
        player->setTitle(value.toString().trimmed());
        break;
    case PlayersModel::Columns::Name:
        player->setName(value.toString());
        break;
    case PlayersModel::Columns::Rating:
        player->setRating(value.toInt());
        break;
    case PlayersModel::Columns::NationalRating:
        player->setNationalRating(value.toInt());
        break;
    case PlayersModel::Columns::PlayerId:
        player->setPlayerId(value.toString());
        break;
    case PlayersModel::Columns::NationalId:
        player->setNationalId(value.toString());
        break;
    case PlayersModel::Columns::BirthDate:
        player->setBirthDate(value.toString());
        break;
    case PlayersModel::Columns::Federation:
        player->setFederation(value.toString());
        break;
    case PlayersModel::Columns::Origin:
        player->setOrigin(value.toString());
        break;
    case PlayersModel::Columns::Gender:
        player->setGender(value.toString());
        break;
    }

    Q_EMIT dataChanged(index, index, {});
    Q_EMIT playerChanged(player, field);

    return true;
}

QHash<int, QByteArray> PlayersModel::roleNames() const
{
    return {
        {Qt::DisplayRole, "display"},
        {Qt::EditRole, "edit"},
        {Qt::TextAlignmentRole, "textAlignment"},
        {Roles::PlayerRole, "player"},
        {Roles::IconRole, "iconSource"},
    };
}

Qt::ItemFlags PlayersModel::flags(const QModelIndex &index) const
{
    Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid));
    return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
}

QVariant PlayersModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    Q_UNUSED(orientation)
    Q_UNUSED(role)
    switch (m_columns.at(section)) {
    case PlayersModel::Columns::StartingRank:
        return i18nc("@title:column Player Starting Rank Number", "№");
    case PlayersModel::Columns::Title:
        return i18nc("@title:column Player Title", "Title");
    case PlayersModel::Columns::Name:
        return i18nc("@title:column Player Name", "Name");
    case PlayersModel::Columns::Rating:
        return i18nc("@title:column Player Rating", "Rating");
    case PlayersModel::Columns::NationalRating:
        return i18nc("@title:column Player National Rating", "National Rating");
    case PlayersModel::Columns::PlayerId:
        return i18nc("@title:column", "Player ID");
    case PlayersModel::Columns::NationalId:
        return i18nc("@title:column Player National ID", "National ID");
    case PlayersModel::Columns::BirthDate:
        return i18nc("@title:column", "Birth date");
    case PlayersModel::Columns::Federation:
        return i18nc("@title:column Player Federation", "Federation");
    case PlayersModel::Columns::Origin:
        return i18nc("@title:column Player Origin", "Origin");
    case PlayersModel::Columns::Gender:
        return i18nc("@title:column Player Gender", "Gender");
    }
    return {};
}

void PlayersModel::setColumns(const QList<int> &columns)
{
    m_columns = columns;
}

void PlayersModel::setTournament(Tournament *tournament)
{
    m_tournament = tournament;
}

void PlayersModel::setPlayers(const QList<Player *> &players)
{
    beginResetModel();
    m_players = players;
    endResetModel();
}

void PlayersModel::addPlayer(const QString &title,
                             const QString &name,
                             int rating,
                             int nationalRating,
                             const QString &playerId,
                             const QString &nationalId,
                             const QString &birthDate,
                             const QString &federation,
                             const QString &origin,
                             const QString &gender)
{
    const auto startingRank = m_tournament->numberOfPlayers() + 1;
    auto player = std::make_unique<Player>(startingRank, title, name, rating, nationalRating, playerId, birthDate, federation, origin, gender);
    player->setNationalId(nationalId);

    if (auto ok = m_tournament->addPlayer(std::move(player)); !ok) {
        Q_EMIT errorOcurred(ok.error());
        return;
    }

    beginInsertRows({}, static_cast<int>(m_players.size()), static_cast<int>(m_players.size()));
    m_players << m_tournament->players().constLast();
    endInsertRows();
}

void PlayersModel::updatePlayer(Player *player)
{
    Q_EMIT dataChanged(index(player->startingRank() - 1, 0), index(player->startingRank() - 1, columnCount() - 1), {});
}

void PlayersModel::reloadPlayers()
{
    Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1), {});
}

#include "moc_playersmodel.cpp"
