/****************************************************************************
**
** 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/>.
****************************************************************************/

#ifndef NEWSBLURSERVICE_H
#define NEWSBLURSERVICE_H
#include <abstractfeedaggreagatorservice.h>
#include <QString>
#include <QList>
#include <QPair>
#include <QByteArray>
#include <QNetworkAccessManager>
#include <QVariantMap>
#include <QAbstractItemModel>
#include <QTimer>
#include <QSignalMapper>
#include "pageitem.h"
#include <QNetworkRequest>
#include <QtPlugin>
#include <QSettings>
#include <QAuthenticator>
#include <QDeclarativePropertyMap>
#include <QDeclarativeItem>
#ifdef QTM_USE_NAMESPACE
#   include <QSystemAlignedTimer>
#endif
#include "newsbluritem.h"
#include "newsblurclient.h"
#include "rootitem.h"
#include "folderitem.h"
#include "feeditem.h"
#include "everythinggroup.h"
#include "starredgroup.h"
#include "storyitem.h"
#include "feedclassifiersmodel.h"

namespace NewsBlur
{
#ifdef QTM_USE_NAMESPACE
QTM_USE_NAMESPACE
#endif
class NewsBlurService : public AbstractFeedAggreagatorService
{
    Q_OBJECT    
    Q_INTERFACES(AbstractFeedAggreagatorService)
    Q_PROPERTY(bool busy READ isBusy WRITE setBusy NOTIFY busyChanged)
    Q_PROPERTY(bool markingEverythingReadBusy READ markingEverythingReadBusy NOTIFY markingEverythingReadBusyChanged)
    Q_PROPERTY(int filterType READ filterType NOTIFY filterTypeChanged)

public:
    explicit NewsBlurService(QObject *parent = 0);
    ~NewsBlurService();
    void enable(PageItemPtr parentItem);
    Q_INVOKABLE PageItem* rootItem() const;
    void disable();
    QString serviceId() const;
    QSet<QString> customItemTypeProperties() const;

