/*
 * QtTapioca, the Tapioca Qt4 Client Library
 * Copyright (C) 2006 by INdT
 *  @author Abner Jose de Faria Silva <abner.silva@indt.org.br>
 *  @author Andre Moreira Magalhaes <andre.magalhaes@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/QMutex>

#include "QtTapioca/Contact"
#include "QtTapioca/Handle"

namespace QtTapioca {

class ContactPrivate {
public:
    ContactPrivate(org::freedesktop::Telepathy::Connection *telepathyConn,
                   org::freedesktop::Telepathy::ChannelGroupInterface *_telepathyGISubscribe,
                   org::freedesktop::Telepathy::ChannelGroupInterface *_telepathyGIPublish,
                   org::freedesktop::Telepathy::ChannelGroupInterface *_telepathyGIHide,
                   org::freedesktop::Telepathy::ChannelGroupInterface *_telepathyGIAllow,
                   org::freedesktop::Telepathy::ChannelGroupInterface *_telepathyGIDeny)
        : hideFrom(false),
          blocked(false),
          subStatus(Contact::NotSubscribed),
          authStatus(Contact::NonExistent),
          telepathyGISubscribe(_telepathyGISubscribe),
          telepathyGIPublish(_telepathyGIPublish),
          telepathyGIHide(_telepathyGIHide),
          telepathyGIAllow(_telepathyGIAllow),
          telepathyGIDeny(_telepathyGIDeny)
    {
        this->telepathyConn = telepathyConn;
    }
    ~ContactPrivate()
    {
    }

    QMutex mutex;
    bool hideFrom;
    bool blocked;
    org::freedesktop::Telepathy::Connection *telepathyConn;
    Contact::SubscriptionStatus subStatus;
    Contact::AuthorizationStatus authStatus;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGISubscribe;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIPublish;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIHide;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIAllow;
    org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIDeny;
};

}

using namespace QtTapioca;

Contact::Contact(org::freedesktop::Telepathy::Connection *telepathyConn,
                 org::freedesktop::Telepathy::ConnectionAvatarsInterface *telepathyIAvatar,
                 org::freedesktop::Telepathy::ConnectionPresenceInterface *telepathyIPresence,
                 org::freedesktop::Telepathy::ConnectionAliasingInterface *telepathyIAliasing,
                 org::freedesktop::Telepathy::ConnectionCapabilitiesInterface *telepathyICapabilities,
                 org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGISubscribe,
                 org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIPublish,
                 org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIHide,
                 org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIAllow,
                 org::freedesktop::Telepathy::ChannelGroupInterface *telepathyGIDeny,
                 Handle *handle,
                 QObject *parent)
    : ContactBase(telepathyConn,
                  telepathyIAvatar,
                  telepathyIPresence,
                  telepathyIAliasing,
                  telepathyICapabilities,
                  handle,
                  parent),
      d(new ContactPrivate(telepathyConn,
                           telepathyGISubscribe,
                           telepathyGIPublish,
                           telepathyGIHide,
                           telepathyGIAllow,
                           telepathyGIDeny))
{
    Q_ASSERT(d);

    if (d->telepathyGISubscribe)
        QObject::connect(d->telepathyGISubscribe, SIGNAL(MembersChanged(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)),
                         this, SLOT(onMembersChangedSubscribe(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)));
    if (d->telepathyGIPublish)
        QObject::connect(d->telepathyGIPublish, SIGNAL(MembersChanged(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)),
                         this, SLOT(onMembersChangedPublish(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)));
    if (d->telepathyGIHide)
        QObject::connect(d->telepathyGIHide, SIGNAL(MembersChanged(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)),
                         this, SLOT(onMembersChangedHide(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)));
    if (d->telepathyGIAllow)
        QObject::connect(d->telepathyGIAllow, SIGNAL(MembersChanged(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)),
                         this, SLOT(onMembersChangedAllow(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)));
    if (d->telepathyGIDeny)
        QObject::connect(d->telepathyGIDeny, SIGNAL(MembersChanged(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)),
                         this, SLOT(onMembersChangedDeny(const QString &, const QList<uint> &, const QList<uint> &, const QList<uint> &, const QList<uint> &, uint, uint)));
}

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

Contact::SubscriptionStatus Contact::subscriptionStatus() const
{
    return d->subStatus;
}

Contact::AuthorizationStatus Contact::authorizationStatus() const
{
    return d->authStatus;
}

bool Contact::isBlocked() const
{
    return d->blocked;
}

bool Contact::hidingFrom() const
{
    return d->hideFrom;
}

void Contact::hideFrom(bool value)
{
    if ((d->hideFrom == value) ||
        (!d->telepathyGIHide))
        return;

    setOnGroup(d->telepathyGIHide, value);
}

void Contact::subscribe(bool value)
{
    if (!d->telepathyGISubscribe)
        return;

    switch (d->subStatus) {
        case Contact::NotSubscribed:
            if (value == true)
                setOnGroup(d->telepathyGISubscribe, value);
            break;
        case Contact::Subscribed:
            if (value == false)
                setOnGroup(d->telepathyGISubscribe, value);
            break;
        case Contact::RemotePending:
            if (value == false)
                setOnGroup(d->telepathyGISubscribe, value);
            break;
        default:
            break;
    }
}

void Contact::authorize(bool value)
{
    if (!d->telepathyGIPublish)
        return;

    switch (d->authStatus) {
        case Contact::LocalPending:
            setOnGroup(d->telepathyGIPublish, value);
            break;
        case Contact::Authorized:
            if (value == false)
                setOnGroup(d->telepathyGIPublish, value);
            break;
        case Contact::NonExistent:
            if (value == true)
                setOnGroup(d->telepathyGIPublish, value);
            break;
        default:
            break;
    }
}

void Contact::block(bool value)
{
    if ((d->blocked == value) ||
        (!d->telepathyGIDeny))
        return;

    setOnGroup(d->telepathyGIDeny, value);
}

void Contact::setSubscriptionStatus(Contact::SubscriptionStatus status, bool emitSignal)
{
    if (status == d->subStatus)
        return;

    d->subStatus = status;

    if (emitSignal)
        emit subscriptionStatusChanged(status);
}

void Contact::setAuthorizationStatus(Contact::AuthorizationStatus status, bool emitSignal)
{
    if (status == d->authStatus)
        return;

    d->authStatus = status;

    if (emitSignal)
        emit authorizationStatusChanged(status);
}

void Contact::setHideStatus(bool status, bool emitSignal)
{
    if (status == d->hideFrom)
        return;

    d->hideFrom = status;

    if (emitSignal)
        emit hideFromStatusChanged(status);
}

void Contact::setBlockStatus(bool status, bool emitSignal)
{
    if (status == d->blocked)
        return;

    d->blocked= status;

    if (emitSignal)
        emit blockStatusChanged(status);
}

void Contact::setOnGroup(org::freedesktop::Telepathy::ChannelGroupInterface *group, bool value)
{
    d->mutex.lock();

    QList<uint> handlelist;
    handlelist << handle()->id();
    if (value)
        group->AddMembers(handlelist, "");
    else
        group->RemoveMembers(handlelist, "");

    d->mutex.unlock();
}

void Contact::onMembersChangedSubscribe(const QString &message, const QList<uint> &added, const QList<uint> &removed,
                                        const QList<uint> &local, const QList<uint> &remote, uint actor, uint reason)
{
    d->mutex.lock();

    if (!remote.isEmpty()) {
        if (added.contains(handle()->id())) {
            d->subStatus = Contact::RemotePending;
            emit subscriptionStatusChanged(d->subStatus);
        }
    }
    else if (!removed.isEmpty()) {
        if (removed.contains(handle()->id())) {
            d->subStatus = Contact::NotSubscribed;
            emit subscriptionStatusChanged(d->subStatus);
        }
    }

    d->mutex.unlock();

}

void Contact::onMembersChangedPublish(const QString &message, const QList<uint> &added, const QList<uint> &removed,
                                      const QList<uint> &local, const QList<uint> &remote, uint actor, uint reason)
{
    d->mutex.lock();

    if (!added.isEmpty()) {
        if (added.contains(handle()->id())) {
            d->authStatus = Contact::Authorized;
            emit authorizationStatusChanged(d->authStatus);
        }
    }
    else if (!removed.isEmpty()) {
        if (removed.contains(handle()->id())) {
            d->authStatus = Contact::NonExistent;
            emit authorizationStatusChanged(d->authStatus);
        }
    }

    d->mutex.unlock();

}

void Contact::onMembersChangedHide(const QString &message, const QList<uint> &added, const QList<uint> &removed,
                                   const QList<uint> &local, const QList<uint> &remote, uint actor, uint reason)
{
    d->mutex.lock();

    if (!added.isEmpty()) {
        if ((added.contains(handle()->id())) &&
            (!d->hideFrom))
            d->hideFrom= true;
    }
    else if (!removed.isEmpty()) {
        if ((removed.contains(handle()->id())) &&
            (d->hideFrom))
            d->hideFrom= false;
    }

    d->mutex.unlock();

    emit hideFromStatusChanged(d->hideFrom);
}

// TODO
void Contact::onMembersChangedAllow(const QString &message, const QList<uint> &added, const QList<uint> &removed,
                                    const QList<uint> &local, const QList<uint> &remote, uint actor, uint reason)
{
}

void Contact::onMembersChangedDeny(const QString &message, const QList<uint> &added, const QList<uint> &removed,
                                   const QList<uint> &local, const QList<uint> &remote, uint actor, uint reason)
{
    d->mutex.lock();

    if (!added.isEmpty()) {
        if ((added.contains(handle()->id())) &&
            (!d->blocked))
            d->blocked = true;
    }
    else if (!removed.isEmpty()) {
        if ((removed.contains(handle()->id())) &&
            (d->blocked))
            d->blocked = false;
    }

    d->mutex.unlock();

    emit blockStatusChanged(d->blocked);
}
