/*
 * 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 <QDebug>
#include <QtCore/QList>
#include <QtCore/QString>
#include "QtTapioca/ContactBase"
#include "QtTapioca/Avatar"
#include "QtTapioca/Handle"

namespace QtTapioca {

class ContactBasePrivate {
public:
    ContactBasePrivate()
        : currentToken("")
    {
    }
    ~ContactBasePrivate()
    {
    }

    QString currentToken;
    QString alias;
    QList<ContactBase::Capability> caps;
};

}

using namespace QtTapioca;

ContactBase::ContactBase(org::freedesktop::Telepathy::Connection *telepathyConn,
                         org::freedesktop::Telepathy::ConnectionAvatarsInterface  *iAvatar,
                         org::freedesktop::Telepathy::ConnectionPresenceInterface *iPresence,
                         org::freedesktop::Telepathy::ConnectionAliasingInterface *iAliasing,
                         org::freedesktop::Telepathy::ConnectionCapabilitiesInterface *iCapabilities,
                         Handle *contact_handle,
                         QObject *parent)
    : ChannelTarget(contact_handle, parent),
      m_presence(ContactBase::Offline),
      telepathyIAvatar(iAvatar),
      telepathyIPresence(iPresence),
      telepathyIAliasing(iAliasing),
      telepathyICapabilities(iCapabilities),
      d(new ContactBasePrivate())
{
    if (telepathyIAvatar)
        QObject::connect(telepathyIAvatar, SIGNAL(AvatarUpdated(uint,const QString&)), this, SLOT(onAvatarUpdated(uint, const QString&)));

    if (telepathyIPresence) {
        QObject::connect(telepathyIPresence, SIGNAL(PresenceUpdate(org::freedesktop::Telepathy::PresenceStateInTimeMap)), this,
                         SLOT(onPresenceUpdated(org::freedesktop::Telepathy::PresenceStateInTimeMap)));
        telepathyIPresence->RequestPresence(QList<uint>() << handle()->id());
    }

    if (telepathyIAliasing)
        QObject::connect(telepathyIAliasing, SIGNAL(AliasesChanged(org::freedesktop::Telepathy::AliasInfoList)), this,
                         SLOT(onAliasChanged(org::freedesktop::Telepathy::AliasInfoList)));

    if (telepathyICapabilities) {
        QObject::connect(telepathyICapabilities, SIGNAL(CapabilitiesChanged(org::freedesktop::Telepathy::CapabilitiesChangedInfoList)), this,
                         SLOT(onCapabilitiesChanged(org::freedesktop::Telepathy::CapabilitiesChangedInfoList)));
        initCapabilities();
    }
}

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

ContactBase::Presence ContactBase::presence() const
{
    return m_presence;
}

QString ContactBase::presenceMessage() const
{
    return m_presenceMessage;
}

QString ContactBase::alias() const
{
    if ((d->alias.isEmpty()) && (telepathyIAliasing)) {
        QDBusReply<QStringList> reply = telepathyIAliasing->RequestAliases(QList<uint>() << handle()->id());

        if (reply.isValid())
            d->alias = reply.value().first();
        else
            qDebug() << "error requesting alias:" << reply.error().message();
    }

    return d->alias;
}

QList<ContactBase::Capability> ContactBase::capabilities() const
{
    return d->caps;
}

void ContactBase::requestAvatar()
{
    if (!telepathyIAvatar) {
        qDebug() << "avatar is not supported";
        return;
    }

    QString newToken;
    QDBusReply<QStringList> tokens = telepathyIAvatar->GetAvatarTokens(QList<uint>() << handle()->id());

    if (!tokens.isValid()) {
        qDebug() << "error getting avatar tokens:" << tokens.error().message();
        return;
    }

    if (!tokens.value().isEmpty()) {
        newToken = tokens.value().first();
        if (d->currentToken != newToken)
            d->currentToken = newToken;

        QString mime;
        QDBusReply<QByteArray> data = telepathyIAvatar->RequestAvatar(handle()->id(), mime);

        if (!data.isValid()) {
            qDebug() << "error requesting avatar:" << data.error().message();
            return;
        }

        Avatar *avatar = new Avatar(data.value(), mime, newToken);

        if (avatar)
            emit avatarReceived(this, avatar);
    }
}

QString ContactBase::currentAvatarToken()
{
    if ((d->currentToken.isEmpty()) && (telepathyIAvatar)) {
        QDBusReply<QStringList> tokens = telepathyIAvatar->GetAvatarTokens(QList<uint>() << handle()->id());
        if (!tokens.isValid())
            qDebug() << "error getting avatar token:" << tokens.error().message();
        else if (!tokens.value().isEmpty())
            d->currentToken = tokens.value().first();
    }

    return d->currentToken;
}

void ContactBase::onPresenceUpdated(org::freedesktop::Telepathy::PresenceStateInTimeMap newPresence)
{
    if (newPresence.contains(handle()->id())) {
        org::freedesktop::Telepathy::PresenceStateInTime stateInTime = newPresence[handle()->id()];
        m_presence = strToPresenceEnum(stateInTime.state.begin().key());
        m_presenceMessage = (stateInTime.state.begin().value())["message"].toString();

        emit presenceUpdated(this, m_presence, m_presenceMessage);
    }
}

ContactBase::Presence ContactBase::strToPresenceEnum(const QString &presenceStr)
{
    ContactBase::Presence presence;
    if (presenceStr == "available")
        presence = ContactBase::Available;
    else if (presenceStr == "away" ||
             presenceStr == "brb")
        presence = ContactBase::Away;
    else if (presenceStr == "xa")
        presence = ContactBase::XA;
    else if (presenceStr == "busy" ||
             presenceStr == "dnd")
        presence = ContactBase::Busy;
    else if (presenceStr == "hidden")
        presence = ContactBase::Hidden;
    else
        presence = ContactBase::Offline;

    return presence;
}

const QString ContactBase::presenceEnumToStr(ContactBase::Presence num)
{
    QString presenceStr;
    switch (num) {
        case ContactBase::Available:
            presenceStr = "available";
            break;
        case ContactBase::Away:
            presenceStr = "away";   //Could be "brb"
            break;
        case ContactBase::XA:
            presenceStr = "xa";
            break;
        case ContactBase::Busy:
            presenceStr = "dnd";   //Could be "busy"
            break;
        case ContactBase::Hidden:
            presenceStr = "hidden";
            break;
        default:
            presenceStr = "offline";
            break;
    }

    return presenceStr;
}

void ContactBase::initCapabilities()
{
    if (!telepathyICapabilities)
        return;

    d->caps.clear();

    QDBusReply<org::freedesktop::Telepathy::CapabilityInfoList> reply = telepathyICapabilities->GetCapabilities(QList<uint>() << handle()->id());
    if (!reply.isValid())
        qDebug() << "error getting capabilities:" << reply.error().message();
    else {
        org::freedesktop::Telepathy::CapabilityInfo capInfo;
        foreach (capInfo, reply.value())
            updateCapabilities(capInfo.channelType, capInfo.typeSpecificFlags);
    }
}

void ContactBase::updateCapabilities(const QString &channelType, quint32 typeSpecificFlags)
{
    /* Check for text cap */
    if ((channelType == "org.freedesktop.Telepathy.Channel.Type.Text") &&
        (!(d->caps.contains(ContactBase::Text))))
            d->caps << ContactBase::Text;
    /* Check for streamedMedia cap */
    else if (channelType == "org.freedesktop.Telepathy.Channel.Type.StreamedMedia") {

        /* Check for audio support */
        if (typeSpecificFlags & 1) {
            if (!d->caps.contains(ContactBase::Audio))
                d->caps << ContactBase::Audio;
        }
        else
            if (d->caps.contains(ContactBase::Audio)) {
                int pos = d->caps.indexOf(ContactBase::Audio);
                if (pos != -1)
                    d->caps.removeAt(pos);
            }

        /* Check for video support */
        if (typeSpecificFlags & 2) {
            if (!d->caps.contains(ContactBase::Video))
                d->caps << ContactBase::Video;
        }
        else
            if (d->caps.contains(ContactBase::Video)) {
                int pos = d->caps.indexOf(ContactBase::Video);
                if (pos != -1)
                    d->caps.removeAt(pos);
            }
    }
}

void ContactBase::onAvatarUpdated(uint contact, const QString &newAvatarToken)
{
    if (handle()->id() == contact)
        if (d->currentToken != newAvatarToken) {
            d->currentToken = newAvatarToken;
            emit avatarUpdated(this, d->currentToken);
    }
}

void ContactBase::onAliasChanged(org::freedesktop::Telepathy::AliasInfoList aliases)
{
    uint id = handle()->id();
    org::freedesktop::Telepathy::AliasInfo aliasInfo;

    foreach (aliasInfo, aliases)
        if (aliasInfo.contactHandle == id) {
            d->alias = aliasInfo.newAlias;
            emit aliasChanged(this, d->alias);
        }
}

void ContactBase::onCapabilitiesChanged(org::freedesktop::Telepathy::CapabilitiesChangedInfoList caps)
{
    uint id = handle()->id();
    org::freedesktop::Telepathy::CapabilitiesChangedInfo cap;

    foreach (cap, caps)
        if (cap.contactHandle == id)
            updateCapabilities(cap.channelType, cap.newTypeSpecificFlags);
}

bool ContactBase::operator==(const ContactBase &info) const
{
    return (*(info.handle()) == *handle());
}

