/*
* Copyright (C) 2010 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
*
* 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 St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "ktelepathyaccountswidget_p.h"
#include "ktelepathyaccountswidget.h"

#include <KDebug>
#include <KMessageBox>
#include <QTimer>

#include <Nepomuk/ResourceManager>
#include <Nepomuk/Variant>
#include <Nepomuk/Query/QueryServiceClient>
#include <Nepomuk/Query/ComparisonTerm>
#include <Nepomuk/Query/ResourceTypeTerm>
#include <Nepomuk/Query/ResourceTerm>
#include <Nepomuk/Query/AndTerm>
#include <Nepomuk/Query/OrTerm>
#include <Nepomuk/Query/Result>

#include "ontologies/pimo.h"
#include "ontologies/nco.h"
#include "ontologies/telepathy.h"
#include "ontologies/informationelement.h"
#include "ontologies/person.h"
#include "ontologies/imaccount.h"


KTelepathyAccountsWidgetPrivate::KTelepathyAccountsWidgetPrivate(KTelepathyAccountsWidget *parent)
    : q_ptr(parent)
{
    Q_Q(KTelepathyAccountsWidget);

    Nepomuk::ResourceManager::instance()->init();

    // Check if Nepomuk Query service client is up and running
    if (!Nepomuk::Query::QueryServiceClient::serviceAvailable()) {
        // That's a failure
        KMessageBox::error(q, i18n("The Nepomuk Query Service client is not available on your system. "
                                      "Contactlist requires the query service client to be available: please "
                                      "check your system settings"));
    }

    Nepomuk::Thing me(QUrl::fromEncoded("nepomuk:/myself"));


    // Loop through all the grounding instances of this person
    foreach (Nepomuk::InformationElement resource, me.groundingOccurrences()) {
        // See if this grounding instance is of type nco:contact.
        if (resource.hasType(Nepomuk::Vocabulary::NCO::PersonContact())) {
            // FIXME: We are going to assume the first NCO::PersonContact is the
            // right one. Can we improve this?
            mePersonContact = resource;
            break;
        }
    }
}


KTelepathyAccountsWidgetPrivate::~KTelepathyAccountsWidgetPrivate()
{
}


bool KTelepathyAccountsWidgetPrivate::isAllowed(Tp::ConnectionPresenceType status)
{
    if (status == Tp::ConnectionPresenceTypeUnset        && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceAvailable))
        return true;
    if (status == Tp::ConnectionPresenceTypeOffline      && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceOffline))
        return true;
    if (status == Tp::ConnectionPresenceTypeAvailable    && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceAvailable))
        return true;
    if (status == Tp::ConnectionPresenceTypeAway         && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceAway))
        return true;
    if (status == Tp::ConnectionPresenceTypeExtendedAway && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceAway))
        return true;
    if (status == Tp::ConnectionPresenceTypeHidden       && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceHidden))
        return true;
    if (status == Tp::ConnectionPresenceTypeBusy         && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceBusy))
        return true;
    if (status == Tp::ConnectionPresenceTypeUnknown      && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceUnknown))
        return true;
    if (status == Tp::ConnectionPresenceTypeError        && (connectionPresences & KTelepathyAccountsWidget::ConnectionPresenceError))
        return true;
    return false;
}


void KTelepathyAccountsWidgetPrivate::removeAllItems()
{
    Q_Q(KTelepathyAccountsWidget);
    kDebug() << q;

    while (q->count() != 0)
        q->removeItem(0);
}


void KTelepathyAccountsWidgetPrivate::startQuery()
{
    Q_Q(KTelepathyAccountsWidget);
    kDebug() << q;

    switch (displayMode) {
        case KTelepathyAccountsWidget::DisplayModeAccounts:
            //QTimer::singleShot(0, q, SLOT(__k__displayModeAccounts())); //TODO Asyncronous?
            __k__displayModeAccounts();
            break;
        case KTelepathyAccountsWidget::DisplayModeContacts:
            //QTimer::singleShot(0, q, SLOT(__k__displayModeContacts())); //TODO Asyncronous?
            __k__displayModeContacts();
            break;
        case KTelepathyAccountsWidget::DisplayModeMetaContacts:
            //QTimer::singleShot(0, q, SLOT(__k__displayModeMetaContacts())); //TODO Asyncronous?
            __k__displayModeMetaContacts();
            break;
    }
}


void KTelepathyAccountsWidgetPrivate::__k__displayModeAccounts()
{
    Q_Q(KTelepathyAccountsWidget);

    // List available accounts
    QList<Nepomuk::Query::Result> results;
    {
        using namespace Nepomuk::Query;

        ResourceTypeTerm aIMAccountTerm(Nepomuk::Vocabulary::NCO::IMAccount());
        ComparisonTerm hasIMAccountTerm(Nepomuk::Vocabulary::NCO::hasIMAccount(),
                                       ResourceTerm(mePersonContact));
        hasIMAccountTerm.setInverted(true);

        Query query(AndTerm(aIMAccountTerm, hasIMAccountTerm));

        bool queryResult = true;
        results = QueryServiceClient::syncQuery(query, &queryResult);

        if (!queryResult) {
            KMessageBox::error(0, i18n("It was not possible to query Nepomuk database. Please check your "
                                       "installation and make sure Nepomuk is running."));
        }
    }

    foreach (const Nepomuk::Query::Result &result, results) {
        kDebug() << result.resource();
        Nepomuk::IMAccount foundAccount = result.resource();

        Tp::ConnectionPresenceType status = currentConnectionPresence(foundAccount);
        QIcon& icon = connectionPresenceIcon(status);

        q->addItem(icon, foundAccount.imIDs().first(), QVariant(foundAccount.resourceUri()));
    }

}


void KTelepathyAccountsWidgetPrivate::__k__displayModeContacts()
{
    Q_Q(KTelepathyAccountsWidget);

    // List available contacts
    QList<Nepomuk::Query::Result> results;
    {
        using namespace Nepomuk::Query;

        ComparisonTerm myIMAccountTerm(Nepomuk::Vocabulary::NCO::hasIMAccount(),
                                       ResourceTerm(mePersonContact));
        myIMAccountTerm.setInverted(true);
        ComparisonTerm isBuddyTerm(Nepomuk::Vocabulary::Telepathy::isBuddyOf(),
                                   myIMAccountTerm);
        ResourceTypeTerm aPersonContactTerm(Nepomuk::Vocabulary::NCO::PersonContact());
        ComparisonTerm contactIMAccountTerm(Nepomuk::Vocabulary::NCO::hasIMAccount(), isBuddyTerm);

        Query query(AndTerm(aPersonContactTerm, contactIMAccountTerm));

        bool queryResult = true;
        results = QueryServiceClient::syncQuery(query, &queryResult);

        if (!queryResult) {
            KMessageBox::error(0, i18n("It was not possible to query Nepomuk database. Please check your "
                                       "installation and make sure Nepomuk is running."));
        }
    }

    foreach (const Nepomuk::Query::Result &result, results) {
        kDebug() << result.resource();
        Nepomuk::PersonContact foundContact = result.resource();

        Tp::ConnectionPresenceType status = currentConnectionPresence(foundContact);
        if (isAllowed(status)) {
            QIcon& icon = connectionPresenceIcon(status);
            q->addItem(icon, foundContact.iMAccounts().first().imIDs().first(), QVariant(foundContact.resourceUri()));
        }
    }
}


void KTelepathyAccountsWidgetPrivate::__k__displayModeMetaContacts()
{
    Q_Q(KTelepathyAccountsWidget);

//TODO Fix Query
    // List available metacontacts
    QList<Nepomuk::Query::Result> results;
    {
        using namespace Nepomuk::Query;

        Query query(ResourceTypeTerm(Nepomuk::Vocabulary::PIMO::Person()));

        bool queryResult = true;
        results = QueryServiceClient::syncQuery(query, &queryResult);

        if (!queryResult) {
            KMessageBox::error(0, i18n("It was not possible to query Nepomuk database. Please check your "
                                       "installation and make sure Nepomuk is running."));
        }
    }

    foreach (const Nepomuk::Query::Result &result, results) {
        kDebug() << result.resource();
        Nepomuk::Person foundMetaContact = result.resource();

        // Exclude "myself" from results
        // TODO Can we do it in the query?
        if (foundMetaContact.resourceUri().toString() == "nepomuk:/myself")
            continue;

        Tp::ConnectionPresenceType status = currentConnectionPresence(foundMetaContact);
        if (isAllowed(status)) {
            QIcon& icon = connectionPresenceIcon(status);
            q->addItem(icon, foundMetaContact.label(), QVariant(foundMetaContact.resourceUri()));
        }
    }
}


Tp::ConnectionPresenceType KTelepathyAccountsWidgetPrivate::currentConnectionPresence(Nepomuk::IMAccount account)
{
    return (Tp::ConnectionPresenceType)(account.statusTypes().first());
}


Tp::ConnectionPresenceType KTelepathyAccountsWidgetPrivate::currentConnectionPresence(Nepomuk::PersonContact contact)
{
    return (Tp::ConnectionPresenceType)(contact.iMAccounts().first().statusTypes().first());
}


Tp::ConnectionPresenceType KTelepathyAccountsWidgetPrivate::currentConnectionPresence(Nepomuk::Person metacontact)
{
    // Get all Telepathy PersonContacts which belong to this metacontact, and match our criteria
    QList< Nepomuk::Query::Result > results;
    {
        using namespace Nepomuk::Query;
        using namespace Nepomuk::Vocabulary;


        // match grouding occurrences of metacontact
        ComparisonTerm retgoterm(PIMO::groundingOccurrence(),
                                 ResourceTerm(metacontact));
        retgoterm.setInverted(true);

        Query query(AndTerm(ResourceTypeTerm(NCO::PersonContact()), retgoterm));

        kDebug() << query.toSparqlQuery();


        bool queryResult = true;
        results = QueryServiceClient::syncQuery(query, &queryResult);

        if (!queryResult) {
            KMessageBox::error(0, i18n("It was not possible to query Nepomuk database. Please check your "
                                       "installation and make sure Nepomuk is running."));
        }
    }

    Tp::ConnectionPresenceType ret = Tp::ConnectionPresenceTypeOffline;
    // Iterate over all the IMAccounts/PersonContacts found.
    foreach (const Nepomuk::Query::Result &result, results) {
        Nepomuk::PersonContact foundPersonContact(result.resource());
        Tp::ConnectionPresenceType connectionPresence = (Tp::ConnectionPresenceType)foundPersonContact.iMAccounts().first().statusTypes().first();
        switch (connectionPresence)
        {
            case Tp::ConnectionPresenceTypeAvailable:
                return connectionPresence;
            //TODO Fix here
            case Tp::ConnectionPresenceTypeHidden:
            case Tp::ConnectionPresenceTypeBusy:
            case Tp::ConnectionPresenceTypeAway:
            case Tp::ConnectionPresenceTypeExtendedAway:
                ret = connectionPresence;
                break;
            case Tp::ConnectionPresenceTypeUnset:
            case Tp::ConnectionPresenceTypeOffline:
            case Tp::ConnectionPresenceTypeUnknown:
            case Tp::ConnectionPresenceTypeError:
            default:
                break;
        }
    }
    return ret;
}


QString KTelepathyAccountsWidgetPrivate::connectionPresenceIconName(Tp::ConnectionPresenceType status)
{
    switch (status)
    {
        case Tp::ConnectionPresenceTypeHidden:
            return "user-invisible";
        case Tp::ConnectionPresenceTypeAvailable:
            return "user-online";
        case Tp::ConnectionPresenceTypeAway:
            return "user-away";
        case Tp::ConnectionPresenceTypeBusy:
            return "user-busy";
        case Tp::ConnectionPresenceTypeExtendedAway:
            return "user-away-extended";
        case Tp::ConnectionPresenceTypeUnset:
        case Tp::ConnectionPresenceTypeOffline:
        case Tp::ConnectionPresenceTypeUnknown:
        case Tp::ConnectionPresenceTypeError:
        default:
            return "user-offline";
    }
}


QIcon& KTelepathyAccountsWidgetPrivate::connectionPresenceIcon(Tp::ConnectionPresenceType status)
{
    QString iconName = connectionPresenceIconName(status);
    return *new KIcon(iconName);
}


KTelepathyAccountsWidget::KTelepathyAccountsWidget(QWidget* parent)
    : QComboBox(parent),
      d_ptr(new KTelepathyAccountsWidgetPrivate(this))
{
    kDebug();
    Q_D(KTelepathyAccountsWidget);

    d->displayMode = DisplayModeContacts;
    d->connectionPresences = ConnectionPresenceAnyConnected;

    d->startQuery();
//    connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(__k__onCurrentIndexChanged(int))); //TODO remove
}


KTelepathyAccountsWidget::KTelepathyAccountsWidget(ConnectionPresences allowedConnectionPresences,
                                                   QWidget* parent)
    : QComboBox(parent),
      d_ptr(new KTelepathyAccountsWidgetPrivate(this))
{
    kDebug();
    Q_D(KTelepathyAccountsWidget);

    d->displayMode = DisplayModeContacts;
    d->connectionPresences = allowedConnectionPresences;

    d->startQuery();
//    connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(__k__onCurrentIndexChanged(int))); //TODO remove
}

KTelepathyAccountsWidget::KTelepathyAccountsWidget(DisplayMode displayMode,
                                                   ConnectionPresences allowedConnectionPresences,
                                                   QWidget* parent)
    : QComboBox(parent),
      d_ptr(new KTelepathyAccountsWidgetPrivate(this))
{
    kDebug();
    Q_D(KTelepathyAccountsWidget);

    d->displayMode = displayMode;
    d->connectionPresences = allowedConnectionPresences;

    d->startQuery();
//    connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(__k__onCurrentIndexChanged(int))); //TODO remove
}


KTelepathyAccountsWidget::~KTelepathyAccountsWidget()
{
}


//TODO Error if mode != DisplayModeAccounts
Nepomuk::IMAccount KTelepathyAccountsWidget::currentAccount()
{
    QUrl resourceUrl = itemData(currentIndex()).toUrl();
    kDebug() << resourceUrl;
    Nepomuk::IMAccount imaccount(resourceUrl);
    kDebug() << imaccount;
    return imaccount;
}


//TODO Error if mode != DisplayModeContacts
Nepomuk::PersonContact KTelepathyAccountsWidget::currentContact()
{
    QUrl resourceUrl = itemData(currentIndex()).toUrl();
    kDebug() << resourceUrl;
    Nepomuk::PersonContact personcontact(resourceUrl);
    kDebug() << personcontact;
    return personcontact;
}


//TODO Error if mode != DisplayModeMetaContacts
Nepomuk::Person KTelepathyAccountsWidget::currentMetaContact()
{
    QUrl resourceUrl = itemData(currentIndex()).toUrl();
    kDebug() << resourceUrl;
    Nepomuk::Person person(resourceUrl);
    kDebug() << person;
    return person;
}


Nepomuk::Variant KTelepathyAccountsWidget::currentResource()
{
    QUrl resourceUrl = itemData(currentIndex()).toUrl();
    kDebug() << resourceUrl;
    Nepomuk::Variant variant(resourceUrl);
    kDebug() << variant;
    return variant;
}


void KTelepathyAccountsWidget::setConnectionPresences(ConnectionPresences connectionPresences)
{
    Q_D(KTelepathyAccountsWidget);

    if (d->connectionPresences == connectionPresences)
        return;

    d->connectionPresences = connectionPresences;

    d->removeAllItems();
    d->startQuery();
}


KTelepathyAccountsWidget::ConnectionPresences KTelepathyAccountsWidget::connectionPresences()
{
    Q_D(KTelepathyAccountsWidget);
    return d->connectionPresences;
}


void KTelepathyAccountsWidget::setDisplayMode(DisplayMode displayMode)
{
    Q_D(KTelepathyAccountsWidget);

    if (d->displayMode == displayMode)
        return;

    d->displayMode = displayMode;

    d->removeAllItems();
    d->startQuery();
}


KTelepathyAccountsWidget::DisplayMode KTelepathyAccountsWidget::displaymode()
{
    Q_D(KTelepathyAccountsWidget);
    return d->displayMode;
}


void KTelepathyAccountsWidgetPrivate::__k__onCurrentIndexChanged(int index)
{
    Q_Q(KTelepathyAccountsWidget);

    kDebug() << index;

    if (index < 0)
        return;

    switch (displayMode)
    {
        case KTelepathyAccountsWidget::DisplayModeAccounts:
        {
            QUrl resourceUrl = q->itemData(index).toUrl();
            kDebug() << resourceUrl;
            Nepomuk::IMAccount currentAccount(resourceUrl);
            kDebug() << currentAccount;
            kDebug() << currentAccount.imIDs().first();
            return;
        }
        case KTelepathyAccountsWidget::DisplayModeContacts:
        {
            QUrl resourceUrl = q->itemData(index).toUrl();
            kDebug() << resourceUrl;
            Nepomuk::PersonContact currentContact(resourceUrl);
            kDebug() << currentContact;
            kDebug() << currentContact.iMAccounts().first().imIDs().first();
            return;
        }
        case KTelepathyAccountsWidget::DisplayModeMetaContacts:
            QUrl resourceUrl = q->itemData(index).toUrl();
            kDebug() << resourceUrl;
            Nepomuk::Person currentMetaContact(resourceUrl);
            kDebug() << currentMetaContact;
            return;
    }
}


void KTelepathyAccountsWidgetPrivate::test()
{
    kDebug() << "ConnectionPresenceUnset           =" << KTelepathyAccountsWidget::ConnectionPresenceUnset;
    kDebug() << "ConnectionPresenceOffline         =" << KTelepathyAccountsWidget::ConnectionPresenceOffline;
    kDebug() << "ConnectionPresenceAvailable       =" << KTelepathyAccountsWidget::ConnectionPresenceAvailable;
    kDebug() << "ConnectionPresenceAway            =" << KTelepathyAccountsWidget::ConnectionPresenceAway;
    kDebug() << "ConnectionPresenceExtendedAway    =" << KTelepathyAccountsWidget::ConnectionPresenceExtendedAway;
    kDebug() << "ConnectionPresenceHidden          =" << KTelepathyAccountsWidget::ConnectionPresenceHidden;
    kDebug() << "ConnectionPresenceBusy            =" << KTelepathyAccountsWidget::ConnectionPresenceBusy;
    kDebug() << "ConnectionPresenceUnknown         =" << KTelepathyAccountsWidget::ConnectionPresenceUnknown;
    kDebug() << "ConnectionPresenceError           =" << KTelepathyAccountsWidget::ConnectionPresenceError;
    kDebug() << "ConnectionPresenceAnyConnected    =" << KTelepathyAccountsWidget::ConnectionPresenceAnyConnected;
    kDebug() << "ConnectionPresenceAnyDisconnected =" << KTelepathyAccountsWidget::ConnectionPresenceAnyDisconnected;
    kDebug() << "ConnectionPresenceAny             =" << KTelepathyAccountsWidget::ConnectionPresenceAny;

    kDebug() << "Current connectionPresences =" << connectionPresences;

    kDebug() << "ConnectionPresenceTypeUnset        " << isAllowed(Tp::ConnectionPresenceTypeUnset);
    kDebug() << "ConnectionPresenceTypeOffline      " << isAllowed(Tp::ConnectionPresenceTypeOffline);
    kDebug() << "ConnectionPresenceTypeAvailable    " << isAllowed(Tp::ConnectionPresenceTypeAvailable);
    kDebug() << "ConnectionPresenceTypeAway         " << isAllowed(Tp::ConnectionPresenceTypeAway);
    kDebug() << "ConnectionPresenceTypeExtendedAway " << isAllowed(Tp::ConnectionPresenceTypeExtendedAway);
    kDebug() << "ConnectionPresenceTypeHidden       " << isAllowed(Tp::ConnectionPresenceTypeHidden);
    kDebug() << "ConnectionPresenceTypeBusy         " << isAllowed(Tp::ConnectionPresenceTypeBusy);
    kDebug() << "ConnectionPresenceTypeUnknown      " << isAllowed(Tp::ConnectionPresenceTypeUnknown);
    kDebug() << "ConnectionPresenceTypeError        " << isAllowed(Tp::ConnectionPresenceTypeError);
}

#include "ktelepathyaccountswidget.moc"
