// 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;
    QList<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;
    QList<QXmppThumbnail> thumbnails;
    std::optional<uint32_t> width;
};

/*!
    \class QXmppFileMetadata
    \inmodule QXmpp

    File metadata from \xep{0446}{File metadata element}.

    \since QXmpp 1.5

    \a info.
*/

/*!
    \brief Creates a QXmppFileMetadata object from information from QFileInfo.

    Sets the filename, file size, media type and the last modification date.

    \a info.
*/
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)

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<QList<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<QList<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 },
    });
}

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

/*!
    Sets when the file was last modified

    \a date.
*/
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 \a description of the file */
void QXmppFileMetadata::setDescription(const std::optional<QString> &description)
{
    d->desc = description;
}

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

/*! Sets the \a hashes of the file */
void QXmppFileMetadata::setHashes(const QList<QXmppHash> &hashes)
{
    d->hashes = hashes;
}

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

/*! Sets the \a 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 \a 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

    \a mediaType.
*/
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

    \a name.
*/
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 \a size of the file in bytes */
void QXmppFileMetadata::setSize(std::optional<uint64_t> size)
{
    d->size = size;
}

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

/*! Sets the \a thumbnail references. */
void QXmppFileMetadata::setThumbnails(const QList<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 \a width of the image or video. */
void QXmppFileMetadata::setWidth(std::optional<uint32_t> width)
{
    d->width = width;
}
