// SPDX-FileCopyrightText: 2009 Manjeet Dahiya <manjeetdahiya@gmail.com>
// SPDX-FileCopyrightText: 2010 Jeremy Lainé <jeremy.laine@m4x.org>
// SPDX-FileCopyrightText: 2018 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2020 Melvin Keskin <melvo@olomono.de>
// SPDX-FileCopyrightText: 2023 Tibor Csötönyi <work@taibsu.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#ifndef QXMPPMESSAGE_H
#define QXMPPMESSAGE_H

#include "QXmppFallback.h"
#include "QXmppFileShare.h"
#include "QXmppMucData.h"
#include "QXmppStanza.h"
#include "QXmppXmlExtensions.h"

#include <optional>

// Required for source compatibility
#include <QDateTime>

class QXmppMessagePrivate;
class QXmppBitsOfBinaryDataList;
class QXmppFallback;
class QXmppJingleMessageInitiationElement;
class QXmppMessageReaction;
class QXmppMixInvitation;
class QXmppMucVoiceRequest;
#ifdef BUILD_OMEMO
class QXmppOmemoElement;
#endif
class QXmppTrustMessageElement;
class QXmppOutOfBandUrl;
class QXmppCallInviteElement;

namespace QXmpp {

/*!
    \inmodule QXmpp

    \brief Message reply as defined in \xep{0461}{Message Replies}.

    \since QXmpp 1.9
*/
struct Reply {
    /*!
        Full JID of the sender of the referenced message (optional).
        In 1:1 chats the bare JID may also be used.
    */
    QString to;
    /*! ID of the referenced message. */
    QString id;
};

}  // namespace QXmpp

struct QXmppStanzaId {
    /*! Identifier of the stanza element */
    QString id;
    /*! JID of the generating entity */
    QString by;
};

/*!
    \inmodule QXmpp

    \brief The QXmppMessage class represents an XMPP message.

    \ingroup Stanzas
*/
class QXMPP_EXPORT QXmppMessage : public QXmppStanza
{
public:
#if QXMPP_DEPRECATED_SINCE(1, 5)
    using EncryptionMethod = QXmpp::EncryptionMethod;

    static const EncryptionMethod NoEncryption = EncryptionMethod::NoEncryption;
    static const EncryptionMethod UnknownEncryption = EncryptionMethod::UnknownEncryption;
    static const EncryptionMethod OTR = EncryptionMethod::Otr;
    static const EncryptionMethod LegacyOpenPGP = EncryptionMethod::LegacyOpenPgp;
    static const EncryptionMethod OX = EncryptionMethod::Ox;
    static const EncryptionMethod OMEMO = EncryptionMethod::Omemo0;
#endif

    /*!
        This enum describes a message type.

        \value Error
        \value Normal
        \value Chat
        \value GroupChat
        \value Headline
    */
    enum Type {
        Error = 0,
        Normal,
        Chat,
        GroupChat,
        Headline
    };

    /*!
        This enum describes a chat state as defined by \xep{0085}{Chat State
        Notifications}.

        \since QXmpp 0.2

        \value None The message does not contain any chat state information.
        \value Active User is actively participating in the chat session.
        \value Inactive User has not been actively participating in the chat session.
        \value Gone User has effectively ended their participation in the chat session.
        \value Composing User is composing a message.
        \value Paused User had been composing but now has stopped.
    */
    enum State {
        None = 0,
        Active,
        Inactive,
        Gone,
        Composing,
        Paused
    };

    /*!
        This enum describes a chat marker as defined by \xep{0333}{Chat Markers}.

        \since QXmpp 0.8.1

        \value NoMarker
        \value Received
        \value Displayed
        \value Acknowledged
    */
    enum Marker {
        NoMarker = 0,
        Received,
        Displayed,
        Acknowledged
    };

    /*!
        \xep{0334}{Message Processing Hints}

        \since QXmpp 1.1

        \value NoPermanentStore Do not allow permanent storage.
        \value NoStore Do not store at all.
        \value NoCopy Do not copy the message.
        \value Store Do store the message.
    */
    enum Hint {
        NoPermanentStore = 1 << 0,
        NoStore = 1 << 1,
        NoCopy = 1 << 2,
        Store = 1 << 3
    };
    Q_DECLARE_FLAGS(Hints, Hint)

    QXmppMessage(const QString &from = QString(), const QString &to = QString(),
                 const QString &body = QString(), const QString &thread = QString());

    QXmppMessage(const QXmppMessage &other);
    QXmppMessage(QXmppMessage &&);
    ~QXmppMessage() override;

    QXmppMessage &operator=(const QXmppMessage &other);
    QXmppMessage &operator=(QXmppMessage &&);

    bool isXmppStanza() const override;

