// SPDX-FileCopyrightText: 2017 Niels Ole Salscheider <niels_ole@salscheider-online.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#ifndef QXMPPSTREAMMANAGEMENT_P_H
#define QXMPPSTREAMMANAGEMENT_P_H

#include "QXmppConstants_p.h"
#include "QXmppGlobal.h"
#include "QXmppPacket_p.h"
#include "QXmppSendResult.h"
#include "QXmppStanza.h"
#include "QXmppTask.h"

#include <deque>

#include <QDomDocument>
#include <QXmlStreamWriter>

namespace QXmpp::Private {
class XmlWriter;
class XmppSocket;
}  // namespace QXmpp::Private

//
//  W A R N I N G
//  -------------
//
// This file is not part of the QXmpp API.  It exists for the convenience
// of the QXmppIncomingClient and QXmppOutgoingClient classes.
//
// This header file may change from version to version without notice,
// or even be removed.
//
// We mean it.
//

namespace QXmpp::Private {

struct SmFeature {
    static constexpr std::tuple XmlTag = { u"sm", QXmpp::Private::ns_stream_management };
};

struct SmEnable {
    static constexpr std::tuple XmlTag = { u"enable", QXmpp::Private::ns_stream_management };
    static std::optional<SmEnable> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    bool resume = false;
    quint64 max = 0;
};

struct SmEnabled {
    static constexpr std::tuple XmlTag = { u"enabled", QXmpp::Private::ns_stream_management };
    static std::optional<SmEnabled> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    bool resume = false;
    QString id;
    quint64 max = 0;
    QString location;
};

struct SmResume {
    static constexpr std::tuple XmlTag = { u"resume", QXmpp::Private::ns_stream_management };
    static std::optional<SmResume> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    quint32 h = 0;
    QString previd;
};

struct SmResumed {
    static constexpr std::tuple XmlTag = { u"resumed", QXmpp::Private::ns_stream_management };
    static std::optional<SmResumed> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    quint32 h = 0;
    QString previd;
};

struct SmFailed {
    static constexpr std::tuple XmlTag = { u"failed", QXmpp::Private::ns_stream_management };
    static std::optional<SmFailed> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    std::optional<QXmppStanza::Error::Condition> error;
};

struct SmAck {
    static constexpr std::tuple XmlTag = { u"a", QXmpp::Private::ns_stream_management };
    static std::optional<SmAck> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;

    quint32 seqNo = 0;
};

struct SmRequest {
    static constexpr std::tuple XmlTag = { u"r", QXmpp::Private::ns_stream_management };
    static std::optional<SmRequest> fromDom(const QDomElement &);
    void toXml(XmlWriter &w) const;
};

//
// This manager handles sending and receiving of stream management acks.
// Enabling of stream management and stream resumption is done in the C2sStreamManager.
//
class StreamAckManager
{
public:
    explicit StreamAckManager(XmppSocket &socket);

    bool enabled() const { return m_enabled; }
    unsigned int lastIncomingSequenceNumber() const { return m_lastIncomingSequenceNumber; }

    void handlePacketSent(QXmppPacket &packet, bool sentData);
    bool handleStanza(const QDomElement &stanza);
    void onSessionClosed();

    void resetCache();
    void enableStreamManagement(bool resetSequenceNumber);
    void setAcknowledgedSequenceNumber(unsigned int sequenceNumber);

    QXmppTask<QXmpp::SendResult> send(QXmppPacket &&);
    bool sendPacketCompat(QXmppPacket &&);
    std::tuple<bool, QXmppTask<QXmpp::SendResult>> internalSend(QXmppPacket &&);

    void sendAcknowledgementRequest();

private:
    void handleAcknowledgement(SmAck ack);

    void sendAcknowledgement();

    QXmpp::Private::XmppSocket &socket;

    bool m_enabled = false;
    std::deque<std::pair<unsigned int, QXmppPacket>> m_unacknowledgedStanzas;
    unsigned int m_lastOutgoingSequenceNumber = 0;
    unsigned int m_lastIncomingSequenceNumber = 0;
};

}  // namespace QXmpp::Private

#endif
