/****************************************************************************
**
** 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 "eventloggerengine_maemo_p.h"
#include "telepathyengine_maemo_p.h"
#include "maemohelpers_p.h"
#include "qmessageservice_maemo_p.h"
#include <QDebug>

QTM_BEGIN_NAMESPACE


Q_GLOBAL_STATIC(EventLoggerEngine,eventLoggerEngine);


EventLoggerEngine* EventLoggerEngine::instance()
{
    return eventLoggerEngine();
}


EventLoggerEngine::EventLoggerEngine(QObject *parent):QObject(parent), _filterId(0)
{
  //    qDebug() << "EventLoggerEngine::EventLoggerEngine";
    DBusError err=DBUS_ERROR_INIT;
    active = false;
    g_type_init();
    dbus = dbus_bus_get(DBUS_BUS_SESSION, &err); // Create dummy Dbus object and
    dbus_connection_setup_with_g_main (dbus, NULL); //add it to g_mainloop because eventlogger library expects that someone alse has added session bus to g_mainloop
    el=rtcom_el_new ();
    if(!RTCOM_IS_EL(el)) qDebug() << "EventLoggerEngine::EventLoggerEngine():Could't create RTComEl\n";

    queryThread=0;
    //    queryThread.run();
    //    connect(queryThread, SIGNAL(messagesFound(const QMessageIdList &)),this, SLOT(messagesFound_(const QMessageIdList &)));


    g_signal_connect(G_OBJECT(el), "new-event", G_CALLBACK(new_event_cb),(void*)this);
    g_signal_connect(G_OBJECT(el), "event-deleted", G_CALLBACK(event_deleted_cb),(void*)this);
    g_signal_connect(G_OBJECT(el), "event-updated", G_CALLBACK(event_updated_cb),(void*)this);

    qRegisterMetaType<QMessageIdList>("QMessageIdList");
}


void EventLoggerEngine::new_event_cb(RTComEl *el,int event_id,
                                    const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                    const char *group_uid,const char *service,EventLoggerEngine *p)
{
  Q_UNUSED(el);
  p->newEvent(event_id, local_uid,remote_uid ,remote_ebook_uid,group_uid,service);
};

void EventLoggerEngine::event_deleted_cb(RTComEl *el,int event_id,
                                    const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                    const char *group_uid,const char *service,EventLoggerEngine *p)
{
  Q_UNUSED(el);
  p->deletedEvent(event_id, local_uid,remote_uid ,remote_ebook_uid,group_uid,service);
};

void EventLoggerEngine::event_updated_cb(RTComEl *el,int event_id,
                                    const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                    const char *group_uid,const char *service,EventLoggerEngine *p)
{
  Q_UNUSED(el);
  p->updatedEvent(event_id, local_uid,remote_uid ,remote_ebook_uid,group_uid,service);
};


void EventLoggerEngine::newEvent(int event_id,
                                const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                const char *group_uid,const char *service)
{
  Q_UNUSED(local_uid); Q_UNUSED(remote_uid);Q_UNUSED(remote_ebook_uid);
  Q_UNUSED(group_uid);Q_UNUSED(service);
   QString eventIds=QString("el")+QString::number(event_id);
    QMessageId id(eventIds);

 qDebug()  << "newEvent id=" << eventIds;
    notification(event_id,service,QMessageStorePrivate::Added);
}

void EventLoggerEngine::updatedEvent(int event_id,
                                const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                const char *group_uid,const char *service)
{
  Q_UNUSED(local_uid); Q_UNUSED(remote_uid);Q_UNUSED(remote_ebook_uid);
  Q_UNUSED(group_uid);Q_UNUSED(service);
   QString eventIds=QString("el")+QString::number(event_id);
    QMessageId id(eventIds);

 qDebug()  << "updatedEvent id=" << eventIds;
    notification(event_id,service,QMessageStorePrivate::Updated);
}

void EventLoggerEngine::deletedEvent(int event_id,
                                const char *local_uid,const char *remote_uid,const char *remote_ebook_uid,
                                const char *group_uid,const char *service)
{
  Q_UNUSED(local_uid); Q_UNUSED(remote_uid);Q_UNUSED(remote_ebook_uid);
  Q_UNUSED(group_uid);Q_UNUSED(service);
   QString eventIds=QString("el")+QString::number(event_id);
    QMessageId id(eventIds);

    qDebug()  << "deletedEvent id=" << eventIds;
    notification(event_id,service,QMessageStorePrivate::Removed);
}

QMessageManager::NotificationFilterId EventLoggerEngine::registerNotificationFilter(QMessageStorePrivate& aPrivateStore,
                                                                           const QMessageFilter &filter)
{
    iListenForNotifications = true;
    ipMessageStorePrivate = &aPrivateStore;

    int filterId = ++_filterId;
    _filters.insert(filterId, filter);
    return filterId;
}

void EventLoggerEngine::unregisterNotificationFilter(QMessageManager::NotificationFilterId notificationFilterId)
{
    _filters.remove(notificationFilterId);
    if (_filters.count() == 0) {
        iListenForNotifications = false;
    }
}
QMessage EventLoggerEngine::eventToMessage(RTComElEvent & ev)
{

    QMessage message;

    if (!strcmp(ev.fld_service, "RTCOM_EL_SERVICE_SMS")) {
        message.setType(QMessage::Sms);
    } else if (!strcmp(ev.fld_service,"RTCOM_EL_SERVICE_CHAT")) {
        message.setType(QMessage::InstantMessage);
    } else {
        message.setType(QMessage::NoType);  // Other type, as exampele voice Call
    };

    message.setParentAccountId(QMessageAccountId(QString("y/Account/%1").arg(ev.fld_local_uid)));

    if (!ev.fld_is_read) {
        message.setStatus(QMessage::Read);
    };
    message.setPriority(QMessage::NormalPriority);
    message.setDate(QDateTime::fromTime_t(ev.fld_start_time));
    message.setReceivedDate(QDateTime::fromTime_t(ev.fld_start_time));
    if (ev.fld_outgoing) QMessagePrivate::setStandardFolder(message,QMessage::SentFolder);
    else
      QMessagePrivate::setStandardFolder(message,QMessage::InboxFolder);
    //    qDebug() << "event_type:"  << ev.fld_event_type << ev.fld_event_type_id << "Outgoing:" << ev.fld_outgoing << " Folder:" << message.standardFolder();

    if (ev.fld_outgoing) {
        // Outgoing message
        message.setFrom(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_local_uid)));
        QMessagePrivate::setSenderName(message, QString(ev.fld_local_uid));
        message.setTo(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_remote_uid)));
    } else {
        // Incoming message
        message.setFrom(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_remote_uid)));
        QMessagePrivate::setSenderName(message, QString(ev.fld_remote_uid));
        QMessageAddressList messageAddresslist;
        messageAddresslist.append(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_local_uid)));
        message.setTo(messageAddresslist);
    }

    message.setBody(QString::fromUtf8(ev.fld_free_text));
    QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
    privateMessage->_id = QMessageId(QString("el")+QString::number(ev.fld_id));
    privateMessage->_modified = false;
    // qDebug() << "id:" << message.id().toString() << "From:" << message.from().addressee() << "Text:" << message.textContent();
    return message;

}
void EventLoggerEngine::addEvent(QMessage &message)
{
    qDebug() << "EventLoggerEngine::addEvent()\n";
    RTComElEvent *ev = rtcom_el_event_new();

    if (message.type()==QMessage::Sms) {
        RTCOM_EL_EVENT_SET_FIELD(ev,service,(gchar *)"RTCOM_EL_SERVICE_SMS");
    }
    else if (message.type()==QMessage::InstantMessage) {
        RTCOM_EL_EVENT_SET_FIELD(ev,service,(gchar *)"RTCOM_EL_SERVICE_CHAT");
        RTCOM_EL_EVENT_SET_FIELD(ev,remote_uid,(gchar *)message.from().addressee().toStdString().c_str());
        RTCOM_EL_EVENT_SET_FIELD(ev,group_uid,(gchar *)message.from().addressee().toStdString().c_str());
    }
    else return; // Invalid messge type

    RTCOM_EL_EVENT_SET_FIELD(ev,event_type,(gchar *)"RTCOM_EL_EVENTTYPE_SMS_INBOUND");
    RTCOM_EL_EVENT_SET_FIELD(ev,local_uid,(gchar *)"ring/tel/ring");
    RTCOM_EL_EVENT_SET_FIELD(ev,local_name,(gchar *)"<SelfHandle>");
    RTCOM_EL_EVENT_SET_FIELD(ev,remote_uid,(gchar *)message.from().addressee().toStdString().c_str());
    RTCOM_EL_EVENT_SET_FIELD(ev,group_uid,(gchar *)message.from().addressee().toStdString().c_str());
    RTCOM_EL_EVENT_SET_FIELD(ev,start_time,time(NULL));
    RTCOM_EL_EVENT_SET_FIELD(ev,remote_ebook_uid,(gchar *)"1");
    RTCOM_EL_EVENT_SET_FIELD(ev,free_text,(gchar *)message.textContent().toStdString().c_str());
    rtcom_el_add_event(el,ev,NULL);
    rtcom_el_event_free(ev);
}

bool EventLoggerEngine::deleteMessage(const QMessageId& id)
{
  int status=rtcom_el_delete_event(el,id.toString().remove("el").toInt(),NULL);
  return status==0;
}

QMessage EventLoggerEngine::message(const QMessageId& id)
{

    QMessage message;

    // qDebug() << "EventLoggerEngine::getMessage id=" << id.toString();

    RTComElEvent ev;
    bzero(&ev,sizeof(ev));
    RTComElQuery *q=rtcom_el_query_new(el);
    rtcom_el_query_prepare(q,"id",id.toString().remove("el").toInt(),RTCOM_EL_OP_EQUAL,NULL);
    RTComElIter *iter=rtcom_el_get_events(el,q);
    g_object_unref(q);
    if(iter && rtcom_el_iter_first(iter))
    {
     gboolean res=rtcom_el_iter_get_full(iter,&ev);
     if(res) {
#if 0
         printf("got event id=%d service_id=%d event_typ_id=%d\n\
local_uid=%s local_name=%s\n\
remote_uid=%s remote_name=%s remote_ebook_uid=%s\n\
channel=%s free_text=%s group_uid=%s\n\
service=%s event_type=%s\n\
additional_text=%s icon_name=%s pango_markup=%s\n",
                    ev.fld_id,ev.fld_service_id,ev.fld_event_type_id,
                    ev.fld_local_uid,ev.fld_local_name,
                    ev.fld_remote_uid,ev.fld_remote_name,ev.fld_remote_ebook_uid,
                    ev.fld_channel,ev.fld_free_text,ev.fld_group_uid,
                    ev.fld_service,ev.fld_event_type,
                    ev.fld_additional_text,ev.fld_icon_name,ev.fld_pango_markup);
#endif
         if (!strcmp(ev.fld_service, "RTCOM_EL_SERVICE_SMS")) {
             message.setType(QMessage::Sms);
         } else if (!strcmp(ev.fld_service,"RTCOM_EL_SERVICE_CHAT")) {
             message.setType(QMessage::InstantMessage);
         } else {
             message.setType(QMessage::NoType);  // Other type, as exampele voice Call
         };
         //QMessageAccount account =  TelepathyEngine::instance()->account(QMessageAccountId(QString("/y/Account%1").arg(ev.fld_local_uid)));

         message.setParentAccountId(QMessageAccountId(QString("/y/Account/%1").arg(ev.fld_local_uid)));
         if (!ev.fld_is_read) {
             message.setStatus(QMessage::Read);
         };
         message.setPriority(QMessage::NormalPriority);
         message.setDate(QDateTime::fromTime_t(ev.fld_start_time));
         message.setReceivedDate(QDateTime::fromTime_t(ev.fld_start_time));
	 if (ev.fld_outgoing) QMessagePrivate::setStandardFolder(message,QMessage::SentFolder);
	 else
	   QMessagePrivate::setStandardFolder(message,QMessage::InboxFolder);

         if (ev.fld_outgoing) {
             // Outgoing message
             message.setFrom(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_local_uid)));
             QMessagePrivate::setSenderName(message, QString(ev.fld_local_uid));
             message.setTo(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_remote_uid)));
         } else {
             // Incoming message
             message.setFrom(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_remote_uid)));
             QMessagePrivate::setSenderName(message, QString(ev.fld_remote_uid));
             QMessageAddressList messageAddresslist;
             messageAddresslist.append(QMessageAddress(QMessageAddress::Phone, QString(ev.fld_local_uid)));
             message.setTo(messageAddresslist);
         }

         message.setBody(QString::fromUtf8(ev.fld_free_text));
         QMessagePrivate* privateMessage = QMessagePrivate::implementation(message);
         privateMessage->_id = id;
         privateMessage->_modified = false;
	 //  qDebug() << "id:" << message.id().toString() << "From:" << message.from().addressee() << "Text:" << message.textContent();
     };
    };
    if(iter) g_object_unref(iter);
    //    debugMessage(message);

    MessageCache::instance()->insert(message);

    return message;

}

void EventLoggerEngine::debugMessage(QMessage &message)
{
    qDebug() << "id:" << message.id().toString() << "type:" << message.type() << "size:" << message.size() << "status:" << message.status() << "priority:" << message.priority();
    qDebug() << "AccountId:" << message.parentAccountId().toString() << "StantardFolder" << message.standardFolder() << "parenFolderId:" << message.parentFolderId().toString();
    qDebug() << "Date:" << message.date() << "receivedDate:" << message.receivedDate() << "Subject:" << message.subject();
    qDebug() << "From:" << message.from().addressee();
    qDebug() << "To:" << (message.to().isEmpty() ? "**none**" : message.to().first().addressee());
    qDebug() << "Body:" <<message.textContent();
}


void EventLoggerEngine::notification(int eventId, QString service,QMessageStorePrivate::NotificationType notificationType)
{


    QMessageManager::NotificationFilterIdSet matchingFilters;
    // qDebug() << "EventLoggerEngine::notification id=" << eventId;

    // Copy the filter map to protect against modification during traversal
    QMap<int, QMessageFilter> filters(_filters);
    QMap<int, QMessageFilter>::const_iterator it = filters.begin(), end = filters.end();
    QMessage msg;
    bool messageRetrieved = false;

    for ( ; it != end; ++it) {
        const QMessageFilter &filter(it.value());
        if (filter.isEmpty()) {
            // Empty filter matches to all messages
            matchingFilters.insert(it.key());
        } else {
            QMessageFilterPrivate* privateMessageFilter = QMessageFilterPrivate::implementation(filter);
            if (!messageRetrieved && (privateMessageFilter->_field == QMessageFilterPrivate::Type)) {
                if (service == "RTCOM_EL_SERVICE_SMS") {
                    msg.setType(QMessage::Sms);
                } else if (service== "RTCOM_EL_SERVICE_CHAT") {
                    msg.setType(QMessage::InstantMessage);
                } else {
                    msg.setType(QMessage::NoType);  // Other type, as example voice calll
                }
            }
            else if (!messageRetrieved) {
                msg = this->message(QMessageId(QString("el")+QString::number(eventId)));
                if (msg.type() == QMessage::NoType) {
                    matchingFilters.clear();
                    break;
                } else {
                    messageRetrieved = true;
                }
            }
            if (privateMessageFilter->filter(msg)) {
                matchingFilters.insert(it.key());
            }
        }
    }

    if (notificationType == QMessageStorePrivate::Updated) {
        // Remove updated message from cache to make sure that message
        // will be retrieved again from backend
        MessageCache::instance()->remove(QMessageId(QString("el")+QString::number(eventId)));
    } else if (notificationType == QMessageStorePrivate::Removed) {
        // Remove removed message from cache
        MessageCache::instance()->remove(QMessageId(QString("el")+QString::number(eventId)));
    }

    if (matchingFilters.count() > 0) {
            ipMessageStorePrivate->messageNotification(notificationType,
                                                       QMessageId(QString("el")+QString::number(eventId)),
                                                       matchingFilters);
        }

}

#if 0
QMessageIdList EventLoggerEngine::filterAndOrderMessages(const QMessageFilter &filter, const QMessageSortOrder& sortOrder,
                                                    QString body, QMessageDataComparator::MatchFlags matchFlags)
{
    filterAndOrderMessages(filte, sortOrder, body, matchFlags);
}
#endif

bool EventLoggerEngine::filterMessages(QMessageServicePrivate* privateService,
                                       const QMessageFilter &filter,
                                       const QMessageSortOrder& sortOrder,
                                       QString body,
                                       QMessageDataComparator::MatchFlags matchFlags)
{

  //  qDebug() << "EventLoggerEngine::filterMessages";
  if (active) {
    qWarning() << "EventLoggerEngine::filterMessages::Service is currently busy";
    return false;
  }


  active = true;
  state = QMessageService::ActiveState;
  emit stateChanged(state);

  if(!queryThread) {
    queryThread=new QueryThread();
    connect(queryThread, SIGNAL(completed()), this, SLOT(reportMatchingIds()), Qt::QueuedConnection);
  };
  queryThread->setArgs(privateService, this, filter, body, matchFlags, sortOrder, 0,0);
  queryThread->start();

    //  return queryThread.queryMessages(filter,sortOrder,body,matchFlags);
    return true;
}

void EventLoggerEngine::messagesFound_(const QMessageIdList &ids)
{
  //  qDebug() << "EventLoggerEngine::messagesFound";
  emit messagesFound(ids,true,false); // filtered but not sorted
}


void EventLoggerEngine::reportMatchingIds()
{
    // qDebug() << "EventLoggerEngine::messagesFound" << m_ids.count();
    QMetaObject::invokeMethod(queryThread->_privateService,
                              "messagesFound",
                              Qt::QueuedConnection,
                              Q_ARG(const QMessageIdList, m_ids),
                              Q_ARG(bool, true),
                              Q_ARG(bool, false));
    completed();
}

void EventLoggerEngine::completed()
{
    active = false;
    state = QMessageService::FinishedState;
    emit stateChanged(state);
}


QMessageIdList EventLoggerEngine::filterAndOrderMessages(const QMessageFilter &filter,
                                                    const QMessageSortOrder& sortOrder,
                                                    QString body,
                                                    QMessageDataComparator::MatchFlags matchFlags)
{
  Q_UNUSED(body);
  Q_UNUSED(matchFlags);
  Q_UNUSED(sortOrder);
    QMessageId fId;  // Filtering id
    //    QMessageType fType;
    QDate fDate;
    RTComElEvent ev;
    QMessage message;
    
    const char *services[]={"RTCOM_EL_SERVICE_CHAT","RTCOM_EL_SERVICE_SMS", NULL };

    QMessageIdList idList;

    QMessageFilterPrivate* pf = QMessageFilterPrivate::implementation(filter);
#if 0
    if ((filters.count() == 1) && (pf->_field == QMessageFilterPrivate::None) && (pf->_filterList.count() == 0)) {
        if (pf->_notFilter) {
            // There is only one filter: empty ~QMessageFilter()
            // => return empty QMessageIdList
            return idList;
        } else {
            // There is only one filter: empty QMessageFilter()
            // => return all messages

            }
        }
    // Pre-filtering setup
    switch (pf->_field) {
    case QMessageFilterPrivate::Id:
    case QMessageFilterPrivate::ParentAccountId:
        {
        if (pf->_comparatorType == QMessageFilterPrivate::Equality) { // QMessageAccountId
            iNumberOfHandledFilters++;
            QMessageDataComparator::EqualityComparator cmp(static_cast<QMessageDataComparator::EqualityComparator>(pf->_comparatorValue));
            if (cmp == QMessageDataComparator::Equal) {
                QMessageAccount messageAccount = TelepathyEngine::instance()->account(QMessageAccountId(QString("/y/Account%1").arg(pf->_value.toString())));
            }
        }
       }
    }
#endif
    RTComElQuery *q=rtcom_el_query_new(el);
    rtcom_el_query_prepare(q,"service", services, RTCOM_EL_OP_IN_STRV, NULL);
    RTComElIter *iter=rtcom_el_get_events(el,q);
    g_object_unref(q);
    if(iter && rtcom_el_iter_first(iter))
       do {
         bzero(&ev,sizeof(ev));
         if(rtcom_el_iter_get_full(iter,&ev))
	   {
	     message=eventToMessage(ev);
	     // debugMessage(message);
	     if (pf->filter(message)) {
	       //   qDebug() <<"Filter :filtering match" << message.id().toString();
	       //matchingFilters.insert(it.key());
               MessageCache::instance()->insert(message);
	       idList.append(message.id());
	     };
	   };
       }
       while( rtcom_el_iter_next(iter));
#if 0
    foreach(const QMessageId& id, idList) {
         	  qDebug() << "id=" << id.toString();
        }
#endif
    return idList;
}


QueryThread::QueryThread(): QThread()
{
}

void QueryThread::setArgs(QMessageServicePrivate* privateService, EventLoggerEngine *parent, const QMessageFilter &filter, const QString &body, QMessageDataComparator::MatchFlags matchFlags, const QMessageSortOrder &sortOrder, uint limit, uint offset)
{
  _privateService = privateService;
  _parent=parent;
  _filter=filter;
  _body=body;
  _matchFlags=matchFlags;
  _sortOrder=sortOrder;
  _limit=limit;
  _offset=offset;
}

void QueryThread::run()
{
  //  qDebug() << "QueryThread::run()";
  _parent->m_ids=EventLoggerEngine::instance()->filterAndOrderMessages(_filter,_sortOrder,_body,_matchFlags);
  //  qDebug() << "QueryThread::run() done" << _parent->m_ids.count();
  emit completed();
}



#include "moc_eventloggerengine_maemo_p.cpp"

QTM_END_NAMESPACE
