/*
 * contactlistmodelselection.cpp - stores persistent contact list selections
 * Copyright (C) 2008-2010  Yandex LLC (Michail Pishchagin)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#include "contactlistmodelselection.h"

#include "contactlistitem.h"
#include "iris/xmpp_xmlcommon.h"
#include "psiaccount.h"
#include "psicontact.h"
#include "textutil.h"

#include <QDomElement>
#include <QStringList>

static const QString psiRosterSelectionMimeType = "application/psi-roster-selection";

ContactListModelSelection::ContactListModelSelection(QList<ContactListItem *> items) : QMimeData(), mimeData_(nullptr)
{
    QDomDocument doc;
    QDomElement  root = doc.createElement("items");
    root.setAttribute("version", "2.0");
    doc.appendChild(root);

    QStringList jids;

    for (auto *item : items) {
        Q_ASSERT(item);

        switch (item->type()) {
        case ContactListItem::Type::ContactType: {
            PsiContact *contact = item->contact();
            QDomElement tag     = textTag(&doc, "contact", contact->jid().full());
            tag.setAttribute("account", contact->account()->id());
            tag.setAttribute("group", item->parent()->isGroup() ? item->parent()->internalName() : "");
            root.appendChild(tag);

            jids << contact->jid().full();
            break;
        }

        case ContactListItem::Type::GroupType: {
            // if group->fullName() consists only of whitespace when we'll try
            // to read it back we'll get an empty string, so we're using CDATA
            // QDomElement tag = textTag(&doc, "group", group->fullName());
            QDomElement tag  = doc.createElement("group");
            QDomText    text = doc.createCDATASection(TextUtil::escape(item->internalName()));
            tag.appendChild(text);

            root.appendChild(tag);

            jids << item->name();
            break;
        }

        case ContactListItem::Type::AccountType: {
            QDomElement tag = doc.createElement("account");
            tag.setAttribute("id", item->account()->id());
            root.appendChild(tag);

            jids << item->name();
            break;
        }

        default:
            break;
        }
    }

    setText(jids.join(", "));
    setData(psiRosterSelectionMimeType, doc.toByteArray());
}

ContactListModelSelection::ContactListModelSelection(const QMimeData *mimeData) : QMimeData(), mimeData_(mimeData)
{
    const ContactListModelSelection *other = qobject_cast<const ContactListModelSelection *>(mimeData_);
    if (other) {
        mimeData_ = other->mimeData();
    }
}

const QString &ContactListModelSelection::mimeType() { return psiRosterSelectionMimeType; }

QDomElement ContactListModelSelection::rootElementFor(const QMimeData *mimeData) const
{
    QDomDocument doc;
    if (!doc.setContent(mimeData->data(psiRosterSelectionMimeType)))
        return QDomElement();

    QDomElement root = doc.documentElement();
    if (root.tagName() != "items" || root.attribute("version") != "2.0")
        return QDomElement();

    return root;
}

bool ContactListModelSelection::haveRosterSelectionIn(const QMimeData *mimeData) const
{
    return !rootElementFor(mimeData).isNull();
}

QList<ContactListModelSelection::Contact> ContactListModelSelection::contactsFor(const QMimeData *mimeData) const
{
    QList<Contact> result;
    QDomElement    root = rootElementFor(mimeData);
    if (root.isNull())
        return result;

    for (QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
        QDomElement e = n.toElement();
        if (e.isNull())
            continue;

        if (e.tagName() == "contact") {
            Jid jid = tagContent(e);
            result << Contact(jid.full(), e.attribute("account"), e.attribute("group"));
        }
    }

    return result;
}

QList<ContactListModelSelection::Group> ContactListModelSelection::groupsFor(const QMimeData *mimeData) const
{
    QList<Group> result;
    QDomElement  root = rootElementFor(mimeData);
    if (root.isNull())
        return result;

    for (QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
        QDomElement e = n.toElement();
        if (e.isNull())
            continue;

        if (e.tagName() == "group") {
            QString groupName = TextUtil::unescape(tagContent(e));
            result << Group(groupName);
        }
    }

    return result;
}

QList<ContactListModelSelection::Account> ContactListModelSelection::accountsFor(const QMimeData *mimeData) const
{
    QList<Account> result;
    QDomElement    root = rootElementFor(mimeData);
    if (root.isNull())
        return result;

    for (QDomNode n = root.firstChild(); !n.isNull(); n = n.nextSibling()) {
        QDomElement e = n.toElement();
        if (e.isNull())
            continue;

        if (e.tagName() == "account") {
            result << Account(e.attribute("id"));
        }
    }

    return result;
}

const QMimeData *ContactListModelSelection::mimeData() const { return mimeData_ ? mimeData_ : this; }

bool ContactListModelSelection::haveRosterSelection() const { return haveRosterSelectionIn(mimeData()); }

QList<ContactListModelSelection::Contact> ContactListModelSelection::contacts() const
{
    return contactsFor(mimeData());
}

QList<ContactListModelSelection::Group> ContactListModelSelection::groups() const { return groupsFor(mimeData()); }

QList<ContactListModelSelection::Account> ContactListModelSelection::accounts() const
{
    return accountsFor(mimeData());
}

bool ContactListModelSelection::isMultiSelection() const { return (contacts().count() + groups().count()) > 1; }

void ContactListModelSelection::debugSelection(const QMimeData *data, const QString &name)
{
    qWarning("*** debugSelection %s", qPrintable(name));
    ContactListModelSelection selection(data);
    const auto               &contacts = selection.contacts();
    for (const ContactListModelSelection::Contact &c : contacts) {
        qWarning("\tc: '%s' group: '%s' account: '%s'", qPrintable(c.jid), qPrintable(c.group), qPrintable(c.account));
    }
    const auto &groups = selection.groups();
    for (const ContactListModelSelection::Group &g : groups) {
        qWarning("\tg: '%s'", qPrintable(g.fullName));
    }
    const auto &accs = selection.accounts();
    for (const ContactListModelSelection::Account &a : accs) {
        qWarning("\ta: '%s'", qPrintable(a.id));
    }
}
