/*
    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 <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 QVariant::Type valueType = QVariant::Invalid;
    const FormatFunction formatAsString = FormatStrings::toStringFunction;
};

const static PropertyInfoData staticEmptyPropertyInfo{ Property::Empty, false, QStringLiteral("empty"), kli18n("Empty"), QVariant::Invalid };

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

namespace {
     auto constexpr propertyDataById(Property::Property property) {
         for (const auto& p : staticPropertyInfo) {
             if (p.prop == property)
                 return &p;
         }
         return &staticEmptyPropertyInfo;
     }
}

PropertyInfo::PropertyInfo(Property::Property property) : d(propertyDataById(property)) {};

PropertyInfo::PropertyInfo() : d(&staticEmptyPropertyInfo) {};

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;
}

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

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

QString PropertyInfo::formatAsDisplayString(const QVariant &value) const
{
    if (value.type() == QVariant::List || value.type() == QVariant::StringList) {
        if (d->valueType == QVariant::String) {
            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)
{
    static QHash<QString, Property::Property> propertyHash = {
        { QStringLiteral("bitrate"), Property::BitRate },
        { QStringLiteral("channels"), Property::Channels },
        { QStringLiteral("duration"), Property::Duration },
        { QStringLiteral("genre"), Property::Genre },
        { QStringLiteral("samplerate"), Property::SampleRate },
        { QStringLiteral("tracknumber"), Property::TrackNumber },
        { QStringLiteral("discnumber"), Property::DiscNumber },
        { QStringLiteral("releaseyear"), Property::ReleaseYear },
        { QStringLiteral("comment"), Property::Comment },
        { QStringLiteral("description"), Property::Description },
        { QStringLiteral("artist"), Property::Artist },
        { QStringLiteral("album"), Property::Album },
        { QStringLiteral("albumartist"), Property::AlbumArtist },
        { QStringLiteral("composer"), Property::Composer },
        { QStringLiteral("lyricist"), Property::Lyricist },
        { QStringLiteral("location"), Property::Location },
        { QStringLiteral("performer"), Property::Performer },
        { QStringLiteral("ensemble"), Property::Ensemble },
        { QStringLiteral("arranger"), Property::Arranger },
        { QStringLiteral("conductor"), Property::Conductor },
        { QStringLiteral("opus"), Property::Opus },
        { QStringLiteral("embeddedrating"), Property::Rating },
        { QStringLiteral("author"), Property::Author },
        { QStringLiteral("title"), Property::Title },
        { QStringLiteral("subject"), Property::Subject },
        { QStringLiteral("generator"), Property::Generator },
        { QStringLiteral("pagecount"), Property::PageCount },
        { QStringLiteral("wordcount"), Property::WordCount },
        { QStringLiteral("linecount"), Property::LineCount },
        { QStringLiteral("language"), Property::Language },
        { QStringLiteral("copyright"), Property::Copyright },
        { QStringLiteral("publisher"), Property::Publisher },
        { QStringLiteral("label"), Property::Label },
        { QStringLiteral("compilation"), Property::Compilation },
        { QStringLiteral("license"), Property::License },
        { QStringLiteral("lyrics"), Property::Lyrics },
        { QStringLiteral("replaygainalbumpeak"), Property::ReplayGainAlbumPeak },
        { QStringLiteral("replaygainalbumgain"), Property::ReplayGainAlbumGain },
        { QStringLiteral("replaygaintrackpeak"), Property::ReplayGainTrackPeak },
        { QStringLiteral("replaygaintrackgain"), Property::ReplayGainTrackGain },
        { QStringLiteral("creationdate"), Property::CreationDate },
        { QStringLiteral("keywords"), Property::Keywords },
        { QStringLiteral("width"), Property::Width },
        { QStringLiteral("height"), Property::Height },
        { QStringLiteral("aspectratio"), Property::AspectRatio },
        { QStringLiteral("framerate"), Property::FrameRate },
        { QStringLiteral("manufacturer"), Property::Manufacturer },
        { QStringLiteral("model"), Property::Model },
        { QStringLiteral("imagedatetime"), Property::ImageDateTime },
        { QStringLiteral("imageorientation"), Property::ImageOrientation },
        { QStringLiteral("photoflash"), Property::PhotoFlash },
        { QStringLiteral("photopixelxdimension"), Property::PhotoPixelXDimension },
        { QStringLiteral("photopixelydimension"), Property::PhotoPixelYDimension },
        { QStringLiteral("photodatetimeoriginal"), Property::PhotoDateTimeOriginal },
        { QStringLiteral("photofocallength"), Property::PhotoFocalLength },
        { QStringLiteral("photofocallengthin35mmfilm"), Property::PhotoFocalLengthIn35mmFilm },
        { QStringLiteral("photoexposuretime"), Property::PhotoExposureTime },
        { QStringLiteral("photofnumber"), Property::PhotoFNumber },
        { QStringLiteral("photoaperturevalue"), Property::PhotoApertureValue },
        { QStringLiteral("photoexposurebiasvalue"), Property::PhotoExposureBiasValue },
        { QStringLiteral("photowhitebalance"), Property::PhotoWhiteBalance },
        { QStringLiteral("photometeringmode"), Property::PhotoMeteringMode },
        { QStringLiteral("photoisospeedratings"), Property::PhotoISOSpeedRatings },
        { QStringLiteral("photosaturation"), Property::PhotoSaturation },
        { QStringLiteral("photosharpness"), Property::PhotoSharpness },
        { QStringLiteral("photogpslatitude"), Property::PhotoGpsLatitude },
        { QStringLiteral("photogpslongitude"), Property::PhotoGpsLongitude },
        { QStringLiteral("photogpsaltitude"), Property::PhotoGpsAltitude },
        { QStringLiteral("translationunitstotal"), Property::TranslationUnitsTotal },
        { QStringLiteral("translationunitswithtranslation"), Property::TranslationUnitsWithTranslation },
        { QStringLiteral("translationunitswithdrafttranslation"), Property::TranslationUnitsWithDraftTranslation },
        { QStringLiteral("translationlastauthor"), Property::TranslationLastAuthor },
        { QStringLiteral("translationlastupdate"), Property::TranslationLastUpDate },
        { QStringLiteral("translationtemplatedate"), Property::TranslationTemplateDate },
        { QStringLiteral("originurl"), Property::OriginUrl },
        { QStringLiteral("originemailsubject"), Property::OriginEmailSubject },
        { QStringLiteral("originemailsender"), Property::OriginEmailSender },
        { QStringLiteral("originemailmessageid"), Property::OriginEmailMessageId }
    };

    return PropertyInfo(propertyHash.value(name.toLower()));
}

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