/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <QObject>

#include <qtcontacts.h>

#ifdef SYMBIANSIM_BACKEND_USE_ETEL_TESTSERVER
#include <etelmm_etel_test_server.h>
#else
#include <etelmm.h>
#endif
#include <mmtsy_names.h>

QTM_USE_NAMESPACE

#ifndef QTRY_COMPARE
#define QTRY_COMPARE(__expr, __expected) \
    do { \
        const int __step = 50; \
        const int __timeout = 5000; \
        if ((__expr) != (__expected)) { \
            QTest::qWait(0); \
        } \
        for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
            QTest::qWait(__step); \
        } \
        QCOMPARE(__expr, __expected); \
    } while(0)
#endif

//TESTED_CLASS=
//TESTED_FILES=

/*!
*/
class tst_SimCMAsync : public QObject
{
Q_OBJECT

public:
    tst_SimCMAsync();
    virtual ~tst_SimCMAsync();

public slots:
    void init();
    void cleanup();
    void initTestCase();
    void cleanupTestCase();

private slots:
    void fetchContactReq();
    void localIdFetchReq();
    void saveContactReq();
    void removeContactReq();
    void detailDefinitionFetchReq();
    void notSupportedRequests();
    void multipleRequests();

private:
    void initManager(QString simStore);
    QContact createContact(QString name, QString number);
    QContact saveContact(QString name, QString number);

private:
    QContactManager* m_cm;
};

tst_SimCMAsync::tst_SimCMAsync() :
    m_cm(0)
{
	qRegisterMetaType<QContactAbstractRequest::State>("QContactAbstractRequest::State");
}

tst_SimCMAsync::~tst_SimCMAsync()
{
}

void tst_SimCMAsync::init()
{
    // remove all contacts
    QList<QContactLocalId> ids = m_cm->contactIds();
    m_cm->removeContacts(ids, 0);   
}

void tst_SimCMAsync::cleanup()
{
    // remove all contacts
    QList<QContactLocalId> ids = m_cm->contactIds();
    m_cm->removeContacts(ids, 0);   
}

void tst_SimCMAsync::initTestCase()
{
    initManager(QString());
}

void tst_SimCMAsync::cleanupTestCase()
{
    delete m_cm;
    m_cm = 0;
}

void tst_SimCMAsync::initManager(QString simStore)
{
    delete m_cm;
    m_cm = 0;

    QString uri("qtcontacts:symbiansim");

    // Set the sim store if available (simbackend defaults to ADN if not available)
    if(!simStore.isEmpty()) {
        uri.append(":store=");
        uri.append(simStore);
    }

    m_cm = QContactManager::fromUri(uri);
}

QContact tst_SimCMAsync::createContact(QString name, QString number)
{
    QContact c;
    
    QContactName n;
    n.setCustomLabel(name);
    c.saveDetail(&n);
    
    QContactPhoneNumber nb;
    nb.setNumber(number);
    c.saveDetail(&nb);

    return c;
}

QContact tst_SimCMAsync::saveContact(QString name, QString number)
{
    QContact c;
    
    QContactName n;
    n.setCustomLabel(name);
    c.saveDetail(&n);
    
    QContactPhoneNumber nb;
    nb.setNumber(number);
    c.saveDetail(&nb);
    
    if (!m_cm->saveContact(&c)) {
        qWarning("*FATAL* could not save contact!");
    }
    
    return c;
}

void tst_SimCMAsync::fetchContactReq()
{
    QContactFetchRequest req;
    req.setManager(m_cm);
    
    QSignalSpy stateSpy(&req, SIGNAL(stateChanged(QContactAbstractRequest::State)));
    QSignalSpy resultSpy(&req, SIGNAL(resultsAvailable()));

    // Save some contacts
    QContact c1 = saveContact("a", "1234567");
    QContact c2 = saveContact("b", "7654321");
    QContact c3 = saveContact("c", "1111111");
    
    // Fetch the contacts
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);    
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.contacts().count() == 3);

    // Test cancelling
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(!req.cancel());
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);    
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.cancel());
    QVERIFY(!req.cancel());
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 0);
    QVERIFY(req.state() == QContactAbstractRequest::CanceledState);
   
    // Remove all contacts
    QList<QContactLocalId> ids = m_cm->contactIds();
    m_cm->removeContacts(ids, 0);    
    
    // Test fetching nothing
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(req.start());
    QVERIFY(!req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QTRY_COMPARE(resultSpy.count(), 1);
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::DoesNotExistError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(req.contacts().count() == 0);
}

