/*
    SPDX-FileCopyrightText: 2017 Nicolas Carion
    SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/

#include "profiletreemodel.hpp"
#include "../profilemodel.hpp"
#include "../profilerepository.hpp"
#include "abstractmodel/treeitem.hpp"

#include <KLocalizedString>
#include <QIcon>
#include <array>
#include <functional>
#include <vector>

ProfileTreeModel::ProfileTreeModel(QObject *parent)
    : AbstractTreeModel(parent)
{
}

std::shared_ptr<ProfileTreeModel> ProfileTreeModel::construct(QObject *parent)
{
    std::shared_ptr<ProfileTreeModel> self(new ProfileTreeModel(parent));
    QList<QVariant> rootData;
    rootData << "Description"
             << "Path"
             << "Height"
             << "Width"
             << "display_aspect_num"
             << "display_aspect_den"
             << "sample_aspect_ratio"
             << "fps"
             << "colorspace";
    self->rootItem = TreeItem::construct(rootData, self, true);
    ProfileRepository::get()->refresh();
    QVector<QPair<QString, QString>> profiles = ProfileRepository::get()->getAllProfiles();

    constexpr size_t nbCrit = 3; // number of criterion we check for profile classification

    // helper lambda that creates a profile category with the given name
    auto createCat = [&](const QString &name) { return self->rootItem->appendChild(QList<QVariant>{name}); };
    // We define the filters as a vector of pairs. The first element correspond to the tree item holding matching profiles, and the array correspond to the
    // filter itself
    std::vector<std::pair<std::shared_ptr<TreeItem>, std::array<QVariant, nbCrit>>> filters{
        {createCat(i18n("5K (Wide 2160)")), {{5120, 2160, -1}}},
        {createCat(i18n("4K UHD 2160")), {{3840, 2160, -1}}},
        {createCat(i18n("4K DCI 2160")), {{4096, 2160, -1}}},
        {createCat(i18n("2.5K QHD 1440")), {{-1, 1440, -1}}},
        {createCat(i18n("Full HD 1080")), {{1920, 1080, -1}}},
        {createCat(i18n("HD 720")), {{-1, 720, -1}}},
        {createCat(i18n("SD/DVD")), {{720, QVariant::fromValue(QPair<int, int>{480, 576}), 4}}},
        {createCat(i18n("SD/DVD Widescreen")), {{720, QVariant::fromValue(QPair<int, int>{480, 576}), 16}}},
    };

    auto customCat = createCat(i18n("Custom"));
    // We define lambdas that controls how a given field should be filtered
    std::array<std::function<bool(QVariant, std::unique_ptr<ProfileModel> &)>, nbCrit> filtLambdas;
    filtLambdas[0] = [](QVariant width, std::unique_ptr<ProfileModel> &ptr) { return width == -1 || ptr->width() == width; };
    filtLambdas[1] = [](QVariant height, std::unique_ptr<ProfileModel> &ptr) {
        if (height.canConvert<int>()) {
            return height.toInt() == -1 || ptr->height() == height.toInt();
        }
        QPair<int, int> valid_values = height.value<QPair<int, int>>();
        return ptr->height() == valid_values.first || ptr->height() == valid_values.second;
    };
    filtLambdas[2] = [](QVariant display_aspect_num, std::unique_ptr<ProfileModel> &ptr) {
        return display_aspect_num == -1 || ptr->display_aspect_num() == display_aspect_num;
    };
    for (const auto &profile : qAsConst(profiles)) {
        bool foundMatch = false;
        // we get a pointer to the profilemodel
        std::unique_ptr<ProfileModel> &ptr = ProfileRepository::get()->getProfile(profile.second);
        // we create the data list corresponding to this profile
        QList<QVariant> data;
        data << profile.first << profile.second << ptr->height() << ptr->width() << ptr->display_aspect_num() << ptr->display_aspect_den() << ptr->sar()
             << ptr->fps() << ProfileRepository::getColorspaceDescription(ptr->colorspace());
        for (const auto &filter : filters) {
            bool matching = true;
            for (size_t i = 0; i < nbCrit && matching; ++i) {
                matching = filtLambdas[i](filter.second[i], ptr);
            }
            if (matching) {
                foundMatch = true;
                filter.first->appendChild(data);
                break;
            }
        }
        if (!foundMatch) {
            // no filter matched, we default to custom
            customCat->appendChild(data);
        }
    }
    return self;
}

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

    auto item = getItemById(int(index.internalId()));
    if (role == Qt::DecorationRole) {
        if (item->depth() == 1) {
            return QIcon::fromTheme(QStringLiteral("folder"));
        }
        return QIcon::fromTheme(QStringLiteral("file"));
    }

    if (role != Qt::DisplayRole) {
        return QVariant();
    }
    return item->dataColumn(index.column());
}

QString ProfileTreeModel::getProfile(const QModelIndex &index)
{
    if (index.isValid()) {
        auto item = getItemById(int(index.internalId()));
        if (item->depth() == 2) {
            return item->dataColumn(1).toString();
        }
    }
    return QString();
}

QModelIndex ProfileTreeModel::findProfile(const QString &profile)
{
    // we iterate over categories
    for (int i = 0; i < rootItem->childCount(); ++i) {
        // we iterate over profiles of the category
        std::shared_ptr<TreeItem> category = rootItem->child(i);
        for (int j = 0; j < category->childCount(); ++j) {
            // we retrieve profile path
            std::shared_ptr<TreeItem> child = category->child(j);
            QString path = child->dataColumn(1).toString();
            if (path == profile) {
                return createIndex(j, 0, quintptr(child->getId()));
            }
        }
    }
    return {};
}