    QString body() const;
    void setBody(const QString &);

    QString e2eeFallbackBody() const;
    void setE2eeFallbackBody(const QString &fallbackBody);

    QString subject() const;
    bool hasSubject() const;
    void setSubject(std::optional<QString>);

    QString thread() const;
    void setThread(const QString &);

    QString parentThread() const;
    void setParentThread(const QString &);

    QXmppMessage::Type type() const;
    void setType(QXmppMessage::Type);

    // XEP-0066: Out of Band Data
    QString outOfBandUrl() const;
    void setOutOfBandUrl(const QString &);

    QVector<QXmppOutOfBandUrl> outOfBandUrls() const;
    void setOutOfBandUrls(const QVector<QXmppOutOfBandUrl> &urls);

    // XEP-0071: XHTML-IM
    QString xhtml() const;
    void setXhtml(const QString &xhtml);

    // XEP-0085: Chat State Notifications
    QXmppMessage::State state() const;
    void setState(QXmppMessage::State);

    // XEP-0091: Legacy Delayed Delivery | XEP-0203: Delayed Delivery
    QDateTime stamp() const;
    void setStamp(const QDateTime &stamp);

    // XEP-0184: Message Delivery Receipts
    bool isReceiptRequested() const;
    void setReceiptRequested(bool requested);

    QString receiptId() const;
    void setReceiptId(const QString &id);

    // XEP-0224: Attention
    bool isAttentionRequested() const;
    void setAttentionRequested(bool requested);

    // XEP-0231: Bits of Binary
    QXmppBitsOfBinaryDataList bitsOfBinaryData() const;
    QXmppBitsOfBinaryDataList &bitsOfBinaryData();
    void setBitsOfBinaryData(const QXmppBitsOfBinaryDataList &bitsOfBinaryData);

    // XEP-0245: The /me Command
    static bool isSlashMeCommand(const QString &body);
    bool isSlashMeCommand() const;
    static QString slashMeCommandText(const QString &body);
    QString slashMeCommandText() const;

    // XEP-0045: Multi-User Chat (since QXmpp 1.16)
    QList<uint32_t> mucStatusCodes() const;
    void setMucStatusCodes(const QList<uint32_t> &codes);
    std::optional<QXmppMucVoiceRequest> mucVoiceRequest() const;
    void setMucVoiceRequest(std::optional<QXmppMucVoiceRequest> request);
    std::optional<QXmpp::Muc::UserQuery> mucUserQuery() const;
    void setMucUserQuery(std::optional<QXmpp::Muc::UserQuery> element);

    // XEP-0249: Direct MUC Invitations
    std::optional<QXmpp::Muc::DirectInvitation> mucDirectInvitation() const;
    void setMucDirectInvitation(std::optional<QXmpp::Muc::DirectInvitation> invitation);

    [[deprecated("Use mucDirectInvitation()")]]
    QString mucInvitationJid() const;
    [[deprecated("Use setMucDirectInvitation()")]]
    void setMucInvitationJid(const QString &jid);

    [[deprecated("Use mucDirectInvitation()")]]
    QString mucInvitationPassword() const;
    [[deprecated("Use setMucDirectInvitation()")]]
    void setMucInvitationPassword(const QString &password);

    [[deprecated("Use mucDirectInvitation()")]]
    QString mucInvitationReason() const;
    [[deprecated("Use setMucDirectInvitation()")]]
    void setMucInvitationReason(const QString &reason);

    // XEP-0280: Message Carbons
    bool isPrivate() const;
    void setPrivate(const bool);
    bool isCarbonForwarded() const;
    void setCarbonForwarded(bool);

    // XEP-0308: Last Message Correction
    QString replaceId() const;
    void setReplaceId(const QString &);

    // XEP-0333: Chat State Markers
    bool isMarkable() const;
    void setMarkable(const bool);

    QString markedId() const;
    void setMarkerId(const QString &);

    QString markedThread() const;
    void setMarkedThread(const QString &);

    Marker marker() const;
    void setMarker(const Marker);

    // XEP-0334: Message Processing Hints
    bool hasHint(const Hint hint) const;
    void addHint(const Hint hint);
    void removeHint(const Hint hint);
    void removeAllHints();

    // XEP-0353: Jingle Message Initiation
    std::optional<QXmppJingleMessageInitiationElement> jingleMessageInitiationElement() const;
    void setJingleMessageInitiationElement(const std::optional<QXmppJingleMessageInitiationElement> &jingleMessageInitiationElement);

    // XEP-0359: Unique and Stable Stanza IDs
#if QXMPP_DEPRECATED_SINCE(1, 8)
    [[deprecated("Use stanzaIds() instead.")]]
    QString stanzaId() const;
    [[deprecated("Use setStanzaIds() instead.")]]
    void setStanzaId(const QString &id);

