/*
 * Copyright (C) 2008-2021 The QXmpp developers
 *
 * Author:
 *  Linus Jahn
 *
 * Source:
 *  https://github.com/qxmpp-project/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "QXmppMixIq.h"

#include "QXmppConstants_p.h"
#include "QXmppDataForm.h"
#include "QXmppUtils.h"

#include <QDomElement>
#include <QSharedData>

static const QStringList MIX_ACTION_TYPES = {
    QString(),
    QStringLiteral("client-join"),
    QStringLiteral("client-leave"),
    QStringLiteral("join"),
    QStringLiteral("leave"),
    QStringLiteral("update-subscription"),
    QStringLiteral("setnick"),
    QStringLiteral("create"),
    QStringLiteral("destroy")
};

class QXmppMixIqPrivate : public QSharedData
{
public:
    QString jid;
    QString channelName;
    QStringList nodes;
    QString nick;
    QXmppMixIq::Type actionType = QXmppMixIq::None;
};

QXmppMixIq::QXmppMixIq()
    : d(new QXmppMixIqPrivate)
{
}

QXmppMixIq::QXmppMixIq(const QXmppMixIq&) = default;

QXmppMixIq::~QXmppMixIq() = default;

QXmppMixIq& QXmppMixIq::operator=(const QXmppMixIq&) = default;

/// Returns the channel JID. It also contains a participant id for Join/
/// ClientJoin results.

QString QXmppMixIq::jid() const
{
    return d->jid;
}

/// Sets the channel JID. For results of Join/ClientJoin queries this also
/// needs to contain a participant id.

void QXmppMixIq::setJid(const QString& jid)
{
    d->jid = jid;
}

/// Returns the channel name (the name part of the channel JID). This may still
/// be empty, if a JID was set.

QString QXmppMixIq::channelName() const
{
    return d->channelName;
}

/// Sets the channel name for creating/destroying specific channels. When you
/// create a new channel, this can also be left empty to let the server
/// generate a name.

void QXmppMixIq::setChannelName(const QString& channelName)
{
    d->channelName = channelName;
}

/// Returns the list of nodes to subscribe to.

QStringList QXmppMixIq::nodes() const
{
    return d->nodes;
}

/// Sets the nodes to subscribe to. Note that for UpdateSubscription queries
/// you only need to include the new subscriptions.

void QXmppMixIq::setNodes(const QStringList& nodes)
{
    d->nodes = nodes;
}

/// Returns the user's nickname in the channel.

QString QXmppMixIq::nick() const
{
    return d->nick;
}

/// Sets the nickname for the channel.

void QXmppMixIq::setNick(const QString& nick)
{
    d->nick = nick;
}

/// Returns the MIX channel action type.

QXmppMixIq::Type QXmppMixIq::actionType() const
{
    return d->actionType;
}

/// Sets the channel action.

void QXmppMixIq::setActionType(QXmppMixIq::Type type)
{
    d->actionType = type;
}

/// \cond
bool QXmppMixIq::isMixIq(const QDomElement& element)
{
    const QDomElement& child = element.firstChildElement();
    return !child.isNull() && (child.namespaceURI() == ns_mix || child.namespaceURI() == ns_mix_pam);
}

void QXmppMixIq::parseElementFromChild(const QDomElement& element)
{
    QDomElement child = element.firstChildElement();
    // determine action type
    d->actionType = (QXmppMixIq::Type)MIX_ACTION_TYPES.indexOf(child.tagName());

    if (child.namespaceURI() == ns_mix_pam) {
        if (child.hasAttribute(QStringLiteral("channel")))
            d->jid = child.attribute(QStringLiteral("channel"));

        child = child.firstChildElement();
    }

    if (!child.isNull() && child.namespaceURI() == ns_mix) {
        if (child.hasAttribute(QStringLiteral("jid")))
            d->jid = child.attribute(QStringLiteral("jid"));
        if (child.hasAttribute(QStringLiteral("channel")))
            d->channelName = child.attribute(QStringLiteral("channel"));

        QDomElement subChild = child.firstChildElement();
        while (!subChild.isNull()) {
            if (subChild.tagName() == QStringLiteral("subscribe"))
                d->nodes << subChild.attribute(QStringLiteral("node"));
            else if (subChild.tagName() == QStringLiteral("nick"))
                d->nick = subChild.text();

            subChild = subChild.nextSiblingElement();
        }
    }
}

void QXmppMixIq::toXmlElementFromChild(QXmlStreamWriter* writer) const
{
    if (d->actionType == None)
        return;

    writer->writeStartElement(MIX_ACTION_TYPES.at(d->actionType));
    if (d->actionType == ClientJoin || d->actionType == ClientLeave) {
        writer->writeDefaultNamespace(ns_mix_pam);
        if (type() == Set)
            helperToXmlAddAttribute(writer, QStringLiteral("channel"), d->jid);

        if (d->actionType == ClientJoin)
            writer->writeStartElement(QStringLiteral("join"));
        else if (d->actionType == ClientLeave)
            writer->writeStartElement(QStringLiteral("leave"));
    }

    writer->writeDefaultNamespace(ns_mix);
    helperToXmlAddAttribute(writer, QStringLiteral("channel"), d->channelName);
    if (type() == Result)
        helperToXmlAddAttribute(writer, QStringLiteral("jid"), d->jid);

    for (const auto& node : d->nodes) {
        writer->writeStartElement(QStringLiteral("subscribe"));
        writer->writeAttribute(QStringLiteral("node"), node);
        writer->writeEndElement();
    }
    if (!d->nick.isEmpty())
        writer->writeTextElement(QStringLiteral("nick"), d->nick);

    writer->writeEndElement();
    if (d->actionType == ClientJoin || d->actionType == ClientLeave)
        writer->writeEndElement();
}
/// \endcond
