/****************************************************************************
**
** Copyright (C) 2012 Róbert Márki
**
** This file is part of Web Feeds.
**
** Web Feeds is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Web Feeds 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Web Feeds.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

#include "abstractfeedaggreagatorservice.h"
#include "pageitem.h"

/*!
  \class AbstractFeedAggreagatorService
  \brief The AbstractFeedAggreagatorService class provides a common base class
  and plugin interface for the various feed aggregator service implementations.
*/
/*!
  \fn AbstractFeedAggreagatorService::enable
  Enables the plugin. The plugin adds it's root item as a child to the
  given \a parentItem.
*/
/*!
  \fn AbstractFeedAggreagatorService::disable
  Disables the plugin. The plugin removes it's root item from the root item's
  parent. The plugin also stops all network related or timed operations and
  clears it's cache.
*/
/*!
  \fn AbstractFeedAggreagatorService::menuItems
  Returns the list of main menu item names specific to this plugin, for the
  given \a item.
*/
/*!
  \fn AbstractFeedAggreagatorService::contextMenuItems
  Returns the list of context menu item names specific to this plugin, for the
  given \a item.
*/
/*!
  \fn AbstractFeedAggreagatorService::isItemAcceptable
  Returns true if the given \a item is acceptable by the plugin's current filter,
  otherwise returns false.
*/
/*!
  \fn AbstractFeedAggreagatorService::servicePageUrl
  Returns the url of the page item that should be displayed on authentication
  requests.
*/
/*!
  \fn AbstractFeedAggreagatorService::servicePageUrl
  Returns the url of the page item that should pushed to the page stack when
  the root item of the plugin is activated.
*/
/*!
  \fn AbstractFeedAggreagatorService::authenticationSheet
  Returns the url of the sheet item that should be displayed on authentication
  requests.
*/
/*!
  \fn AbstractFeedAggreagatorService::serviceName
  Returns the display name of the plugin.
*/
/*!
  \fn AbstractFeedAggreagatorService::serviceId
  Returns the identifier of the plugin. The returned id shouldn't contain any
  whitespaces.
*/
/*!
  \fn AbstractFeedAggreagatorService::serviceImageUrl
  Returns the url of the image associated with the plugin.
*/
/*!
  \fn AbstractFeedAggreagatorService::isAuthenticationDataSet
  Returns the true if valid authentication data has been set for the plugin,
  otherwise returns false.
*/
/*!
  \fn AbstractFeedAggreagatorService::clearAuthenticationData
  Clears the authentication data set for the plugin.
*/
/*!
  \fn AbstractFeedAggreagatorService::rootItem
  Returns the root item of the plugin's subtree.
*/
/*!
  \fn AbstractFeedAggreagatorService::authenticationDataChanged
  This signal is emitted when the authentication data set for the plugin
  changes.
*/
/*!
  \fn AbstractFeedAggreagatorService::filterChanged
  This signal is emitted when the plugin's item filter type has
  changed.
*/
/*!
  \fn AbstractFeedAggreagatorService::itemTypeProperties
  Returns the set of property names for the given item type.
*/

/*!
  Constructs an AbstractFeedAggreagatorService object with the given \a parent.
*/
AbstractFeedAggreagatorService::AbstractFeedAggreagatorService(QObject *parent) :
    QObject(parent),
    QDeclarativeImageProvider(QDeclarativeImageProvider::Image),
    m_settings(0),
    m_pluginSettings(0)
{
}

/*!
  Destroys the AbstractFeedAggreagatorService object.
*/
AbstractFeedAggreagatorService::~AbstractFeedAggreagatorService()
{
    if(m_pluginSettings)
        delete m_pluginSettings;
}

/*!
  Return the url of an image for the given \a imageId.
*/
QString AbstractFeedAggreagatorService::imageUrl(const QString &imageId) const
{
    return QString("image://%1/%2").arg(serviceId()).arg(imageId);
}

/*!
  Returns true if the given \a item is acceptable by the plugins current filter,
  otherwise returns false.
*/
bool AbstractFeedAggreagatorService::isItemAcceptable(PageItemPtr item)
{
    Q_UNUSED(item);
    return true;
}

/*!
  Returns the set of additional property names that the plugin want's to be
  exposed to the UI. These property names are the additional properties that
  the PageItem subclasses have specific to this plugin.
*/
QSet<QString> AbstractFeedAggreagatorService::customItemTypeProperties() const
{
    return QSet<QString>();
}

/*!
  Sets the plugin's ApplicationSettings class instance. Which can be used to
  read the application's user editable settings.
*/
void AbstractFeedAggreagatorService::setApplicationSettings(ApplicationSettings *settings)
{
    m_settings = settings;
}

/*!
  Returns a pointer to the settings object of the service.
*/
QSettings* AbstractFeedAggreagatorService::pluginSettings() const
{
    if(!m_pluginSettings)
    {
        m_pluginSettings = new QSettings;
        m_pluginSettings->beginGroup(serviceName());
    }
    return m_pluginSettings;
}

/*!
  Removes all values saved by the plugin in the settings object.
*/
void AbstractFeedAggreagatorService::clearPluginSettings()
{
    pluginSettings()->remove("");
}

/*!
  Returns the plugin's current ApplicationSettings object instance. If no settings object
  has been set for the plugin a null pointer is returned.
*/
ApplicationSettings *AbstractFeedAggreagatorService::applicationSettings() const
{
    return m_settings;
}

/*!
  Saves the given \a reply associated with the given \a relatedItem.
*/
void AbstractFeedAggreagatorService::addRequest(PageItemPtr relatedItem, WebServiceReplyWeakRef reply)
{
    m_runningRequests.insert(relatedItem, reply);
}

