/**
 * Copyright (C) 2013 Canonical, Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
 * SATISFACTORY QUALITY, 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 program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: Tiago Salem Herrmann <tiago.herrmann@canonical.com>
 */
#include <QDBusArgument>
#include <QtCore/QObject>
#include <QtTest/QtTest>
#include <QVariant>
#include <TelepathyQt/Types>
#include <TelepathyQt/Account>
#include <TelepathyQt/CallChannel>
#include <TelepathyQt/Contact>
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
#include <TelepathyQt/ContactManager>
#pragma GCC diagnostic pop
#include <TelepathyQt/PendingContacts>
#include <TelepathyQt/PendingOperation>
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
#include <TelepathyQt/TextChannel>
#pragma GCC diagnostic pop
#include <TelepathyQt/ReceivedMessage>
#include "telepathyhelper_p.h"
#include "telepathytest.h"
#include "mockcontroller.h"
#include "handler.h"
#include "approver.h"

#include "manager.h"
#include "thread.h"
#include "textevent.h"
#include "texteventattachment.h"
#include "voiceevent.h"
#include "config.h"
#include "dbustypes.h"

Q_DECLARE_METATYPE(Tp::CallChannelPtr)
Q_DECLARE_METATYPE(Tp::TextChannelPtr)
Q_DECLARE_METATYPE(QList<Tp::ContactPtr>)
Q_DECLARE_METATYPE(History::Threads)
Q_DECLARE_METATYPE(History::Events)
Q_DECLARE_METATYPE(History::TextEventAttachment)
Q_DECLARE_METATYPE(History::TextEventAttachments)
Q_DECLARE_METATYPE(History::MessageStatus)

QDBusArgument &operator<<(QDBusArgument &argument, const AttachmentStruct &attachment)
{
    argument.beginStructure();
    argument << attachment.id << attachment.contentType << attachment.filePath << attachment.offset << attachment.length;
    argument.endStructure();
    return argument;
}

const QDBusArgument &operator>>(const QDBusArgument &argument, AttachmentStruct &attachment)
{
    argument.beginStructure();
    argument >> attachment.id >> attachment.contentType >> attachment.filePath >> attachment.offset >> attachment.length;
    argument.endStructure();
    return argument;
}

class DaemonTest : public TelepathyTest
{
    Q_OBJECT

Q_SIGNALS:
    void contactsReceived(QList<Tp::ContactPtr> contacts);

private Q_SLOTS:
    void initTestCase();
    void init();
    void cleanup();
    void testMessageReceived();
    void testMMSReceived();
    void testMessageSentNoEventId();
    void testMessageSent();
    void testMMSSent();
    void testMissedCall();
    void testOutgoingCall();
    void testDeliveryReport_data();
    void testDeliveryReport();

    // helper slots
    void onPendingContactsFinished(Tp::PendingOperation*);

private:
    Approver *mApprover;
    Handler *mHandler;
    MockController *mMockController;
    Tp::AccountPtr mAccount;
};

void DaemonTest::initTestCase()
{
    qRegisterMetaType<Tp::Presence>();
    qRegisterMetaType<Tp::CallChannelPtr>();
    qRegisterMetaType<Tp::TextChannelPtr>();
    qRegisterMetaType<Tp::PendingOperation*>();
    qRegisterMetaType<QList<Tp::ContactPtr> >();
    qRegisterMetaType<History::Threads>();
    qRegisterMetaType<History::Events>();
    qRegisterMetaType<History::TextEventAttachment>();
    qRegisterMetaType<History::TextEventAttachments>();
    qRegisterMetaType<History::MessageStatus>();

    qDBusRegisterMetaType<AttachmentStruct>();
    qDBusRegisterMetaType<AttachmentList>();

    initialize();

    // register the handler
    mHandler = new Handler(this);
    History::TelepathyHelper::instance()->registerClient(mHandler, "HistoryTestHandler");
    QTRY_VERIFY(mHandler->isRegistered());

    // register the approver
    mApprover = new Approver(this);
    History::TelepathyHelper::instance()->registerClient(mApprover, "HistoryTestApprover");
    // Tp-qt does not set registered status to approvers
    QTRY_VERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(TELEPHONY_SERVICE_APPROVER));

    // we need to wait in order to give telepathy time to notify about the approver and handler
    QTest::qWait(3000);
}

void DaemonTest::init()
{
    mAccount = addAccount("mock", "mock", "the account");
    QVERIFY(!mAccount.isNull());
    QTRY_VERIFY(mAccount->isReady(Tp::Account::FeatureCore));
    QTRY_VERIFY(!mAccount->connection().isNull());
    QTRY_VERIFY(History::TelepathyHelper::instance()->connected());

    mMockController = new MockController("mock", this);
}

