/*
 * QtTapioca, the Tapioca Qt4 Client Library
 * Copyright (C) 2006 by INdT
 *  @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/QPointer>

#include "QtTapioca/TextChannel"
#include "QtTapioca/Contact"
#include "QtTapioca/Handle"
#include "QtTapioca/Connection"

#include <QtTelepathy/Client/Channel>
#include <QtTelepathy/Client/ChannelText>
#include <QtTelepathy/Client/ChannelChatStateInterface>

namespace QtTapioca {

class TextChannelPrivate {
public:
    TextChannelPrivate(Connection *_conn, const QString &serviceName, const QString &objPath)
        :conn(_conn),
         currentChatState(TextChannel::Inactive),
         iChatState(NULL)
    {
        ch = new org::freedesktop::Telepathy::ChannelText(serviceName, objPath, QDBusConnection::sessionBus());

        loadInterfaces(serviceName, objPath);
    }
    ~TextChannelPrivate()
    {
        delete ch;
        delete iChatState;
    }

    void loadInterfaces(const QString &serviceName, const QString &objPath)
    {
        org::freedesktop::Telepathy::Channel *_channel = new org::freedesktop::Telepathy::Channel(serviceName, objPath, QDBusConnection::sessionBus());

        if (_channel) {
            QStringList interfaces = _channel->GetInterfaces();

            if (interfaces.contains(org::freedesktop::Telepathy::ChannelChatStateInterface::staticInterfaceName()))
                iChatState = new org::freedesktop::Telepathy::ChannelChatStateInterface(ch->service(), ch->path(), QDBusConnection::sessionBus());

            delete _channel;
        }
    }

    Connection *conn;
    TextChannel::ChatState currentChatState;
    org::freedesktop::Telepathy::ChannelText *ch;
    org::freedesktop::Telepathy::ChannelChatStateInterface *iChatState;
};

}

using namespace QtTapioca;

TextChannel::TextChannel(Connection *conn, const QString &serviceName, const QString &objPath, ChannelTarget *target, QObject *parent)
    : Channel(conn, serviceName, objPath, Channel::Text, target, parent),
      d(new TextChannelPrivate(conn, serviceName, objPath))
{
    Q_ASSERT(d);

    QObject::connect(d->ch, SIGNAL(Received(uint, uint, uint, uint, uint, const QString &)),
                     this, SLOT(onMessageReceived(uint, uint, uint, uint, uint, const QString &)));
    QObject::connect(d->ch, SIGNAL(SendError(uint, uint, uint, const QString &)),
                     this, SLOT(onSendError(uint, uint, uint, const QString &)));
    QObject::connect(d->ch, SIGNAL(Sent(uint, uint, const QString &)),
                     this, SLOT(onSent(uint, uint, const QString &)));

    if (d->iChatState)
        QObject::connect(d->iChatState, SIGNAL(ChatStateChanged(uint, uint)),
                         this, SLOT(onChatStateChanged(uint, uint)));
}

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

void TextChannel::sendMessage(const QString &contents)
{
    d->ch->Send(static_cast<uint>(TextChannel::Message::Normal), contents);
}

void TextChannel::sendMessage(const Message &message)
{
    d->ch->Send(static_cast<uint>(message.type()), message.contents());
}

QList<TextChannel::Message> TextChannel::pendingMessages() const
{
    Q_ASSERT(d->ch);

    QList<TextChannel::Message> ret;
    org::freedesktop::Telepathy::TextMessageInfo msgInfo;
    QDBusReply<org::freedesktop::Telepathy::TextMessageInfoList> reply = d->ch->ListPendingMessages(false);

    if (!reply.isValid()) {
        qDebug() << "error getting pending messages:" << reply.error().message();
        return ret;
    }

    foreach (msgInfo, reply.value())
        ret << Message(msgInfo.message, msgInfo.timestamp, static_cast<TextChannel::Message::Type>(msgInfo.type), msgInfo.id, true);

    return ret;
}

void TextChannel::acknowledge(const Message &message)
{
    Q_ASSERT(d->ch);

    if ((message.m_pending) && (message.m_id >= 0)) {
        QList<uint> ids;
        ids << message.m_id;
        QDBusReply<void> reply = d->ch->AcknowledgePendingMessages(ids);

        if (!reply.isValid()) {
            qDebug() << "error acknowledging pending messages:" << reply.error().message();
            return;
        }

        message.m_pending = false;
    }
}

bool TextChannel::setLocalChatState(TextChannel::ChatState state)
{
    Q_ASSERT(d->ch);

    if (!d->iChatState)
        return false;

    if (d->currentChatState != state) {
        QDBusReply<void> reply = d->iChatState->SetChatState(state);

        if (!reply.isValid()) {
            qDebug() << "error setting chat state:" << reply.error().message();
            return false;
        }
    }

    return true;
}

TextChannel::ChatState TextChannel::localChatState() const
{
    return d->currentChatState;
}

void TextChannel::onMessageReceived(uint id, uint timestamp, uint sender, uint type, uint flags, const QString &text)
{
    Message msg(text, timestamp, static_cast<TextChannel::Message::Type>(type), id, true);
    emit messageReceived(this, msg);
}

void TextChannel::onSendError(uint error, uint timestamp, uint type, const QString &text)
{
    Message msg(text, timestamp, static_cast<TextChannel::Message::Type>(type));
    emit messageDeliveryError(msg, static_cast<TextChannel::Message::DeliveryError>(error));
}

void TextChannel::onSent(uint timestamp, uint type, const QString &text)
{
    Message msg(text, timestamp, static_cast<TextChannel::Message::Type>(type));
    emit messageSent(msg);
}

void TextChannel::onChatStateChanged(uint contact, uint state)
{
    if (d->conn->userContact()->handle()->id() == contact)
        d->currentChatState = static_cast<TextChannel::ChatState>(state);

    emit chatStateChanged(this, static_cast<TextChannel::ChatState>(state));
}

TextChannel::Message::Message(const QString &contents)
    : m_contents(contents),
      m_timestamp(0),
      m_type(TextChannel::Message::Normal),
      m_id(-1),
      m_pending(false)
{
}

TextChannel::Message::Message(const QString &contents, uint timestamp, TextChannel::Message::Type type, int id, bool pending)
    : m_contents(contents),
      m_timestamp(timestamp),
      m_type(type),
      m_id(id),
      m_pending(pending)
{
}

TextChannel::Message::~Message()
{
}

