/*
 * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
 * SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "kbaloorolesprovider.h"

#include <Baloo/File>
#include <KFileMetaData/PropertyInfo>
#include <KFileMetaData/UserMetaData>
#include <kfilemetadata_version.h>

#include <QCollator>
#include <QSize>

namespace
{
QString tagsFromValues(const QStringList &values)
{
    if (values.size() == 1) {
        return values.at(0);
    }

    QStringList alphabeticalOrderTags = values;
    QCollator coll;
    coll.setNumericMode(true);
    std::sort(alphabeticalOrderTags.begin(), alphabeticalOrderTags.end(), [&](const QString &s1, const QString &s2) {
        return coll.compare(s1, s2) < 0;
    });
    return alphabeticalOrderTags.join(QLatin1String(", "));
}

    using Property = KFileMetaData::Property::Property;
    // Mapping from the KFM::Property to the KFileItemModel roles.
    const QHash<Property, QByteArray> propertyRoleMap() {
        static const auto map = QHash<Property, QByteArray>{
            {Property::Rating, QByteArrayLiteral("rating")},
            {Property::Comment, QByteArrayLiteral("comment")},
            {Property::Title, QByteArrayLiteral("title")},
            {Property::Author, QByteArrayLiteral("author")},
            {Property::Publisher, QByteArrayLiteral("publisher")},
            {Property::PageCount, QByteArrayLiteral("pageCount")},
            {Property::WordCount, QByteArrayLiteral("wordCount")},
            {Property::LineCount, QByteArrayLiteral("lineCount")},
            {Property::Width, QByteArrayLiteral("width")},
            {Property::Height, QByteArrayLiteral("height")},
            {Property::ImageDateTime, QByteArrayLiteral("imageDateTime")},
            {Property::ImageOrientation, QByteArrayLiteral("orientation")},
            {Property::Artist, QByteArrayLiteral("artist")},
            {Property::Genre, QByteArrayLiteral("genre")},
            {Property::Album, QByteArrayLiteral("album")},
            {Property::Duration, QByteArrayLiteral("duration")},
            {Property::BitRate, QByteArrayLiteral("bitrate")},
            {Property::AspectRatio, QByteArrayLiteral("aspectRatio")},
            {Property::FrameRate, QByteArrayLiteral("frameRate")},
            {Property::ReleaseYear, QByteArrayLiteral("releaseYear")},
            {Property::TrackNumber, QByteArrayLiteral("track")},
#if KFILEMETADATA_VERSION >= QT_VERSION_CHECK(6, 13, 0)
            {Property::AudioCodec, QByteArrayLiteral("audioCodec")},
            {Property::VideoCodec, QByteArrayLiteral("videoCodec")},
            {Property::PixelFormat, QByteArrayLiteral("pixelFormat")},
            {Property::ColorSpace, QByteArrayLiteral("colorSpace")},
#endif
        };
        return map;
    }
}

struct KBalooRolesProviderSingleton {
    KBalooRolesProvider instance;
};
Q_GLOBAL_STATIC(KBalooRolesProviderSingleton, s_balooRolesProvider)

KBalooRolesProvider &KBalooRolesProvider::instance()
{
    return s_balooRolesProvider->instance;
}

KBalooRolesProvider::~KBalooRolesProvider()
{
}

QSet<QByteArray> KBalooRolesProvider::roles() const
{
    return m_roles;
}

QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File &file, const QSet<QByteArray> &roles) const
{
    QHash<QByteArray, QVariant> values;

    using entry = std::pair<const KFileMetaData::Property::Property &, const QVariant &>;

    const auto &propMap = file.properties();
    auto rangeBegin = propMap.constKeyValueBegin();

    while (rangeBegin != propMap.constKeyValueEnd()) {
        auto key = (*rangeBegin).first;

        auto rangeEnd = std::find_if(rangeBegin, propMap.constKeyValueEnd(), [key](const entry &e) {
            return e.first != key;
        });

        const QByteArray role = propertyRoleMap().value(key);
        if (role.isEmpty() || !roles.contains(role)) {
            rangeBegin = rangeEnd;
            continue;
        }

        const KFileMetaData::PropertyInfo propertyInfo(key);
        auto distance = std::distance(rangeBegin, rangeEnd);
        if (distance > 1) {
            QVariantList list;
            list.reserve(static_cast<int>(distance));
            std::for_each(rangeBegin, rangeEnd, [&list](const entry &s) {
                list.append(s.second);
            });
            values.insert(role, propertyInfo.formatAsDisplayString(list));
        } else {
            if (propertyInfo.valueType() == QMetaType::Type::QDateTime) {
                // Let dolphin format later Dates
                values.insert(role, (*rangeBegin).second);
            } else {
                values.insert(role, propertyInfo.formatAsDisplayString((*rangeBegin).second));
            }
        }
        rangeBegin = rangeEnd;
    }

    if (roles.contains("dimensions")) {
        bool widthOk = false;
        bool heightOk = false;

        const int width = propMap.value(KFileMetaData::Property::Width).toInt(&widthOk);
        const int height = propMap.value(KFileMetaData::Property::Height).toInt(&heightOk);

        if (widthOk && heightOk && width >= 0 && height >= 0) {
            values.insert("dimensions", QSize(width, height));
        }
    }

    KFileMetaData::UserMetaData::Attributes attributes;
    if (roles.contains("tags")) {
        attributes |= KFileMetaData::UserMetaData::Tags;
    }
    if (roles.contains("rating")) {
        attributes |= KFileMetaData::UserMetaData::Rating;
    }
    if (roles.contains("comment")) {
        attributes |= KFileMetaData::UserMetaData::Comment;
    }
    if (roles.contains("originUrl")) {
        attributes |= KFileMetaData::UserMetaData::OriginUrl;
    }

    if (attributes == KFileMetaData::UserMetaData::None) {
        return values;
    }

    KFileMetaData::UserMetaData md(file.path());
    attributes = md.queryAttributes(attributes);

    if (attributes & KFileMetaData::UserMetaData::Tags) {
        values.insert("tags", tagsFromValues(md.tags()));
    }
    if (attributes & KFileMetaData::UserMetaData::Rating) {
        values.insert("rating", QString::number(md.rating()));
    }
    if (attributes & KFileMetaData::UserMetaData::Comment) {
        values.insert("comment", md.userComment());
    }
    if (attributes & KFileMetaData::UserMetaData::OriginUrl) {
        values.insert("originUrl", md.originUrl());
    }

    return values;
}

KBalooRolesProvider::KBalooRolesProvider()
{
    // Display roles filled from Baloo property cache
    for (const auto &role : propertyRoleMap()) {
        m_roles.insert(role);
    }
    m_roles.insert("dimensions");

    // Display roles provided by UserMetaData
    m_roles.insert(QByteArrayLiteral("tags"));
    m_roles.insert(QByteArrayLiteral("rating"));
    m_roles.insert(QByteArrayLiteral("comment"));
    m_roles.insert(QByteArrayLiteral("originUrl"));
}
