/*
 *   This file is part of Dianara
 *   Copyright 2012-2019  JanKusanagi JRR <jancoding@gmx.com>
 *
 *   This program 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 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program 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 this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .
 */

#ifndef PUMPCONTROLLER_H
#define PUMPCONTROLLER_H

#include <QObject>
#include <QStringList>
#include <QByteArray>
#include <QUrl>
#include <QUrlQuery>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QSslError>
#include <QNetworkProxy>
#include <QTimer>
#include <QFile>

#include <QSettings>
#include <QDesktopServices>

#include <QDebug>


/// For JSON parsing
#include <QVariant>
#include <QVariantMap>
#include <QJsonDocument>

// For OAuth authentication
#include <QtOAuth>

#include "mischelpers.h"
#include "asperson.h"
#include "asobject.h"


class PumpController : public QObject
{
    Q_OBJECT

public:
    enum requestTypes
    {
        NoRequest,

        ClientRegistrationRequest,
        TokenRequest,

        UserProfileRequest,
        UpdateProfileRequest,
        UpdateEmailRequest,

        FollowingListRequest,
        FollowersListRequest,
        ListsListRequest,
        SiteUserListRequest,
        CreatePersonListRequest,
        DeletePersonListRequest,
        PersonListRequest,
        AddMemberToListRequest,
        RemoveMemberFromListRequest,

        CreateGroupRequest,
        DeleteGroupRequest,
        JoinGroupRequest,
        LeaveGroupRequest,

        MainTimelineRequest,
        DirectTimelineRequest,
        ActivityTimelineRequest,
        FavoritesTimelineRequest,
        UserTimelineRequest,

        PostLikesRequest,
        PostCommentsRequest,
        PostSharesRequest,

        MinorFeedMainRequest,
        MinorFeedDirectRequest,
        MinorFeedActivityRequest,

        PublishPostRequest,
        LikePostRequest,
        CommentPostRequest,
        SharePostRequest,
        UnsharePostRequest,
        DeletePostRequest,

        UpdatePostRequest,
        UpdateCommentRequest,

        CheckContactRequest,
        FollowContactRequest,
        UnfollowContactRequest,


        AvatarRequest,
        ImageRequest,
        MediaRequest,

        UploadFileRequest,
        UploadMediaForPostRequest,
        UploadAvatarRequest,
        PublishAvatarRequest
    };


    explicit PumpController(QObject *parent = 0);
    ~PumpController();

    void setProxyConfig(QNetworkProxy::ProxyType proxyType,
                        QString hostname, int port,
                        bool useAuth,
                        QString user, QString password);
    bool needsProxyPassword();
    void setProxyPassword(QString password);

    void updateApiUrls();

    void setPostsPerPageMain(int ppp);
    void setPostsPerPageOther(int ppp);


    void setNewUserId(QString userId);
    void setUserCredentials(QString userId);
    QString currentUserId();
    QString currentUsername();
    QString currentServerScheme();
    QString currentServerDomain();
    QString currentFollowersUrl();
    int currentFollowingCount();
    int currentFollowersCount();
    bool currentlyAuthorized();

    void getUserProfile(QString userId);
    void updateUserProfile(QString avatarUrl, QString fullName,
                           QString hometown, QString bio);
    void updateUserEmail(QString newEmail, QString password);

    void enqueueAvatarForDownload(QString url);
    void enqueueImageForDownload(QString url);
    void getAvatar(QString avatarUrl);
    void getImage(QString imageUrl);
    QNetworkReply *getMedia(QString mediaUrl);
    void notifyAvatarStored(QString avatarUrl, QString avatarFilename);
    void notifyImageStored(QString imageUrl);
    void notifyImageFailed(QString imageUrl);

    void getContactList(QString listType, int offset=0);
    void getSiteUserList();
    bool userInFollowing(QString contactId);
    void updateInternalFollowingIdList(QStringList idList);
    void removeFromInternalFollowingList(QString id);

    void getListsList();
    void createPersonList(QString name, QString description);
    void deletePersonList(QString listId);
    void getPersonList(QString url);
    void addPersonToList(QString listId, QString personId);
    void removePersonFromList(QString listId, QString personId);