void tst_SimCMAsync::localIdFetchReq()
{
    QContactLocalIdFetchRequest req;
    req.setManager(m_cm);
    
    QSignalSpy stateSpy(&req, SIGNAL(stateChanged(QContactAbstractRequest::State)));
    QSignalSpy resultSpy(&req, SIGNAL(resultsAvailable()));

    // Save some contacts
    QContact c1 = saveContact("a", "1234567");
    QContact c2 = saveContact("b", "7654321");
    QContact c3 = saveContact("c", "1111111");
    
    // Fetch the contacts
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.ids().count() == 3);
    QVERIFY(req.ids().contains(c1.localId()));
    QVERIFY(req.ids().contains(c2.localId()));
    QVERIFY(req.ids().contains(c3.localId()));
    
    // Test cancelling
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(!req.cancel());
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);    
    QVERIFY(req.start());
    QVERIFY(!req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.cancel());
    QVERIFY(!req.cancel());
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 0);
    QVERIFY(req.state() == QContactAbstractRequest::CanceledState);    
    
    // Remove all contacts
    QList<QContactLocalId> ids = m_cm->contactIds();
    m_cm->removeContacts(ids, 0);    
    
    // Start again
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QTRY_COMPARE(resultSpy.count(), 1);
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::DoesNotExistError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(req.ids().count() == 0);    
}

void tst_SimCMAsync::saveContactReq()
{
    QContactSaveRequest req;
    req.setManager(m_cm);
    
    QSignalSpy stateSpy(&req, SIGNAL(stateChanged(QContactAbstractRequest::State)));
    QSignalSpy resultSpy(&req, SIGNAL(resultsAvailable()));

    // Create some contacts
    QContact c1 = createContact("Keeppu", "47474747");
    QContact c2 = createContact("Rilli", "74747474");
    
    // Test cancel
    QList<QContact> contacts;
    contacts << c1 << c2;
    req.setContacts(contacts);    
    QVERIFY(!req.cancel());
    QVERIFY(req.start());
    QVERIFY(!req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.cancel());
    QVERIFY(!req.cancel());
    QVERIFY(req.state() == QContactAbstractRequest::CanceledState);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 0);
    QVERIFY(m_cm->contactIds().count() == 0);
    
    // Test save
    stateSpy.clear();
    req.setContacts(contacts); 
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QTRY_COMPARE(resultSpy.count(), 1);
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(req.errorMap().count() == 0);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.contacts().count() == 2);
    QContact c = req.contacts().at(0);
    QVERIFY(c.id().managerUri() == m_cm->managerUri());
    QVERIFY(c.localId() != QContactLocalId(0));
    QVERIFY(c.detail<QContactName>().firstName() == c1.detail<QContactName>().firstName());
    QVERIFY(c.detail<QContactPhoneNumber>().number() == c1.detail<QContactPhoneNumber>().number());
    QVERIFY(m_cm->contactIds().count() == 2);
    
    // Test saving again
    c1 = req.contacts().at(0);
    c2 = req.contacts().at(1);

    QContactName name = c1.detail<QContactName>();
    name.setCustomLabel("Keeputin");
    c1.saveDetail(&name);
    QContactPhoneNumber number = c1.detail<QContactPhoneNumber>();
    c1.removeDetail(&number);
    
    contacts.clear();
    contacts << c1 << c2;
    req.setContacts(contacts);
    
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(req.errorMap().count() == 0);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.contacts().count() == 2);
    QVERIFY(req.contacts().at(0).localId() == c1.localId());
    QVERIFY(req.contacts().at(1).localId() == c2.localId());
    c = req.contacts().at(0);
    QVERIFY(c.details(QContactPhoneNumber::DefinitionName).count() == 0);
    QVERIFY(c.detail<QContactName>().customLabel() == c1.detail<QContactName>().customLabel());
    QVERIFY(m_cm->contactIds().count() == 2);
    c = m_cm->contact(c1.localId());
    QVERIFY(c.details(QContactPhoneNumber::DefinitionName).count() == 0);
    QVERIFY(c.detail<QContactName>().customLabel() == c1.detail<QContactName>().customLabel());
}

void tst_SimCMAsync::removeContactReq()
{
    QContactRemoveRequest req;
    req.setManager(m_cm);
    
    QSignalSpy stateSpy(&req, SIGNAL(stateChanged(QContactAbstractRequest::State)));
    QSignalSpy resultSpy(&req, SIGNAL(resultsAvailable()));
    
    // Save some contacts
    QContact c1 = saveContact("a", "1234567");
    QContact c2 = saveContact("b", "7654321");

    // Remove the contacts
    QList<QContactLocalId> ids;
    ids << c1.localId() << c2.localId();
    req.setContactIds(ids);
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(req.errorMap().count() == 0);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(m_cm->contactIds().count() == 0);
    
    // Test cancel
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(!req.cancel());
    QVERIFY(req.start());
    QVERIFY(!req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.cancel());
    QVERIFY(!req.cancel());
    QVERIFY(req.state() == QContactAbstractRequest::CanceledState);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 0);
    
    // Remove same ones again
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QTRY_COMPARE(resultSpy.count(), 1);
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    // NOTE: In emulator removing a nonexisting contact will return DoesNotExistError
    // and in hardware there will be no error.
    QVERIFY(req.error() == QContactManager::DoesNotExistError || req.error() == QContactManager::NoError);
    if (req.errorMap().count()) {
        QVERIFY(req.errorMap().count() == 2);
        QVERIFY(req.errorMap().value(0) == QContactManager::DoesNotExistError);
        QVERIFY(req.errorMap().value(1) == QContactManager::DoesNotExistError);
    }
}

