/**************************************************************************
** Piperka Client
** Copyright (C) 2019  Kari Pahula
**
** 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.
**************************************************************************/

#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QtNetwork/QNetworkCookie>
#include <QtNetwork/QNetworkCookieJar>
#include <QtNetwork/QNetworkReply>
#include <QSysInfo>
#include <QUrl>
#include <QUrlQuery>

#include "browse.h"
#include "download.h"

void addBookmarksToQuery(QUrlQuery &query, const QMap<int, int> &bm)
{
    for (QMap<int, int>::const_iterator iter = bm.cbegin(); iter != bm.cend(); ++iter) {
        if (iter.value() == -1)
            query.addQueryItem("bm", QString::number(iter.key()));
        else
            query.addQueryItem("bm", QString::number(iter.key()).append("+")
                               .append(QString::number(iter.value())));
    }
}

void addBookmarksToPostData(QByteArray &postData, const QMap<int, int> &bm)
{
    for (QMap<int, int>::const_iterator iter = bm.cbegin(); iter != bm.cend(); ++iter) {
        postData.append("&bm=");
        if (iter.value() == -1)
            postData.append(QString::number(iter.key()));
        else
            postData.append(QString::number(iter.key()))
                    .append('+')
                    .append(QString::number(iter.value()));
    }

}

Download::Download(QObject *parent)
    : QObject(parent)
    , userAgent(CLIENT_NAME "/" APP_VERSION)
{
#ifdef PIPERKA_DEVEL
    connect(&mgr, &QNetworkAccessManager::sslErrors,
            this, &Download::ignoreSSL);
#endif
    userAgent
            .append(" (")
            .append(QSysInfo::prettyProductName())
            .append(")");
}

QNetworkReply *Download::createAccount(const QString &user, const QString &email, const QString &password,
                                       const QMap<int, int> &bookmarks)
{
    QNetworkRequest request = QNetworkRequest(uri_createAccount);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    QByteArray postData;
    postData.append("_new_login=");
    postData.append(QUrl::toPercentEncoding(user));
    postData.append("&email=");
    postData.append(QUrl::toPercentEncoding(email));
    postData.append("&_new_password=");
    postData.append(QUrl::toPercentEncoding(password));
    postData.append("&_new_password_again=");
    postData.append(QUrl::toPercentEncoding(password));
    addBookmarksToPostData(postData, bookmarks);
    QNetworkReply *reply = mgr.post(request, postData);
    return reply;
}

QNetworkReply *Download::login(const QString &user, const QString &password, const QMap<int, int> *bookmarks)
{
    QNetworkRequest request = QNetworkRequest(uri_login);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    QByteArray postData;
    postData.append("user=");
    postData.append(QUrl::toPercentEncoding(user));
    postData.append("&passwd=");
    postData.append(QUrl::toPercentEncoding(password));
    if (bookmarks)
        addBookmarksToPostData(postData, *bookmarks);
    QNetworkReply *reply = mgr.post(request, postData);
    return reply;
}

QNetworkReply *Download::logout(const QString &token)
{
    QUrlQuery query;
    query.addQueryItem("action", "logout");
    query.addQueryItem("csrf_ham", token);
    QUrl uri = QUrl(uri_logout);
    uri.setQuery(query);
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    return mgr.get(request);
}

QNetworkReply *Download::subscribe(const QString &token, const int &cid, const int &ord)
{
    QUrlQuery query;
    query.addQueryItem("getstatrow", "1");
    query.addQueryItem("bookmark[]", QString::number(cid));
    query.addQueryItem("bookmark[]", ord == INT_MAX ? "max" : QString::number(ord));
    query.addQueryItem("csrf_hash", token);
    QUrl uri = QUrl(uri_userPrefs);
    uri.setQuery(query);
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    return mgr.get(request);
}

QNetworkReply *Download::unsubscribe(const QString &token, const int &cid)
{
    QUrlQuery query;
    query.addQueryItem("bookmark[]", QString::number(cid));
    query.addQueryItem("bookmark[]", "del");
    query.addQueryItem("csrf_hash", token);
    QUrl uri = QUrl(uri_userPrefs);
    uri.setQuery(query);
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    return mgr.get(request);
}