    void createGroup(QString name, QString summary, QString description);
    void joinGroup(QString id);
    void leaveGroup(QString id);

    void getPostLikes(QString postLikesUrl);
    void getPostComments(QString postCommentsUrl, QString postId);
    void getPostShares(QString postSharesUrl);

    void getFeed(requestTypes feedType, int itemCount,
                 QString url = "", int feedOffset = 0);
    static QStringList getFeedNameAndPath(int feedType);
    QString getFeedApiUrl(int feedType);

    QNetworkRequest prepareRequest(QString url,
                                   QOAuth::HttpMethod method,
                                   requestTypes requestType,
                                   QOAuth::ParamMap paramMap = QOAuth::ParamMap(),
                                   QString contentTypeString="application/json");

    QByteArray prepareJSON(QVariantMap jsonVariantMap);
    QVariantMap parseJSON(QByteArray rawData, bool *parsedOk);


    QNetworkReply *uploadFile(QString filename, QString contentType,
                              requestTypes uploadType = UploadFileRequest);


    bool urlIsInOurHost(QString url);
    void addCommentUrlToSeenList(QString id, QString url);
    QString commentsUrlForPost(QString id);

    void showTransientMessage(QString message);

    void showStatusMessageAndLogIt(QString message, QString url="");
    void showObjectSnippetAndLogIt(QString message,
                                   QVariantMap jsonMap,
                                   QString messageWhenTitled="");

    void setIgnoreSslErrors(bool state);
    void setIgnoreSslInImages(bool state);
    void setNoHttpsMode();

    void setSilentFollows(bool state);
    void setSilentLists(bool state);
    void setSilentLikes(bool state);

    void updatePostsEverSeen(QVariantMap postMap);
    QVariantMap getPostsEverSeen();


signals:
    void openingAuthorizeUrl(QUrl url, bool browserLaunched);
    void authorizationSucceeded();
    void authorizationFailed(QString errorTitle, QString errorMessage);
    void authorizationStatusChanged(bool authorized);


    void initializationStepChanged(int step);
    void initializationCompleted();

    void profileReceived(QString avatarURL, QString fullName,
                         QString hometown, QString bio,
                         QString email);
    void contactListReceived(QString listType, QVariantList contactList,
                             int totalReceivedCount);
    void siteUserListReceived(QVariantList contactList, int totalItems);
    void contactVerified(QString userId, int httpCode,
                         bool requestTimedOut, QString serverVersion);
    void contactFollowed(ASPerson *contact);
    void contactUnfollowed(ASPerson *contact);
    void cannotFollowNow(QString userId);
    void followingListChanged();

    void listsListReceived(QVariantList listsList);
    void personListReceived(QVariantList personList, QString listUrl);
    void personAddedToList(QString id, QString name, QString avatarUrl);
    void personRemovedFromList(QString id);


    void mainTimelineReceived(QVariantList postList, QString previousLink,
                              QString nextLink, int totalItems);
    void directTimelineReceived(QVariantList postList, QString previousLink,
                                QString nextLink, int totalItems);
    void activityTimelineReceived(QVariantList postList, QString previousLink,
                                  QString nextLink, int totalItems);
    void favoritesTimelineReceived(QVariantList postList, QString previousLink,
                                   QString nextLink, int totalItems);
    void userTimelineReceived(QVariantList postList, QString previousLink,
                              QString nextLink, int totalItems,
                              QString url);
    void userTimelineFailed();
    void timelineFailed(int requestType);

    void likesReceived(QVariantList likesList, QString originatingPostURL);
    void commentsReceived(QVariantList commentsList, QString originatingPostURL);
    void commentsNotReceived(QString requestedUrl);

    void minorFeedMainReceived(QVariantList activitiesList,
                               QString previousLink, QString nextLink,
                               int totalItemCount);
    void minorFeedDirectReceived(QVariantList activitiesList,
                                 QString previousLink, QString nextLink,
                                 int totalItemCount);
    void minorFeedActivityReceived(QVariantList activitiesList,
                                   QString previousLink, QString nextLink,
                                   int totalItemCount);
    void minorFeedFailed(int requestType);