void DaemonTest::cleanup()
{
    doCleanup();
    mMockController->deleteLater();
}

void DaemonTest::testMessageReceived()
{
    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QVariantMap properties;
    QString sender = "123456789";
    QString message = "Test message";
    QDateTime sentTime = QDateTime::currentDateTime();
    properties["Sender"] = sender;
    properties["SentTime"] = sentTime.toString(Qt::ISODate);
    properties["Recipients"] = QStringList() << sender;

    QSignalSpy handlerSpy(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    mMockController->placeIncomingMessage(message, properties);
    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();
    QCOMPARE(event.senderId(), sender);
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);
    QCOMPARE(event.timestamp().toString(Qt::ISODate), sentTime.toString(Qt::ISODate));
    QCOMPARE(event.message(), message);

    QTRY_COMPARE(handlerSpy.count(), 1);
    Tp::TextChannelPtr channel = handlerSpy.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);
    channel->requestClose();
}

void DaemonTest::testMMSReceived()
{
    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QVariantMap properties;
    QString sender = "9876543210";
    QString message = "Test message";
    QDateTime sentTime = QDateTime::currentDateTime();
    properties["Sender"] = sender;
    properties["SentTime"] = sentTime.toString(Qt::ISODate);
    properties["Recipients"] = QStringList() << sender;

    AttachmentList newAttachments;

    AttachmentStruct newAttachment;
    newAttachment.id = QString("dialer-app.png");
    newAttachment.contentType = QString("image/png");
    newAttachment.filePath = QString("%1/dialer-app.png").arg(TEST_DATA_DIR);
    newAttachment.offset = 0;
    newAttachment.length = 31654;

    newAttachments << newAttachment;

    AttachmentStruct newAttachment2;
    newAttachment2.id = QString("<AFELIX.GIF>");
    newAttachment2.contentType = QString("image/gif");
    newAttachment2.filePath = QString("%1/AFELIX.GIF").arg(TEST_DATA_DIR);
    newAttachment2.offset = 0;
    newAttachment2.length = 28809;

    newAttachments << newAttachment2;
    properties["Attachments"] = QVariant::fromValue(newAttachments);

    QSignalSpy handlerSpy(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    mMockController->placeIncomingMessage(message, properties);
    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();
    QCOMPARE(event.senderId(), sender);
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);

    // attachments
    QCOMPARE(event.attachments().count(), 2);
    History::TextEventAttachment txtAtt = event.attachments().first();
    QVERIFY(txtAtt.contentType() == QString("image/png"));
    QVERIFY(txtAtt.attachmentId() == QString("dialer-app.png"));
    QVERIFY(QFile(txtAtt.filePath()).exists());

    txtAtt = event.attachments().at(1);
    QVERIFY(txtAtt.contentType() == QString("image/gif"));
    QVERIFY(txtAtt.attachmentId() == QString("<AFELIX.GIF>"));
    QVERIFY(QFile(txtAtt.filePath()).exists());
    QFileInfo fi(txtAtt.filePath());
    // "<>" chars should have been stripped
    QVERIFY(fi.fileName() == "AFELIX.GIF");

    QTRY_COMPARE(handlerSpy.count(), 1);
    Tp::TextChannelPtr channel = handlerSpy.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);
    channel->requestClose();
}

void DaemonTest::testMessageSentNoEventId()
{
    // Request the contact to start chatting to
    QSignalSpy spy(this, SIGNAL(contactsReceived(QList<Tp::ContactPtr>)));

    QString recipient = "11111111";

    connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << recipient),
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onPendingContactsFinished(Tp::PendingOperation*)));

    QTRY_COMPARE(spy.count(), 1);

    QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts.first()->id(), recipient);

    QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    Q_FOREACH(Tp::ContactPtr contact, contacts) {
        mAccount->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
    }
    QTRY_COMPARE(spyTextChannel.count(), 1);

    Tp::TextChannelPtr channel = spyTextChannel.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);

    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QString messageText = "Hello, big world!";
    Tp::Message m(Tp::ChannelTextMessageTypeNormal, messageText);
    Tp::MessagePartList parts = m.parts();
    Tp::MessagePart header = parts[0];
    Tp::MessagePart body = parts[1];
    header["no-event-id"] = QDBusVariant(true);
    Tp::MessagePartList newPart;
    newPart << header << body;
    Tp::PendingSendMessage *message = channel->send(newPart);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();
    QVERIFY(!event.eventId().isEmpty());

    channel->requestClose();
}

