// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "QXmppFileMetadata.h"

#include "QXmppConstants_p.h"
#include "QXmppHash.h"
#include "QXmppThumbnail.h"
#include "QXmppUtils.h"
#include "QXmppUtils_p.h"

#include "Algorithms.h"
#include "StringLiterals.h"
#include "XmlWriter.h"

#include <utility>

#include <QDateTime>
#include <QDomElement>
#include <QFileInfo>
#include <QMimeDatabase>

using namespace QXmpp::Private;

class QXmppFileMetadataPrivate : public QSharedData
{
public:
    std::optional<QDateTime> date;
    std::optional<QString> desc;
    QVector<QXmppHash> hashes;
    std::optional<uint32_t> height;
    std::optional<uint32_t> length;
    std::optional<QMimeType> mediaType;
    std::optional<QString> name;
    std::optional<uint64_t> size;
    QVector<QXmppThumbnail> thumbnails;
    std::optional<uint32_t> width;
};

///
/// \class QXmppFileMetadata
///
/// File metadata from \xep{0446, File metadata element}.
///
/// \since QXmpp 1.5
///

///
/// \brief Creates a QXmppFileMetadata object from information from QFileInfo.
///
/// Sets the filename, file size, media type and the last modification date.
///
QXmppFileMetadata QXmppFileMetadata::fromFileInfo(const QFileInfo &info)
{
    QXmppFileMetadata metadata;
    metadata.setFilename(info.fileName());
    metadata.setSize(info.size());
    metadata.setMediaType(QMimeDatabase().mimeTypeForFile(info));
    metadata.setLastModified(info.lastModified());
    return metadata;
}

QXmppFileMetadata::QXmppFileMetadata()
    : d(new QXmppFileMetadataPrivate())
{
}

QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppFileMetadata)

/// \cond
bool QXmppFileMetadata::parse(const QDomElement &el)
{
    if (el.isNull()) {
        return false;
    }

    if (auto dateEl = firstChildElement(el, u"date"); !dateEl.isNull()) {
        d->date = QXmppUtils::datetimeFromString(dateEl.text());
    }

    if (auto descEl = firstChildElement(el, u"desc"); !descEl.isNull()) {
        d->desc = descEl.text();
    }

    d->hashes = parseChildElements<QVector<QXmppHash>>(el);

    if (auto heightEl = firstChildElement(el, u"height"); !heightEl.isNull()) {
        d->height = firstChildElement(el, u"height").text().toUInt();
    }
    if (auto lengthEl = firstChildElement(el, u"length"); !lengthEl.isNull()) {
        d->length = lengthEl.text().toUInt();
    }
    if (auto mediaTypeEl = firstChildElement(el, u"media-type"); !mediaTypeEl.isNull()) {
        d->mediaType = QMimeDatabase().mimeTypeForName(mediaTypeEl.text());
    }
    if (auto nameEl = firstChildElement(el, u"name"); !nameEl.isNull()) {
        d->name = nameEl.text();
    }
    if (auto sizeEl = firstChildElement(el, u"size"); !sizeEl.isNull()) {
        d->size = sizeEl.text().toULong();
    }
    d->thumbnails = parseChildElements<QVector<QXmppThumbnail>>(el);
    if (auto widthEl = firstChildElement(el, u"width"); !widthEl.isNull()) {
        d->width = widthEl.text().toUInt();
    }
    return true;
}

void QXmppFileMetadata::toXml(QXmlStreamWriter *writer) const
{
    XmlWriter(writer).write(Element {
        { u"file", ns_file_metadata },
        OptionalTextElement { u"date", d->date },
        OptionalTextElement { u"desc", d->desc },
        d->hashes,
        OptionalTextElement { u"height", d->height },
        OptionalTextElement { u"length", d->length },
        OptionalTextElement { u"media-type", d->mediaType },
        OptionalTextElement { u"name", d->name },
        OptionalTextElement { u"size", d->size },
        d->thumbnails,
        OptionalTextElement { u"width", d->width },
    });
}
/// \endcond

/// Returns when the file was last modified
const std::optional<QDateTime> &QXmppFileMetadata::lastModified() const
{
    return d->date;
}

/// Sets when the file was last modified
void QXmppFileMetadata::setLastModified(const std::optional<QDateTime> &date)
{
    d->date = date;
}

/// Returns the description of the file
const std::optional<QString> &QXmppFileMetadata::description() const
{
    return d->desc;
}

/// Sets the description of the file
void QXmppFileMetadata::setDescription(const std::optional<QString> &description)
{
    d->desc = description;
}

/// Returns the hashes of the file
const QVector<QXmppHash> &QXmppFileMetadata::hashes() const
{
    return d->hashes;
}

/// Sets the hashes of the file
void QXmppFileMetadata::setHashes(const QVector<QXmppHash> &hashes)
{
    d->hashes = hashes;
}

/// Returns the height of the image
std::optional<uint32_t> QXmppFileMetadata::height() const
{
    return d->height;
}

/// Sets the height of the image
void QXmppFileMetadata::setHeight(std::optional<uint32_t> height)
{
    d->height = height;
}

/// Returns the length of a video or audio file
std::optional<uint32_t> QXmppFileMetadata::length() const
{
    return d->length;
}

/// Sets the length of a video or audio file
void QXmppFileMetadata::setLength(std::optional<uint32_t> length)
{
    d->length = length;
}

/// Returns the media type of the file
const std::optional<QMimeType> &QXmppFileMetadata::mediaType() const
{
    return d->mediaType;
}

/// Sets the media type of the file
void QXmppFileMetadata::setMediaType(std::optional<QMimeType> mediaType)
{
    d->mediaType = std::move(mediaType);
}

/// Returns the filename
std::optional<QString> QXmppFileMetadata::filename() const
{
    return d->name;
}

/// Sets the filename
void QXmppFileMetadata::setFilename(std::optional<QString> name)
{
    d->name = std::move(name);
}

/// Returns the size of the file in bytes
std::optional<uint64_t> QXmppFileMetadata::size() const
{
    return d->size;
}

/// Sets the size of the file in bytes
void QXmppFileMetadata::setSize(std::optional<uint64_t> size)
{
    d->size = size;
}

/// Returns the thumbnail references.
const QVector<QXmppThumbnail> &QXmppFileMetadata::thumbnails() const
{
    return d->thumbnails;
}

/// Sets the thumbnail references.
void QXmppFileMetadata::setThumbnails(const QVector<QXmppThumbnail> &thumbnail)
{
    d->thumbnails = thumbnail;
}

/// Returns the width of the image or video.
std::optional<uint32_t> QXmppFileMetadata::width() const
{
    return d->width;
}

/// Sets the width of the image or video.
void QXmppFileMetadata::setWidth(std::optional<uint32_t> width)
{
    d->width = width;
}