    void avatarPictureReceived(QByteArray pictureData, QString pictureUrl);
    void imageReceived(QByteArray pictureData, QString pictureUrl);
    void imageFailed(QString imageUrl);
    void downloadCompleted(QString fileUrl);
    void downloadFailed(QString fileUrl);
    void avatarStored(QString avatarUrl, QString avatarFilename);
    void imageStored(QString imageUrl);

    void postPublished();
    void postPublishingFailed();
    void likeSet();
    void commentPosted(QString parentPostId);
    void commentPostingFailed(QString parentPostId);

    void userDidSomething();

    void avatarUploaded(QString url);

    void showErrorNotification(QString message);
    void currentJobChanged(QString message);
    void transientStatusBarMessage(QString message);
    void logMessage(QString message, QString url="");


public slots:
    void requestFinished(QNetworkReply *reply);

    void sslErrorsHandler(QNetworkReply *reply, QList<QSslError> errorList);

    void getToken();
    void authorizeApplication(QString verifierCode);

    void getInitialData();


    void postNote(QVariantMap audienceMap,
                  QString postText,
                  QString postTitle);
    QNetworkReply *postMedia(QVariantMap audienceMap,
                             QString postText, QString postTitle,
                             QString mediaFilename, QString mediaType,
                             QString mimeContentType);
    void postMediaStepTwo(QString id);

    void postAvatarStepTwo(QString id);


    void updatePost(QString id, QString type, QString content, QString title);

    void likePost(QString postId, QString postType, QString authorId, bool like);

    void addComment(QString comment, QString postId, QString postType);
    void updateComment(QString id, QString content, QString inReplyToId);

    void sharePost(QString postId, QString postType);
    void unsharePost(QString postId, QString postType);
    void deletePost(QString postId, QString postType);

    void followContact(QString address);
    void followVerifiedContact(QString address);
    void unfollowContact(QString address);
    void onValidationTimeout();


private:
    QNetworkAccessManager m_nam;
    QByteArray m_userAgentString;

    // QOAuth-related
    QOAuth::Interface *m_qoauth;

    bool m_applicationAuthorized;

    QString m_clientId;
    QString m_clientSecret;
    QByteArray m_token;
    QByteArray m_tokenSecret;


    QString m_userId;  // Full Webfinger address, user@host.tld
    QString m_userName;
    QString m_serverDomain;
    QString m_apiBaseUrl;
    QString m_apiFeedUrl;
    QString m_serverScheme;

    bool m_proxyUsesAuth;

    QString m_userFollowersUrl;
    int m_userFollowingCount;
    int m_userFollowersCount;
    QStringList m_followingIdList;
    int m_totalReceivedFollowing;
    int m_totalReceivedFollowers;


    QVariantMap m_postsEverSeen;


    QTimer *m_initialDataTimer;
    int m_initialDataStep;
    int m_initialDataAttempts;
    bool m_haveProfile;
    bool m_haveFollowing;
    bool m_haveFollowers;
    bool m_havePersonLists;
    bool m_haveMainTL;
    bool m_haveDirectTL;
    bool m_haveActivityTL;
    bool m_haveFavoritesTL;
    bool m_haveMainMF;
    bool m_haveDirectMF;
    bool m_haveActivityMF;


    // For multi-step operations in posts
    QString m_currentPostTitle;
    QString m_currentPostDescription;
    QVariantMap m_currentPostAudience;
    QString m_currentPostType;


    // For multi-step verify+follow operations
    QNetworkReply *m_webfingerCheckReply;
    QTimer *m_webfingerCheckTimer;
    bool m_webfingerCheckTimedOut;
    QString m_addressPendingToFollow;


    // Avatars / Images queue
    QStringList m_pendingAvatarsList;
    QStringList m_pendingImagesList;


    // Settings stuff
    int m_postsPerPageMain;  // FIXME: get rid of these, query globalObject
    int m_postsPerPageOther;

    bool m_ignoreSslErrors;
    bool m_ignoreSslInImages;

    bool m_silentFollows;
    bool m_silentListsHandling;
    bool m_silentLikes;
};

#endif // PUMPCONTROLLER_H