void DaemonTest::testMessageSent()
{
    // Request the contact to start chatting to
    QSignalSpy spy(this, SIGNAL(contactsReceived(QList<Tp::ContactPtr>)));

    QString recipient = "987654321";

    connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << recipient),
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onPendingContactsFinished(Tp::PendingOperation*)));

    QTRY_COMPARE(spy.count(), 1);

    QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts.first()->id(), recipient);

    QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    Q_FOREACH(Tp::ContactPtr contact, contacts) {
        mAccount->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
    }
    QTRY_COMPARE(spyTextChannel.count(), 1);

    Tp::TextChannelPtr channel = spyTextChannel.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);

    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QString messageText = "Hello, big world!";
    Tp::PendingSendMessage *message = channel->send(messageText);

    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();
    QCOMPARE(event.senderId(), QString("self"));
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);
    QCOMPARE(event.message(), messageText);

    channel->requestClose();
}

void DaemonTest::testMMSSent()
{
    // Request the contact to start chatting to
    QSignalSpy spy(this, SIGNAL(contactsReceived(QList<Tp::ContactPtr>)));

    QString recipient = "123412340";

    connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << recipient),
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onPendingContactsFinished(Tp::PendingOperation*)));

    QTRY_COMPARE(spy.count(), 1);

    QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts.first()->id(), recipient);

    QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    Q_FOREACH(Tp::ContactPtr contact, contacts) {
        mAccount->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
    }
    QTRY_COMPARE(spyTextChannel.count(), 1);

    Tp::TextChannelPtr channel = spyTextChannel.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);

    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QString messageText = "Hello, big world!";
    Tp::Message m(Tp::ChannelTextMessageTypeNormal, messageText);
    Tp::MessagePartList parts = m.parts();

    Tp::MessagePart header = parts[0];
    header["message-sender"] = QDBusVariant(0);
    header["message-type"] = QDBusVariant(Tp::ChannelTextMessageTypeNormal);

    QByteArray fileData;
    QFile attachmentFile(QString("%1/dialer-app.png").arg(TEST_DATA_DIR));
    attachmentFile.open(QIODevice::ReadOnly);
    fileData = attachmentFile.readAll();

    Tp::MessagePart body = parts[1];
    body["content-type"] =  QDBusVariant("image/png");
    body["identifier"] = QDBusVariant("<dia/ler-app>.png");
    body["content"] = QDBusVariant(fileData);
    body["size"] = QDBusVariant(fileData.size());

    Tp::MessagePartList newPart;
    newPart << header << body;

    Tp::PendingSendMessage *message = channel->send(newPart);

    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();
    QCOMPARE(event.senderId(), QString("self"));
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);

    //Attachments
    QCOMPARE(event.attachments().count(), 1);
    History::TextEventAttachment txtAtt = event.attachments().first();
    QVERIFY(txtAtt.contentType() == QString("image/png"));
    QVERIFY(txtAtt.attachmentId() == QString("<dia/ler-app>.png"));
    QVERIFY(QFile(txtAtt.filePath()).exists());
    QFileInfo fi(txtAtt.filePath());
    // special chars should have been stripped
    QVERIFY(fi.fileName() == "dialer-app.png");

    channel->requestClose();
}

void DaemonTest::testMissedCall()
{
    QSignalSpy newCallSpy(mApprover, SIGNAL(newCall()));

    // create an incoming call
    QString callerId = "33333333";
    QVariantMap properties;
    properties["Caller"] = callerId;
    properties["State"] = "incoming";
    mMockController->placeCall(properties);
    QTRY_COMPARE(newCallSpy.count(), 1);

    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    // now hangup the call and check that the event was added to the database
    mMockController->hangupCall(callerId);

    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::VoiceEvent event = events.first();
    QCOMPARE(event.senderId(), callerId);
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);
    QCOMPARE(event.missed(), true);
}

