/*
    This file is part of the KFileMetaData project
    SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/

#include "propertyinfo.h"

#include <KLazyLocalizedString>
#include <KLocalizedString>

#include "formatstrings_p.h"
#include "icnamematch_p.h"

#include <array>

#include <QLocale>

using namespace KFileMetaData;

class KFileMetaData::PropertyInfoData
{
    using FormatFunction = QString (&)(const QVariant&);

public:
    const Property::Property prop;
    const bool shouldBeIndexed = false;
    const QString name;
    const KLazyLocalizedString displayName;
    const QMetaType::Type valueType = QMetaType::UnknownType;
    const FormatFunction formatAsString = FormatStrings::toStringFunction;

    static constexpr auto fromId(Property::Property property) -> const PropertyInfoData*;
    static auto fromName(const QString& name) -> const PropertyInfoData*;

    static const PropertyInfoData s_Empty;
    static const std::array<PropertyInfoData, 83> s_allProperties;
    static const QHash<LcIdentifierName, const PropertyInfoData*> s_propertyHash;
};

const PropertyInfoData PropertyInfoData::s_Empty{ Property::Empty, false, QStringLiteral("empty"), kli18nc("@label", "Empty") };

const std::array<PropertyInfoData, 83> PropertyInfoData::s_allProperties
{
    PropertyInfoData{ Property::AssistiveAlternateDescription, true,  QStringLiteral("assistiveDescription"),    kli18nc("@label accessible alt text", "Assistive Description"), QMetaType::QString },
    PropertyInfoData{ Property::Album,                      true,  QStringLiteral("album"),                      kli18nc("@label music album", "Album"),                QMetaType::QString },
    PropertyInfoData{ Property::AlbumArtist,                true,  QStringLiteral("albumArtist"),                kli18nc("@label", "Album Artist"),                     QMetaType::QString },
    PropertyInfoData{ Property::Artist,                     true,  QStringLiteral("artist"),                     kli18nc("@label", "Artist"),                           QMetaType::QString },
    PropertyInfoData{ Property::AspectRatio,                false, QStringLiteral("aspectRatio"),                kli18nc("@label", "Aspect Ratio"),                     QMetaType::Double,    FormatStrings::formatAspectRatio },
    PropertyInfoData{ Property::Author,                     true,  QStringLiteral("author"),                     kli18nc("@label", "Author"),                           QMetaType::QString },
    PropertyInfoData{ Property::BitRate,                    false, QStringLiteral("bitRate"),                    kli18nc("@label", "Bitrate"),                          QMetaType::Int,       FormatStrings::formatBitRate },
    PropertyInfoData{ Property::Channels,                   false, QStringLiteral("channels"),                   kli18nc("@label", "Channels"),                         QMetaType::Int },
    PropertyInfoData{ Property::Comment,                    false, QStringLiteral("comment"),                    kli18nc("@label", "Comment"),                          QMetaType::QString },
    PropertyInfoData{ Property::Description,                false, QStringLiteral("description"),                kli18nc("@label", "Description"),                      QMetaType::QString },
    PropertyInfoData{ Property::Composer,                   true,  QStringLiteral("composer"),                   kli18nc("@label", "Composer"),                         QMetaType::QString },
    PropertyInfoData{ Property::Copyright,                  false, QStringLiteral("copyright"),                  kli18nc("@label", "Copyright"),                        QMetaType::QString },
    PropertyInfoData{ Property::CreationDate,               false, QStringLiteral("creationDate"),               kli18nc("@label", "Creation Date"),                    QMetaType::QDateTime, FormatStrings::formatDate },
    PropertyInfoData{ Property::Duration,                   false, QStringLiteral("duration"),                   kli18nc("@label", "Duration"),                         QMetaType::Int,       FormatStrings::formatDuration },
    PropertyInfoData{ Property::FrameRate,                  false, QStringLiteral("frameRate"),                  kli18nc("@label", "Frame Rate"),                       QMetaType::Double,    FormatStrings::formatAsFrameRate },
    PropertyInfoData{ Property::Generator,                  false, QStringLiteral("generator"),                  kli18nc("@label", "Document Generated By"),            QMetaType::QString },
    PropertyInfoData{ Property::Genre,                      true,  QStringLiteral("genre"),                      kli18nc("@label music genre", "Genre"),                QMetaType::QString },
    PropertyInfoData{ Property::Height,                     false, QStringLiteral("height"),                     kli18nc("@label", "Height"),                           QMetaType::Int },
    PropertyInfoData{ Property::ImageDateTime,              false, QStringLiteral("imageDateTime"),              kli18nc("@label EXIF", "Image Date Time"),             QMetaType::QDateTime, FormatStrings::formatDate },
    PropertyInfoData{ Property::Manufacturer,               false, QStringLiteral("manufacturer"),               kli18nc("@label EXIF", "Manufacturer"),                QMetaType::QString },
    PropertyInfoData{ Property::Model,                      false, QStringLiteral("model"),                      kli18nc("@label EXIF", "Model"),                       QMetaType::QString },
    PropertyInfoData{ Property::ImageOrientation,           false, QStringLiteral("imageOrientation"),           kli18nc("@label EXIF", "Image Orientation"),           QMetaType::Int,       FormatStrings::formatOrientationValue },
    PropertyInfoData{ Property::Keywords,                   false, QStringLiteral("keywords"),                   kli18nc("@label", "Keywords"),                         QMetaType::QString },
    PropertyInfoData{ Property::Language,                   false, QStringLiteral("language"),                   kli18nc("@label", "Language"),                         QMetaType::QString },
    PropertyInfoData{ Property::LineCount,                  false, QStringLiteral("lineCount"),                  kli18nc("@label number of lines", "Line Count"),       QMetaType::Int },
    PropertyInfoData{ Property::Lyricist,                   true,  QStringLiteral("lyricist"),                   kli18nc("@label", "Lyricist"),                         QMetaType::QString },
    PropertyInfoData{ Property::PageCount,                  false, QStringLiteral("pageCount"),                  kli18nc("@label", "Page Count"),                       QMetaType::Int },
    PropertyInfoData{ Property::PhotoApertureValue,         false, QStringLiteral("photoApertureValue"),         kli18nc("@label EXIF", "Aperture Value"),              QMetaType::Double,    FormatStrings::formatAsFNumber },
    PropertyInfoData{ Property::PhotoDateTimeOriginal,      false, QStringLiteral("photoDateTimeOriginal"),      kli18nc("@label EXIF", "Original Date Time"),          QMetaType::QDateTime, FormatStrings::formatDate },
    PropertyInfoData{ Property::PhotoExposureBiasValue,     false, QStringLiteral("photoExposureBiasValue"),     kli18nc("@label EXIF", "Exposure Bias"),               QMetaType::Double,    FormatStrings::formatPhotoExposureBias },
    PropertyInfoData{ Property::PhotoExposureTime,          false, QStringLiteral("photoExposureTime"),          kli18nc("@label EXIF", "Exposure Time"),               QMetaType::Double,    FormatStrings::formatPhotoTime },
    PropertyInfoData{ Property::PhotoFlash,                 false, QStringLiteral("photoFlash"),                 kli18nc("@label EXIF", "Flash"),                       QMetaType::Int,       FormatStrings::formatPhotoFlashValue },
    PropertyInfoData{ Property::PhotoFNumber,               false, QStringLiteral("photoFNumber"),               kli18nc("@label EXIF", "F Number"),                    QMetaType::Double,    FormatStrings::formatAsFNumber },
    PropertyInfoData{ Property::PhotoFocalLength,           false, QStringLiteral("photoFocalLength"),           kli18nc("@label EXIF", "Focal Length"),                QMetaType::Double,    FormatStrings::formatAsMilliMeter },
    PropertyInfoData{ Property::PhotoFocalLengthIn35mmFilm, false, QStringLiteral("photoFocalLengthIn35mmFilm"), kli18nc("@label EXIF", "Focal Length 35mm"),           QMetaType::Double,    FormatStrings::formatAsMilliMeter },
    PropertyInfoData{ Property::PhotoGpsLatitude,           false, QStringLiteral("photoGpsLatitude"),           kli18nc("@label EXIF", "GPS Latitude"),                QMetaType::Double,    FormatStrings::formatAsDegree },
    PropertyInfoData{ Property::PhotoGpsLongitude,          false, QStringLiteral("photoGpsLongitude"),          kli18nc("@label EXIF", "GPS Longitude"),               QMetaType::Double,    FormatStrings::formatAsDegree },
    PropertyInfoData{ Property::PhotoGpsAltitude,           false, QStringLiteral("photoGpsAltitude"),           kli18nc("@label EXIF", "GPS Altitude"),                QMetaType::Double,    FormatStrings::formatAsMeter },
    PropertyInfoData{ Property::PhotoISOSpeedRatings,       false, QStringLiteral("photoISOSpeedRatings"),       kli18nc("@label EXIF", "ISO Speed Rating"),            QMetaType::Int },
    PropertyInfoData{ Property::PhotoMeteringMode,          false, QStringLiteral("photoMeteringMode"),          kli18nc("@label EXIF", "Metering Mode"),               QMetaType::Int },
    PropertyInfoData{ Property::PhotoPixelXDimension,       false, QStringLiteral("photoPixelXDimension"),       kli18nc("@label EXIF", "X Dimension"),                 QMetaType::Int },
    PropertyInfoData{ Property::PhotoPixelYDimension,       false, QStringLiteral("photoPixelYDimension"),       kli18nc("@label EXIF", "Y Dimension"),                 QMetaType::Int },
    PropertyInfoData{ Property::PhotoSaturation,            false, QStringLiteral("photoSaturation"),            kli18nc("@label EXIF", "Saturation"),                  QMetaType::Int },
    PropertyInfoData{ Property::PhotoSharpness,             false, QStringLiteral("photoSharpness"),             kli18nc("@label EXIF", "Sharpness"),                   QMetaType::Int },
    PropertyInfoData{ Property::PhotoWhiteBalance,          false, QStringLiteral("photoWhiteBalance"),          kli18nc("@label EXIF", "White Balance"),               QMetaType::Int },
    PropertyInfoData{ Property::ColorSpace,                 false, QStringLiteral("colorSpace"),                 kli18nc("@label", "Color Space"),                      QMetaType::QString },
    PropertyInfoData{ Property::VideoCodec,                 false, QStringLiteral("videoCodec"),                 kli18nc("@label", "Video Codec"),                      QMetaType::QString },
    PropertyInfoData{ Property::AudioCodec,                 false, QStringLiteral("audioCodec"),                 kli18nc("@label", "Audio Codec"),                      QMetaType::QString },
    PropertyInfoData{ Property::PixelFormat,                false, QStringLiteral("pixelFormat"),                kli18nc("@label", "Pixel Format"),                     QMetaType::QString },
    PropertyInfoData{ Property::Publisher,                  true,  QStringLiteral("publisher"),                  kli18nc("@label", "Publisher"),                        QMetaType::QString },
    PropertyInfoData{ Property::Label,                      true,  QStringLiteral("label"),                      kli18nc("@label", "Label"),                            QMetaType::QString },
    PropertyInfoData{ Property::ReleaseYear,                false, QStringLiteral("releaseYear"),                kli18nc("@label", "Release Year"),                     QMetaType::Int },
    PropertyInfoData{ Property::SampleRate,                 false, QStringLiteral("sampleRate"),                 kli18nc("@label", "Sample Rate"),                      QMetaType::Int,       FormatStrings::formatSampleRate },
    PropertyInfoData{ Property::Subject,                    false, QStringLiteral("subject"),                    kli18nc("@label", "Subject"),                          QMetaType::QString },
    PropertyInfoData{ Property::Title,                      true,  QStringLiteral("title"),                      kli18nc("@label", "Title"),                            QMetaType::QString },
    PropertyInfoData{ Property::TrackNumber,                false, QStringLiteral("trackNumber"),                kli18nc("@label music track number", "Track Number"),  QMetaType::Int },
    PropertyInfoData{ Property::DiscNumber,                 false, QStringLiteral("discNumber"),                 kli18nc("@label music disc number", "Disc Number"),    QMetaType::Int },
    PropertyInfoData{ Property::Location,                   true,  QStringLiteral("location"),                   kli18nc("@label", "Location"),                         QMetaType::QString },
    PropertyInfoData{ Property::Performer,                  true,  QStringLiteral("performer"),                  kli18nc("@label", "Performer"),                        QMetaType::QString },
    PropertyInfoData{ Property::Ensemble,                   true,  QStringLiteral("ensemble"),                   kli18nc("@label", "Ensemble"),                         QMetaType::QString },
    PropertyInfoData{ Property::Arranger,                   true,  QStringLiteral("arranger"),                   kli18nc("@label", "Arranger"),                         QMetaType::QString },
    PropertyInfoData{ Property::Conductor,                  true,  QStringLiteral("conductor"),                  kli18nc("@label", "Conductor"),                        QMetaType::QString },
    PropertyInfoData{ Property::Compilation,                true,  QStringLiteral("compilation"),                kli18nc("@label", "Compilation"),                      QMetaType::QString },
    PropertyInfoData{ Property::License,                    true,  QStringLiteral("license"),                    kli18nc("@label", "License"),                          QMetaType::QString },
    PropertyInfoData{ Property::Lyrics,                     true,  QStringLiteral("lyrics"),                     kli18nc("@label", "Lyrics"),                           QMetaType::QString },
    PropertyInfoData{ Property::Opus,                       false, QStringLiteral("opus"),                       kli18nc("@label", "Opus"),                             QMetaType::Int },
    PropertyInfoData{ Property::Rating,                     false, QStringLiteral("embeddedRating"),             kli18nc("@label", "Rating"),                           QMetaType::Int },
    PropertyInfoData{ Property::ReplayGainAlbumPeak,        false, QStringLiteral("replayGainAlbumPeak"),        kli18nc("@label ReplayGain is the name of a standard", "ReplayGain Album Peak"),           QMetaType::Double, FormatStrings::formatDouble },
    PropertyInfoData{ Property::ReplayGainAlbumGain,        false, QStringLiteral("replayGainAlbumGain"),        kli18nc("@label ReplayGain is the name of a standard", "ReplayGain Album Gain"),           QMetaType::Double, FormatStrings::formatDouble },
    PropertyInfoData{ Property::ReplayGainTrackPeak,        false, QStringLiteral("replayGainTrackPeak"),        kli18nc("@label ReplayGain is the name of a standard", "ReplayGain Track Peak"),           QMetaType::Double, FormatStrings::formatDouble },
    PropertyInfoData{ Property::ReplayGainTrackGain,        false, QStringLiteral("replayGainTrackGain"),        kli18nc("@label ReplayGain is the name of a standard", "ReplayGain Track Gain"),           QMetaType::Double, FormatStrings::formatDouble },
    PropertyInfoData{ Property::Width,                      false, QStringLiteral("width"),                      kli18nc("@label", "Width"),                            QMetaType::Int },
    PropertyInfoData{ Property::WordCount,                  false, QStringLiteral("wordCount"),                  kli18nc("@label number of words", "Word Count"),       QMetaType::Int },
    PropertyInfoData{ Property::TranslationUnitsTotal,                false, QStringLiteral("translationUnitsTotal"),                kli18nc("@label number of translatable strings", "Translatable Units"),      QMetaType::Int },
    PropertyInfoData{ Property::TranslationUnitsWithTranslation,      false, QStringLiteral("translationUnitsWithTranslation"),      kli18nc("@label number of translated strings", "Translations"),              QMetaType::Int },
    PropertyInfoData{ Property::TranslationUnitsWithDraftTranslation, false, QStringLiteral("translationUnitsWithDraftTranslation"), kli18nc("@label number of fuzzy translated strings", "Draft Translations"),  QMetaType::Int },
    PropertyInfoData{ Property::TranslationLastAuthor,                false, QStringLiteral("translationLastAuthor"),                kli18nc("@label translation author", "Author"),                              QMetaType::QString },
    PropertyInfoData{ Property::TranslationLastUpDate,                false, QStringLiteral("translationLastUpDate"),                kli18nc("@label translations last update", "Last Update"),                   QMetaType::QString, FormatStrings::formatDate },
    PropertyInfoData{ Property::TranslationTemplateDate,              false, QStringLiteral("translationTemplateDate"),              kli18nc("@label date of template creation8", "Template Creation"),           QMetaType::QString, FormatStrings::formatDate },
    PropertyInfoData{ Property::OriginUrl,                  false, QStringLiteral("originUrl"),                  kli18nc("@label the URL a file was originally downloaded from", "Downloaded From"),                     QMetaType::QUrl },
    PropertyInfoData{ Property::OriginEmailSubject,         false, QStringLiteral("originEmailSubject"),         kli18nc("@label the subject of an email this file was attached to", "E-Mail Attachment Subject"),       QMetaType::QString },
    PropertyInfoData{ Property::OriginEmailSender,          false, QStringLiteral("originEmailSender"),          kli18nc("@label the sender of an email this file was attached to", "E-Mail Attachment Sender"),         QMetaType::QString },
    PropertyInfoData{ Property::OriginEmailMessageId,       false, QStringLiteral("originEmailMessageId"),       kli18nc("@label the message ID of an email this file was attached to", "E-Mail Attachment Message ID"), QMetaType::QString }
};

const QHash<LcIdentifierName, const PropertyInfoData*> PropertyInfoData::s_propertyHash = []()
{
    QHash<LcIdentifierName, const PropertyInfoData*> infoHash;
    infoHash.reserve(s_allProperties.size());

    for (const auto& info: s_allProperties) {
        infoHash[QStringView(info.name)] = &info;
    }
    return infoHash;
}();

constexpr auto PropertyInfoData::fromId(Property::Property property) -> const PropertyInfoData*
{
    for (const auto& p : s_allProperties) {
        if (p.prop == property)
            return &p;
    }
    return &s_Empty;
}

auto PropertyInfoData::fromName(const QString& name) -> const PropertyInfoData*
{
    return s_propertyHash.value(LcIdentifierName(name), &s_Empty);
}


PropertyInfo::PropertyInfo(Property::Property property) : d(PropertyInfoData::fromId(property)) {};

PropertyInfo::PropertyInfo() : d(&PropertyInfoData::s_Empty) {};

PropertyInfo::PropertyInfo(const PropertyInfo& pi)
    : d(pi.d)
{
}

PropertyInfo::~PropertyInfo() = default;

PropertyInfo& PropertyInfo::operator=(const PropertyInfo& rhs)
{
    d = rhs.d;
    return *this;
}

bool PropertyInfo::operator==(const PropertyInfo& rhs) const
{
    return d == rhs.d;
}

QString PropertyInfo::displayName() const
{
    return d->displayName.toString();
}

QString PropertyInfo::name() const
{
    return d->name;
}

Property::Property PropertyInfo::property() const
{
    return d->prop;
}

QMetaType::Type PropertyInfo::valueType() const
{
    return d->valueType;
}

bool PropertyInfo::shouldBeIndexed() const
{
    return d->shouldBeIndexed;
}

QString PropertyInfo::formatAsDisplayString(const QVariant &value) const
{
    if (value.userType() == QMetaType::QVariantList || value.userType() == QMetaType::QStringList) {
        if (d->valueType == QMetaType::QString) {
            return QLocale().createSeparatedList(value.toStringList());
        } else {
            QStringList displayList;
            const auto valueList = value.toList();
            for (const auto& entry : valueList) {
                displayList << d->formatAsString(entry);
            }
            return QLocale().createSeparatedList(displayList);
        }
    } else {
        return d->formatAsString(value);
    }
}

PropertyInfo PropertyInfo::fromName(const QString& name)
{
    PropertyInfo pi;
    pi.d = PropertyInfoData::fromName(name);
    return pi;
}

QStringList PropertyInfo::allNames()
{
    static QStringList sNames = []() {
        QStringList names;
        names.reserve(PropertyInfoData::s_allProperties.size());
        for (auto info: PropertyInfoData::s_allProperties) {
            names.append(info.name);
        }
        return names;
    }();
    return sNames;
}