    [[deprecated("Use stanzaIds() instead.")]]
    QString stanzaIdBy() const;
    [[deprecated("Use setStanzaIds() instead.")]]
    void setStanzaIdBy(const QString &id);
#endif

    QVector<QXmppStanzaId> stanzaIds() const;
    void setStanzaIds(const QVector<QXmppStanzaId> &);

    QString originId() const;
    void setOriginId(const QString &id);

    // XEP-0367: Message Attaching
    QString attachId() const;
    void setAttachId(const QString &);

    // XEP-0369: Mediated Information eXchange (MIX)
    QString mixParticipantId() const;

    QString mixUserJid() const;
    void setMixUserJid(const QString &);

    QString mixUserNick() const;
    void setMixUserNick(const QString &);

    // XEP-0380: Explicit Message Encryption
    QXmpp::EncryptionMethod encryptionMethod() const;
    void setEncryptionMethod(QXmpp::EncryptionMethod);
    QString encryptionMethodNs() const;
    void setEncryptionMethodNs(const QString &);

    QString encryptionName() const;
    void setEncryptionName(const QString &);

    // XEP-0382: Spoiler messages
    bool isSpoiler() const;
    void setIsSpoiler(bool);

    QString spoilerHint() const;
    void setSpoilerHint(const QString &);

    // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities
    std::optional<QXmppMixInvitation> mixInvitation() const;
    void setMixInvitation(const std::optional<QXmppMixInvitation> &mixInvitation);

    // XEP-0421: Occupant identifiers for semi-anonymous MUCs
    QString mucOccupantId() const;
    void setMucOccupantId(const QString &occupantId);

    // XEP-0428: Fallback Indication
#if QXMPP_DEPRECATED_SINCE(1, 7)
    [[deprecated("Use fallbackMarkers()")]]
    bool isFallback() const;
    [[deprecated("Use setFallbackMarkers()")]]
    void setIsFallback(bool isFallback);
#endif
    const QVector<QXmppFallback> &fallbackMarkers() const;
    void setFallbackMarkers(const QVector<QXmppFallback> &);
    QString readFallbackRemovedText(QXmppFallback::Element element, const QVector<QString> &supportedNamespaces) const;
    QString readFallbackText(QXmppFallback::Element element, QStringView forNamespace) const;

    // XEP-0434: Trust Messages (TM)
    std::optional<QXmppTrustMessageElement> trustMessageElement() const;
    void setTrustMessageElement(const std::optional<QXmppTrustMessageElement> &trustMessageElement);

    // XEP-0444: Message Reactions
    std::optional<QXmppMessageReaction> reaction() const;
    void setReaction(const std::optional<QXmppMessageReaction> &reaction);

    // XEP-0447: Stateless file sharing
    const QVector<QXmppFileShare> &sharedFiles() const;
    void setSharedFiles(const QVector<QXmppFileShare> &sharedFiles);
    QVector<QXmppFileSourcesAttachment> fileSourcesAttachments() const;
    void setFileSourcesAttachments(const QVector<QXmppFileSourcesAttachment> &);

    // XEP-0461: Message Replies
    std::optional<QXmpp::Reply> reply() const;
    void setReply(const std::optional<QXmpp::Reply> &);
    QString readReplyQuoteFromBody() const;

    // XEP-0482: Call Invites
    std::optional<QXmppCallInviteElement> callInviteElement() const;
    void setCallInviteElement(std::optional<QXmppCallInviteElement> callInviteElement);

    QXmpp::Xml::Extensions &extensions();
    const QXmpp::Xml::Extensions &extensions() const;

#ifdef BUILD_OMEMO
    // XEP-0384: OMEMO Encryption
    std::optional<QXmppOmemoElement> omemoElement() const;
    void setOmemoElement(const std::optional<QXmppOmemoElement> &omemoElement);
#endif

    void parse(const QDomElement &element) override final;
    virtual void parse(const QDomElement &element, QXmpp::SceMode);
    void toXml(QXmlStreamWriter *writer) const override final;
    virtual void toXml(QXmlStreamWriter *writer, QXmpp::SceMode) const;

    void parseExtensions(const QDomElement &element, QXmpp::SceMode sceMode);
    virtual bool parseExtension(const QDomElement &element, QXmpp::SceMode);
    virtual void serializeExtensions(QXmlStreamWriter *writer, QXmpp::SceMode, const QString &baseNamespace = {}) const;

private:
    QSharedDataPointer<QXmppMessagePrivate> d;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(QXmppMessage::Hints)
Q_DECLARE_METATYPE(QXmppMessage)

#endif  // QXMPPMESSAGE_H