    QStringList menuItems(PageItemPtr item) const;
    QStringList contextMenuItems(PageItemPtr item) const;
    bool isItemAcceptable(PageItemPtr item);
    QString userName() const;
    void setUserName(const QString& userName);
    QString password() const;
    void setPassword(const QString& password);
    bool isBusy() const;
    void setBusy(bool busy);
    bool markingEverythingReadBusy() const;
    void setMarkingEverythingReadBusy(bool busy);

public slots:
    Q_INVOKABLE QString servicePageUrl() const;
    Q_INVOKABLE QString authenticationSheet() const;
    Q_INVOKABLE QString serviceName() const;
    Q_INVOKABLE QString serviceImageUrl() const;
    Q_INVOKABLE void onMoreChildrenRequested(PageItemPtr parentItem);
    Q_INVOKABLE void markFeedAsRead(NewsBlur::FeedItem *feed);
    Q_INVOKABLE void markFolderAsRead(NewsBlur::FolderItem *folder);
    Q_INVOKABLE void markEverythingAsRead();
    Q_INVOKABLE void markStoryAsUnread(NewsBlur::StoryItem *story);
    Q_INVOKABLE void verifyAuthenticationData(const QString& userName, const QString& password);
    Q_INVOKABLE void setAuthenticationData(const QString& userName, const QString& password);
    Q_INVOKABLE void abortVerification();
    Q_INVOKABLE bool isAuthenticationDataSet() const;
    Q_INVOKABLE void clearAuthenticationData();
    Q_INVOKABLE void saveStory(NewsBlur::StoryItem *story);
    Q_INVOKABLE void unSaveStory(NewsBlur::StoryItem *story);
    Q_INVOKABLE void renameItem(NewsBlur::NewsBlurItem *item, const QString& name);
    Q_INVOKABLE void abortItemRename(NewsBlur::NewsBlurItem* item);
    Q_INVOKABLE void deleteItem(NewsBlur::NewsBlurItem *item);
    Q_INVOKABLE void moveItem(NewsBlur::NewsBlurItem *item, NewsBlur::FolderItem *destItem);
    Q_INVOKABLE void addFolder(NewsBlur::FolderItem *parentItem, const QString& name);
    Q_INVOKABLE void abortAddFolder(NewsBlur::FolderItem* parentItem);
    Q_INVOKABLE void addFeed(NewsBlur::FolderItem *parentItem, const QString& url);
    Q_INVOKABLE void abortAddFeed(NewsBlur::FolderItem* parentItem);
    Q_INVOKABLE void findFeeds(const QString& searchTerm);
    Q_INVOKABLE void loadFeedClassifiers(NewsBlur::NewsBlurItem* item);
    Q_INVOKABLE void abortLoadingFeedClassifiers(NewsBlur::NewsBlurItem* item);
    Q_INVOKABLE void saveFeedClassifications(NewsBlur::FeedClassifiersModel* model, const QString& feedId,
                                             const QString& storyId = QString());
    Q_INVOKABLE void abortSavingFeedClassifications();
    Q_INVOKABLE void setFilterType(int type, NewsBlur::FeedItem* currentFeed);
    Q_INVOKABLE int filterType() const;
    Q_INVOKABLE QString feedTitle(int feedId) const;
    Q_INVOKABLE void refreshEverythingGroup();

private:    
    bool m_unreadCountsOutOfDate;
    bool m_serviceBusy;
    bool m_markingEverythingReadBusy;
    NewsBlurClient* m_client;
    RootItemPtr m_rootItem;
    QMultiHash<int, FeedItemPtr> m_feedItems;
    QList<FolderItemPtr> m_folderItems;
    StarredGroupPtr m_starredGroupItem;
    EverythingGroupPtr m_everythingGroupItem;
    QHash<QString, QImage> m_feedFavicons;
#ifdef QTM_USE_NAMESPACE
    QSystemAlignedTimer m_unreadCountUpdateTimer;
#else
    QTimer m_unreadCountUpdateTimer;
#endif

    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
    QList<FeedItemPtr> unreadFeeds(FolderItemPtr parent) const;
    QList<FolderItemPtr> childFolders(FolderItemPtr parent) const;
    void updateFolderUnreadCounts(FolderItemPtr folderItem = FolderItemPtr(), int *unreadCountSum = 0,
                                   int *posUnreadCoundSum = 0, int *negUnreadCountSum = 0);
    QList<StoryItemPtr> matchingStoryItems(int feedId, const QString &storyId, bool skipStarredStories = false) const;
    void markStoryItemsAsRead(int feedId, const QString& storyId);
    FeedClassifiers createStorySpecificFeedClassifiers(StoryItemPtr story, const FeedClassifiers& feedClassifiers) const;
    void markFeedsAsRead(const QList<FeedItemPtr>& feeds);
    void trimCachedItems();
    //operation functions
    NewsBlurItemPtr relatedNewsBlurItem(WebServiceReplyWeakRef reply);

private slots:
    void onItemActivated(PageItemPtr item);
    void onItemDeactivated(PageItemPtr item);
    void onActivityStatusChanged(PageItemPtr item, bool active);
    void onFeedOutOfDate(int feedId);
    void onRiverOutOfDate();
    void onUnreadCountUpdateTimerTimeout();


    void onAuthenticationRequired(QAuthenticator* auth);
    void updateUnreadCounts(bool resetTimer = true);
    void updateTreeStructure();    
    void loadMoreFeedStories(FeedItemPtr feedItem);
    void loadNewFeedStories(FeedItemPtr feedItem);
    void loadMoreRiverStories();
    void loadNewRiverStories();
    void loadMoreStarredStories();
    void loadNewStarredStories();

