/*
 * QtTapioca, the Tapioca Qt4 Client Library
 * Copyright (C) 2006 by INdT
 *  @author Abner Jose de Faria Silva <abner.silva@indt.org.br>
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA
 */

#include "config.h"

#include <QtCore/QDebug>
#include "QtTapioca/ContactGroup"
#include "QtTapioca/Connection"
#include "QtTapioca/ContactList"
#include "QtTapioca/Handle"

#include <QtTelepathy/Client/ChannelGroupInterface>

namespace QtTapioca {

class ContactGroupPrivate {
public:
    ContactGroupPrivate(Connection *connection,
                        const QString &serviceName,
                        const QString &objPath,
                        Channel * chan)
        : conn(connection), channel(chan)
    {
        telepathyIChannelGroup = new org::freedesktop::Telepathy::ChannelGroupInterface(serviceName, objPath, QDBusConnection::sessionBus());

        if (telepathyIChannelGroup)
            flags |= telepathyIChannelGroup->GetGroupFlags();
    }
    ~ContactGroupPrivate()
    {
        delete telepathyIChannelGroup;
    }

    uint flags;
    Connection *conn;
    Channel * channel;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyIChannelGroup;
};

}

using namespace QtTapioca;

ContactGroup::ContactGroup(Connection *connection, const QString &serviceName, const QString &objPath, Channel *channel)
    : d(new ContactGroupPrivate(connection, serviceName, objPath, channel))
{
    Q_ASSERT(d);

    QObject::connect(d->telepathyIChannelGroup, SIGNAL(GroupFlagsChanged(uint, uint)), this, SLOT(onGroupFlagsChanged(uint, uint)));
    QObject::connect(d->telepathyIChannelGroup, SIGNAL(MembersChanged(const QString&, const QList<uint>&,
                                                                      const QList<uint>&, const QList<uint> &,
                                                                      const QList<uint>&, uint, uint)),
                     this,
                     SLOT(onMembersChanged(const QString&, const QList<uint>&,
                                           const QList<uint>&, const QList<uint>&,
                                           const QList<uint>&, uint, uint)));
}

ContactGroup::~ContactGroup()
{
    delete d;
}

void ContactGroup::inviteContact(Contact *contact)
{
    Q_ASSERT(d->telepathyIChannelGroup);

    if (!contact)
        return;

    if (canInvite()) {
        QList<uint> contacts;
        contacts << contact->handle()->id();
        QDBusReply<void> reply = d->telepathyIChannelGroup->AddMembers(contacts, "");

        if (!reply.isValid())
            qDebug() << "error adding members:" << reply.error().message();
    }
}

void ContactGroup::expelContact(Contact *contact)
{
    Q_ASSERT(d->telepathyIChannelGroup);

    if (!contact)
        return;

    if (canExpel()) {
        QList<uint> contacts;
        contacts << contact->handle()->id();
        QDBusReply<void> reply = d->telepathyIChannelGroup->RemoveMembers(contacts, "");

        if (!reply.isValid())
            qDebug() << "error removing members:" << reply.error().message();
    }
}

bool ContactGroup::canInvite() const
{
    return (d->flags & FlagCanAdd);
}

bool ContactGroup::canExpel() const
{
    return (d->flags & FlagCanRemove);
}

QList<Contact *> ContactGroup::contacts() const
{
    Q_ASSERT(d->telepathyIChannelGroup);

    return contactsFromContactList(d->telepathyIChannelGroup->GetMembers());
}

QList<Contact *> ContactGroup::pendingContacts() const
{
    Q_ASSERT(d->telepathyIChannelGroup);

    QList<Contact *> lc;
    QDBusReply<QList<uint> > reply = d->telepathyIChannelGroup->GetLocalPendingMembers();

    if (!reply.isValid()) {
        qDebug() << "error getting local pending members:" << reply.error().message();
        return QList<Contact *>();
    }

    lc = contactsFromContactList(reply.value());

    reply = d->telepathyIChannelGroup->GetRemotePendingMembers();

    if (!reply.isValid()) {
        qDebug() << "error getting remote pending members:" << reply.error().message();
        return QList<Contact *>();
    }

    lc += contactsFromContactList(reply.value());

    return lc;
}

Channel * ContactGroup::channel() const { return d->channel; }

QList<Contact *> ContactGroup::contactsFromContactList(QList<uint> ids) const
{
    uint i;
    Contact *contact = NULL;
    QList<Contact *> lc;

    foreach (i, ids) {
        contact = d->conn->contactList()->contact(i);
        if (!contact) {
            contact = d->conn->contactList()->addContact(i);
        }
        Q_ASSERT(0 != contact);
        lc << contact;
        // FIXME: Check if this function should always add a new contact
        // in the contactlist when it's not there.
        //if (contact)
        //    lc << contact;
        //else
        //    qDebug() << "Contact id:" << i << "not found.";

    }

    return lc;
}

void ContactGroup::onGroupFlagsChanged(uint added, uint removed)
{
    //TODO
}

void ContactGroup::onMembersChanged(const QString &message, const QList<uint> &added,
                                    const QList<uint> &removed, const QList<uint> &local_pending,
                                    const QList<uint> &remote_pending, uint actor, uint reason)
{
    QList<Contact *> lc;
    Contact *contact = NULL;

    if (!added.isEmpty()) {
        lc = contactsFromContactList(added);
        if (!lc.isEmpty())
            foreach (contact, lc)
                emit contactEntered(contact);
    }

    if (!removed.isEmpty()) {
        lc = contactsFromContactList(removed);
        if (!lc.isEmpty())
            foreach (contact, lc)
                emit contactLeft(contact);
    }

    if ((!local_pending.isEmpty()) ||
        (!remote_pending.isEmpty())) {
        lc.clear();

        if (!local_pending.isEmpty())
            lc += contactsFromContactList(local_pending);
        if (!remote_pending.isEmpty())
            lc += contactsFromContactList(remote_pending);

        if (!lc.isEmpty())
            foreach(contact, lc)
                emit newPendingContact(contact);
    }
}