/*!
  Removes the given \a reply from the list of saved replies.
  Any busy properties associated with the \a reply are set to
  false.
*/
void AbstractFeedAggreagatorService::removeRequest(WebServiceReplyWeakRef reply)
{
    QList<QPair<QWeakPointer<QObject>, QString> > busyProperties = m_busyProperties.values(reply);
    for(int i = 0; i < busyProperties.count(); ++i)
    {
        QSharedPointer<QObject> item = busyProperties.at(i).first.toStrongRef();
        if(item)
            item->setProperty(qPrintable(busyProperties.at(i).second), false);
    }
    m_busyProperties.remove(reply);
    m_runningRequests.remove(relatedItem(reply), reply);
}

/*!
  Creates an association between the \a reply and the property with the name \a propertyName of the given \a object.
  The \a propertyName property of the \a object is set to true.
*/
void AbstractFeedAggreagatorService::assingBusyPropertyToRequest(WebServiceReplyWeakRef reply, QSharedPointer<QObject> object, const QString &propertyName)
{
    if(object && reply && !propertyName.isEmpty())
    {
        object->setProperty(qPrintable(propertyName), true);
        m_busyProperties.insert(reply, qMakePair<QWeakPointer<QObject>, QString>(object, propertyName));
    }
}

/*!
  Returns true if a request is running with a type of \a requestType related to the given
  \a relatedItem. If the \a relatedItem is invalid, it will be ignored.
*/
bool AbstractFeedAggreagatorService::isRequestRunning(int requestType, PageItemPtr relatedItem) const
{
    bool ret = false;
    QList<WebServiceReplyWeakRef> replies;
    if(relatedItem)
        replies = m_runningRequests.values(relatedItem);
    else
        replies = m_runningRequests.values();
    foreach(const WebServiceReplyWeakRef& replyRef, replies)
    {
        WebServiceReplyPtr replyPtr = replyRef.toStrongRef();
        if(replyPtr && (replyPtr->requestType() == requestType))
        {
            ret = true;
            break;
        }
    }
    return ret;
}

/*!
  Returns the item related to the given \a reply.
*/
PageItemPtr AbstractFeedAggreagatorService::relatedItem(WebServiceReplyWeakRef reply)
{
    PageItemPtr relatedItem;
    int index = m_runningRequests.values().indexOf(reply);
    if(index >= 0)
        relatedItem = m_runningRequests.keys().at(index);
    return relatedItem;
}

/*!
  Returns the reply with the type of \a requestType related to the given \a item.
*/
WebServiceReplyWeakRef AbstractFeedAggreagatorService::relatedReply(PageItemPtr item, int requestType)
{
    WebServiceReplyWeakRef ret;
    QList<WebServiceReplyWeakRef> replies;
    replies = m_runningRequests.values(item);
    foreach(const WebServiceReplyWeakRef& replyRef, replies)
    {
        WebServiceReplyPtr replyPtr = replyRef.toStrongRef();
        if(replyPtr && (replyPtr->requestType() == requestType))
        {
            ret = replyRef;
            break;
        }
    }
    return ret;
}

/*!
  Aborts the request with the given properties.
  The request is aborted if it's type mathces the \a type parameter or no
  \a type was specified, if it's role matches the \a role parameter or no
  \a role was specified, if it's related item matches the \a relatedItem
  parameter or not \a relatedItem was specified.
  If none of the parameters are specified, all requests are abored.
*/
void AbstractFeedAggreagatorService::abortRequest(int type,
                                                  WebServiceReply::Role role,
                                                  PageItemPtr relatedItem)
{
    for(int i = 0; i < m_runningRequests.count(); ++i)
    {
        WebServiceReplyPtr reply = m_runningRequests.values().at(i);
        if(reply)
        {
            PageItemPtr replyRelatedItem = m_runningRequests.keys().at(i);
            if(((replyRelatedItem == relatedItem) || (relatedItem == 0))
                && ((type == 0) || (reply->requestType() == type))
                && ((role == WebServiceReply::RlUndefined) || (reply->role() == role)))
            {                
                reply->abort();
            }
        }
    }
}

/*!
  Aborts all running requests.
*/
void AbstractFeedAggreagatorService::abortAllRequests()
{
    abortRequest();    
    m_runningRequests.clear();
}

/*!
  Register the given \a item with the service, so the service gets
  notified when the item's activity status changes or more child
  items are requested for it. If \a recursive is true, all the
  child items of the \a item will be registered too.
*/
void AbstractFeedAggreagatorService::registerItem(PageItemPtr item, bool recursive)
{
    item->setService(this);
    connect(item.data(), SIGNAL(activityStatusChanged(PageItemPtr,bool)),
            this, SLOT(onActivityStatusChanged(PageItemPtr,bool)),
            Qt::UniqueConnection);
    connect(item.data(), SIGNAL(moreChildrenRequested(PageItemPtr)),
            this, SLOT(onMoreChildrenRequested(PageItemPtr)),
            Qt::UniqueConnection);
    if(recursive)
    {
        foreach(PageItemPtr child, item->childItems())
            registerItem(child, recursive);
    }
}

void AbstractFeedAggreagatorService::onItemActivated(PageItemPtr item)
{
    Q_UNUSED(item);
}

void AbstractFeedAggreagatorService::onItemDeactivated(PageItemPtr item)
{
    Q_UNUSED(item);
}

void AbstractFeedAggreagatorService::onMoreChildrenRequested(PageItemPtr parentItem)
{
    Q_UNUSED(parentItem);
}

void AbstractFeedAggreagatorService::onActivityStatusChanged(PageItemPtr item, bool active)
{
    if(active)
        onItemActivated(item);
    else
        onItemDeactivated(item);
}


