// SPDX-FileCopyrightText: 2012 Jeremy Lainé <jeremy.laine@m4x.org>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#include "QXmppBookmarkManager.h"

#include "QXmppBookmarkSet.h"
#include "QXmppClient.h"
#include "QXmppConstants_p.h"
#include "QXmppIq.h"
#include "QXmppUtils.h"

#include <QDomElement>

// The QXmppPrivateStorageIq class represents an XML private storage IQ
// as defined by XEP-0049: Private XML Storage.
//
// FIXME: currently, we only handle bookmarks

class QXmppPrivateStorageIq : public QXmppIq
{
public:
    QXmppBookmarkSet bookmarks() const;
    void setBookmarks(const QXmppBookmarkSet &bookmark);

    static bool isPrivateStorageIq(const QDomElement &element);

protected:
    void parseElementFromChild(const QDomElement &element) override;
    void toXmlElementFromChild(QXmlStreamWriter *writer) const override;

private:
    QXmppBookmarkSet m_bookmarks;
};

QXmppBookmarkSet QXmppPrivateStorageIq::bookmarks() const
{
    return m_bookmarks;
}

void QXmppPrivateStorageIq::setBookmarks(const QXmppBookmarkSet &bookmarks)
{
    m_bookmarks = bookmarks;
}

bool QXmppPrivateStorageIq::isPrivateStorageIq(const QDomElement &element)
{
    const QDomElement queryElement = element.firstChildElement("query");
    return queryElement.namespaceURI() == ns_private &&
        QXmppBookmarkSet::isBookmarkSet(queryElement.firstChildElement());
}

void QXmppPrivateStorageIq::parseElementFromChild(const QDomElement &element)
{
    const QDomElement queryElement = element.firstChildElement("query");
    m_bookmarks.parse(queryElement.firstChildElement());
}

void QXmppPrivateStorageIq::toXmlElementFromChild(QXmlStreamWriter *writer) const
{
    writer->writeStartElement("query");
    writer->writeDefaultNamespace(ns_private);
    m_bookmarks.toXml(writer);
    writer->writeEndElement();
}

class QXmppBookmarkManagerPrivate
{
public:
    QXmppBookmarkSet bookmarks;
    QXmppBookmarkSet pendingBookmarks;
    QString pendingId;
    bool bookmarksReceived;
};

///
/// Constructs a new bookmark manager.
///
QXmppBookmarkManager::QXmppBookmarkManager()
    : d(new QXmppBookmarkManagerPrivate)
{
    d->bookmarksReceived = false;
}

QXmppBookmarkManager::~QXmppBookmarkManager() = default;

/// Returns true if the bookmarks have been received from the server,
/// false otherwise.
///
bool QXmppBookmarkManager::areBookmarksReceived() const
{
    return d->bookmarksReceived;
}

/// Returns the bookmarks stored on the server.
///
/// Before calling this method, check that the bookmarks
/// have indeed been received by calling areBookmarksReceived().
///

QXmppBookmarkSet QXmppBookmarkManager::bookmarks() const
{
    return d->bookmarks;
}

/// Stores the bookmarks on the server.
///
/// \param bookmarks

bool QXmppBookmarkManager::setBookmarks(const QXmppBookmarkSet &bookmarks)
{
    QXmppPrivateStorageIq iq;
    iq.setType(QXmppIq::Set);
    iq.setBookmarks(bookmarks);
    if (!client()->sendPacket(iq)) {
        return false;
    }

    d->pendingBookmarks = bookmarks;
    d->pendingId = iq.id();
    return true;
}

/// \cond
void QXmppBookmarkManager::setClient(QXmppClient *client)
{

    QXmppClientExtension::setClient(client);

    connect(client, &QXmppClient::connected,
            this, &QXmppBookmarkManager::slotConnected);

    connect(client, &QXmppClient::disconnected,
            this, &QXmppBookmarkManager::slotDisconnected);
}

bool QXmppBookmarkManager::handleStanza(const QDomElement &stanza)
{
    if (stanza.tagName() == "iq") {
        if (QXmppPrivateStorageIq::isPrivateStorageIq(stanza)) {
            QXmppPrivateStorageIq iq;
            iq.parse(stanza);

            if (iq.type() == QXmppIq::Result) {
                d->bookmarks = iq.bookmarks();
                d->bookmarksReceived = true;
                Q_EMIT bookmarksReceived(d->bookmarks);
            }
            return true;
        } else if (!d->pendingId.isEmpty() && stanza.attribute("id") == d->pendingId) {
            QXmppIq iq;
            iq.parse(stanza);
            if (iq.type() == QXmppIq::Result) {
                d->bookmarks = d->pendingBookmarks;
                Q_EMIT bookmarksReceived(d->bookmarks);
            }
            d->pendingId = QString();
            return true;
        }
    }
    return false;
}
/// \endcond

void QXmppBookmarkManager::slotConnected()
{
    QXmppPrivateStorageIq iq;
    iq.setType(QXmppIq::Get);
    client()->sendPacket(iq);
}

void QXmppBookmarkManager::slotDisconnected()
{
    d->bookmarks = QXmppBookmarkSet();
    d->bookmarksReceived = false;
}