void tst_SimCMAsync::detailDefinitionFetchReq()
{
    QContactDetailDefinitionFetchRequest req;
    req.setManager(m_cm);
    
    QSignalSpy stateSpy(&req, SIGNAL(stateChanged(QContactAbstractRequest::State)));
    QSignalSpy resultSpy(&req, SIGNAL(resultsAvailable()));

    // Fetch all
    req.setContactType(QContactType::TypeContact);
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NoError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.definitions().count());
    QVERIFY(req.definitions() == m_cm->detailDefinitions(req.contactType()));
    
    // Test cancel
    stateSpy.clear();
    resultSpy.clear();
    QVERIFY(!req.cancel());
    QVERIFY(req.start());
    QVERIFY(!req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.cancel());
    QVERIFY(!req.cancel());
    QVERIFY(req.state() == QContactAbstractRequest::CanceledState);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 0);

    // Fetch some defs
    stateSpy.clear();
    resultSpy.clear();
    req.setDefinitionNames(QStringList() << QContactPhoneNumber::DefinitionName << QContactNote::DefinitionName);
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QTRY_COMPARE(resultSpy.count(), 1);
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::DoesNotExistError);
    QVERIFY(req.errorMap().value(1) == QContactManager::DoesNotExistError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
    QVERIFY(req.definitions().count() == 1);
    QVERIFY(req.definitions().contains(QContactPhoneNumber::DefinitionName));
    
    // Fetch non-existing type
    stateSpy.clear();
    resultSpy.clear();
    req.setContactType(QContactType::TypeGroup);
    req.setDefinitionNames(QStringList());
    QVERIFY(req.start());
    QVERIFY(req.state() == QContactAbstractRequest::ActiveState);
    QVERIFY(stateSpy.count() == 1);
    QVERIFY(req.waitForFinished(0));
    QVERIFY(req.state() == QContactAbstractRequest::FinishedState);
    QVERIFY(req.error() == QContactManager::NotSupportedError);
    QVERIFY(stateSpy.count() == 2);
    QVERIFY(resultSpy.count() == 1);
}

void tst_SimCMAsync::notSupportedRequests()
{
    QVERIFY(!m_cm->hasFeature(QContactManager::Relationships));
    QContactRelationshipFetchRequest rfreq;
    rfreq.setManager(m_cm);
    QVERIFY(!rfreq.start());
    QContactRelationshipRemoveRequest rrreq;
    rrreq.setManager(m_cm);
    QVERIFY(!rrreq.start());
    
    QVERIFY(!m_cm->hasFeature(QContactManager::MutableDefinitions));
    QContactDetailDefinitionRemoveRequest ddrreq;
    ddrreq.setManager(m_cm);
    QVERIFY(!ddrreq.start());
    QContactDetailDefinitionSaveRequest ddsreq;
    ddsreq.setManager(m_cm);
    QVERIFY(!ddsreq.start());
}

void tst_SimCMAsync::multipleRequests()
{
    // Save some contacts
    QContact c1 = saveContact("a", "1234567");
    QContact c2 = saveContact("b", "7654321");
    
    QContactLocalIdFetchRequest req1;
    req1.setManager(m_cm);

    QContactFetchRequest req2;
    req2.setManager(m_cm);
    
    QVERIFY(req1.start());
    QVERIFY(!req2.start());
    QVERIFY(req1.isActive());
    QVERIFY(!req2.isActive());
    QVERIFY(!req2.waitForFinished(0));
    QVERIFY(req1.waitForFinished(0));
    QVERIFY(req1.error() == QContactManager::NoError);
        
    QVERIFY(req2.start());
    QVERIFY(!req1.start());
    QVERIFY(req2.isActive());
    QVERIFY(!req1.isActive());
    QVERIFY(req1.waitForFinished(0)); // already at finished state so will return true
    QVERIFY(req2.waitForFinished(0));
    QVERIFY(req2.error() == QContactManager::NoError);
}


QTEST_MAIN(tst_SimCMAsync)
#include "tst_simcmasync.moc"