void DaemonTest::testOutgoingCall()
{
    // Request the contact to start chatting to
    QString phoneNumber = "44444444";
    QSignalSpy spy(this, SIGNAL(contactsReceived(QList<Tp::ContactPtr>)));

    connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber),
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onPendingContactsFinished(Tp::PendingOperation*)));
    QVERIFY(spy.wait());

    QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts.first()->id(), phoneNumber);

    QSignalSpy spyCallChannel(mHandler, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)));

    Q_FOREACH(Tp::ContactPtr contact, contacts) {
        mAccount->ensureAudioCall(contact, "audio", QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
    }
    QVERIFY(spyCallChannel.wait());

    Tp::CallChannelPtr channel = spyCallChannel.first().first().value<Tp::CallChannelPtr>();
    QVERIFY(channel);

    mMockController->setCallState(phoneNumber, "alerting");
    QTRY_COMPARE(channel->callState(), Tp::CallStateInitialised);

    mMockController->setCallState(phoneNumber, "active");
    QTRY_COMPARE(channel->callState(), Tp::CallStateActive);

    QSignalSpy threadsAddedSpy(History::Manager::instance(), SIGNAL(threadsAdded(History::Threads)));
    QSignalSpy threadsModifiedSpy(History::Manager::instance(), SIGNAL(threadsModified(History::Threads)));
    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    mMockController->setCallState(phoneNumber, "disconnected");
    QTRY_COMPARE(channel->callState(), Tp::CallStateEnded);

    QTRY_COMPARE(threadsAddedSpy.count(), 1);
    History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread thread = threads.first();

    QTRY_COMPARE(threadsModifiedSpy.count(), 1);
    threads = threadsModifiedSpy.first().first().value<History::Threads>();
    QCOMPARE(threads.count(), 1);
    History::Thread modifiedThread = threads.first();
    QVERIFY(modifiedThread == thread);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::VoiceEvent event = events.first();
    QCOMPARE(event.senderId(), QString("self"));
    QCOMPARE(event.threadId(), modifiedThread.threadId());
    QVERIFY(modifiedThread.lastEvent() == event);
    QCOMPARE(event.missed(), false);
    QVERIFY(event.duration().isValid());
}

void DaemonTest::testDeliveryReport_data()
{
    QTest::addColumn<QString>("phoneNumber");
    QTest::addColumn<QString>("deliveryStatus");
    QTest::addColumn<History::MessageStatus>("messageStatus");

    QTest::newRow("delivered status") << "11112222" << "delivered" << History::MessageStatusDelivered;
    QTest::newRow("temporarily failed") << "11113333" << "temporarily_failed" << History::MessageStatusTemporarilyFailed;
    QTest::newRow("permanently failed") << "11114444" << "permanently_failed" << History::MessageStatusPermanentlyFailed;
    QTest::newRow("accepted status") << "11115555" << "accepted" << History::MessageStatusAccepted;
    QTest::newRow("read status") << "11116666" << "read" << History::MessageStatusRead;
    QTest::newRow("deleted") << "11117777" << "deleted" << History::MessageStatusDeleted;
    QTest::newRow("unknown") << "11118888" << "unknown" << History::MessageStatusUnknown;
}

void DaemonTest::testDeliveryReport()
{
    QFETCH(QString, phoneNumber);
    QFETCH(QString, deliveryStatus);
    QFETCH(History::MessageStatus, messageStatus);

    // Request the contact to start chatting to
    QSignalSpy spy(this, SIGNAL(contactsReceived(QList<Tp::ContactPtr>)));

    connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber),
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onPendingContactsFinished(Tp::PendingOperation*)));

    QTRY_COMPARE(spy.count(), 1);

    QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
    QCOMPARE(contacts.count(), 1);
    QCOMPARE(contacts.first()->id(), phoneNumber);

    QSignalSpy spyTextChannel(mHandler, SIGNAL(textChannelAvailable(Tp::TextChannelPtr)));

    Q_FOREACH(Tp::ContactPtr contact, contacts) {
        mAccount->ensureTextChat(contact, QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
    }
    QTRY_COMPARE(spyTextChannel.count(), 1);

    Tp::TextChannelPtr channel = spyTextChannel.first().first().value<Tp::TextChannelPtr>();
    QVERIFY(channel);

    QSignalSpy eventsAddedSpy(History::Manager::instance(), SIGNAL(eventsAdded(History::Events)));

    QString messageText = "Hello, big world!";
    Tp::PendingSendMessage *message = channel->send(messageText);

    QTRY_COMPARE(eventsAddedSpy.count(), 1);
    History::Events events = eventsAddedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    History::TextEvent event = events.first();

    // now send a delivery report for this text and make sure the event gets updated
    QSignalSpy eventsModifiedSpy(History::Manager::instance(), SIGNAL(eventsModified(History::Events)));

    mMockController->placeDeliveryReport(QStringList() << phoneNumber, event.eventId(), deliveryStatus);
    QTRY_COMPARE(eventsModifiedSpy.count(), 1);
    events = eventsModifiedSpy.first().first().value<History::Events>();
    QCOMPARE(events.count(), 1);
    event = events.first();
    QCOMPARE(event.messageStatus(), messageStatus);

    channel->requestClose();
}

void DaemonTest::onPendingContactsFinished(Tp::PendingOperation *op)
{
    Tp::PendingContacts *pc = qobject_cast<Tp::PendingContacts*>(op);
    if (!pc) {
        return;
    }

    Q_EMIT contactsReceived(pc->contacts());
}

QTEST_MAIN(DaemonTest)
#include "DaemonTest.moc"