QNetworkReply *Download::bookmarkPositions(const QMap<int, int> &bm)
{
    QUrlQuery query;
    addBookmarksToQuery(query, bm);
    QUrl uri = QUrl(uri_bookmarkPositions);
    uri.setQuery(query);
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    return mgr.get(request);
}

void Download::setSession(const QByteArray &session)
{
    QNetworkCookieJar *jar = mgr.cookieJar();
    QNetworkCookie cookie = QNetworkCookie("p_session", session);
    cookie.setDomain(PIPERKA_HOST);
    cookie.setPath("/");
    jar->insertCookie(cookie);
}

void Download::downloadPages(int cid)
{
    QUrl uri = QUrl(uri_archive);
    uri.setPath(uri_archive.path().append(QString::number(cid)));
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    QNetworkReply *reply = mgr.get(request);
    connect(reply, &QNetworkReply::finished, this,
            [=]() {
        reply->deleteLater();
        if (reply->error() != QNetworkReply::NoError) {
            emit downloadPagesError(reply);
            return;
        }
        QByteArray data = reply->readAll();
        QJsonDocument doc = QJsonDocument::fromJson(data);
        emit downloadPagesComplete(doc);
    });
}

void Download::downloadRecommend()
{
    QNetworkRequest request = QNetworkRequest(uri_recommend);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    QNetworkReply *reply = mgr.get(request);
    connect(reply, &QNetworkReply::finished, this,
            [=]() {
        reply->deleteLater();
        if (reply->error() != QNetworkReply::NoError) {
            emit downloadRecommendError(reply);
            return;
        }
        QByteArray data = reply->readAll();
        QJsonDocument doc = QJsonDocument::fromJson(data);
        emit downloadRecommendComplete(doc);
    });
}

void Download::downloadComicsList()
{
    QNetworkRequest request = QNetworkRequest(uri_comicsOrdered);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    if (!comicsListStamp.isEmpty())
        request.setRawHeader("if-modified-since", comicsListStamp);
    QNetworkReply *reply = mgr.get(request);
    connect(reply, &QNetworkReply::finished, this,
            [=]() {
        reply->deleteLater();
        if (reply->error() != QNetworkReply::NoError) {
            emit downloadComicsListError(reply);
            return;
        }
        int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        if (status == 304) {
            emit downloadComicsListUnchanged();
            return;
        }
        for (QList<QByteArray>::const_iterator iter = reply->rawHeaderList().cbegin();
             iter != reply->rawHeaderList().cend(); ++iter) {
            if (QString::fromLatin1(*iter).toLower() == "last-modified") {
                comicsListStamp = reply->rawHeader(*iter);
                break;
            }
        }

        QByteArray data = reply->readAll();
        QJsonDocument doc = QJsonDocument::fromJson(data);
        emit downloadComicsListComplete(doc);
    });
}

void Download::downloadSort(int sortType)
{
    QUrlQuery query;
    query.addQueryItem("sort", QString::number(sortType));
    QUrl uri = QUrl(uri_sort);
    uri.setQuery(query);
    QNetworkRequest request = QNetworkRequest(uri);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    QNetworkReply *reply = mgr.get(request);
    connect(reply, &QNetworkReply::finished, this,
            [=](){
        reply->deleteLater();
        if (reply->error() != QNetworkReply::NoError) {
            emit downloadSortError(reply);
            return;
        }
        QByteArray data = reply->readAll();
        QJsonDocument doc = QJsonDocument::fromJson(data);
        emit downloadSortComplete(sortType, doc);
    });
}

void Download::forgetUser()
{
    mgr.setCookieJar(new QNetworkCookieJar());
}

QNetworkReply *Download::userPrefs()
{
    QNetworkRequest request = QNetworkRequest(uri_userPrefs);
    request.setHeader(QNetworkRequest::UserAgentHeader, userAgent);
    return mgr.get(request);
}

#ifdef PIPERKA_DEVEL
void Download::ignoreSSL(QNetworkReply *reply, const QList<QSslError> &errors)
{
    Q_UNUSED(errors);
    reply->ignoreSslErrors();
}
#endif