    bool isRequestAllowed(NewsBlurClient::RequestType type, NewsBlurItemPtr relatedItem) const;
    void onUpdateTreeCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onLoadFeedStoriesCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onLoadNewFeedStoriesFinished(WebServiceReplyPtr reply);
    void onLoadRiverStoriesCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onLoadNewRiverStoriesFinished(WebServiceReplyPtr reply);
    void onLoadStarredStoriesFinished(WebServiceReplyPtr reply);
    void onLoadNewStarredStoriesFinished(WebServiceReplyPtr reply);
    void onMarkStoryAsReadFinished(WebServiceReplyPtr reply);
    void onMarkStoryAsUnreadFinished(WebServiceReplyPtr reply);
    void onSaveStoryFinished(WebServiceReplyPtr reply);
    void onUnsaveStoryFinished(WebServiceReplyPtr reply);
    void onMarkFeedReadFinished(WebServiceReplyPtr reply);
    void onMarkFolderReadFinished(WebServiceReplyPtr reply);
    void onMarkEverythingReadCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onUpdateUnreadCountsFinished(WebServiceReplyPtr reply);
    void onItemRenameCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onAddFeedCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onAddFolderCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onFindFeedsCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onLoadFeedClassifiersCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onSaveFeedClassificationsCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onAccountVerificationCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onRequestCompleted(WebServiceReplyPtr reply, WebServiceReply::CompletedState state);
    void onRequestFinished(WebServiceReplyPtr reply);
    void onRequestFailed(WebServiceReplyPtr reply);
    void onUpdateRequestFailed(WebServiceReplyPtr reply);
    void onCommitRequestFailed(WebServiceReplyPtr reply);
    void onRequestAborted(WebServiceReplyPtr reply);

signals:
    void accountVerificationComplete(int result);
    void itemRenamingFinished(int result);
    void folderAddingFinished(int result);
    void feedAddingFinished(int result);
    void feedsFound(int result, QStringList labels, QStringList urls);
    void feedClassifiersLoaded(int result, NewsBlur::FeedClassifiersModel* model);
    void feedClassificationsSaved(int result);
    void filterTypeChanged();    
    void busyChanged();
    void markingEverythingReadBusyChanged();

private:
    static const char* s_serviceName;
    static const char* s_serviceId;
    static const char* s_serviceImageUrl;
    static const char* s_starGroupImageUrl;
    static const char* s_generalFeedImageUrl;
    static const char* s_folderImageUrl;
    static const char* s_folderIconId;
    static const char* s_feedIconId;
    static const char* s_starredGroupIconId;
    static const char* s_serviceIconId;

    static const char* s_userNameKeyName;
    static const char* s_passwordKeyName;
    static const char* s_filterTypeKeyName;

    static const int s_unreadCountUpdateIntervalMinMs;
    static const int s_unreadCountUpdateIntervalMaxMs;
    static const int s_savedStoriesPerPage;
    static const int s_riverStoriesPerPage;
    static const int s_feedStoriesPerPage;
    static const int s_preferredStoryCountPerRequest;
    static const int s_preferredFilteredStoryCountPerRequest;
    static const int s_preferredMinimalStoryCount;
    static const int s_itemIconWidth;
    static const int s_itemIconHeight;
    static const char* s_busyPropertyName;
    static const char* s_childrenBusyPropertyName;
    static const char* s_starredBusyPropertyName;
    static const char* s_feedReadBusyPropertyName;
    static const char* s_serviceBusyPropertyName;
    static const char* s_deletingBusyPropertyName;
    static const char* s_movingBusyPropertyName;

    static const char* s_authenticationPageUrl;
    static const char* s_servicePageUrl;
    static const char* s_markFeedAsReadToolButtonUrl;
    static const char* s_markFolderAsReadToolButtonUrl;
    static const char* s_markEverythingAsReadToolButtonUrl;
    static const char* s_starUnstarStoryToolButtonUrl;
    static const char* s_shareStoryToolButtonUrl;
    static const char* s_markStoryAsUnreadMenuItemName;
    static const char* s_intelligenceTrainerMenuItemName;
    static const char* s_renameMenuItemName;
    static const char* s_moveMenuItemName;
    static const char* s_deleteMenuItemName;
    static const char* s_markFeedAsReadMenuItemName;
    static const char* s_shareStoryMenuItemName;
    static const char* s_starStoryMenuItemName;
    static const char* s_unstarStoryMenuItemName;
    static const char* s_newsBlurHelpMenuItemName;
};
} // namespace NewsBlur
Q_DECLARE_METATYPE(QStringList)
#endif // NEWSBLURSERVICE_H
