#include "hpptclient.h"

//HpptClient* HpptClient::gInstance = nullptr;
HpptClient::HpptClient(QObject *p): QObject(p) {
    connect(&mNetAccessManager, &QNetworkAccessManager::finished, this, [this](QNetworkReply *reply) {
        if(reply->property("data").isValid()) onHttpPostRspFinished(reply);
        else onHttpGetRspFinished(reply);
    });
}
void HpptClient::clearRp(QNetworkReply *rp) {
    if(rp)
    {
        QString url = rp->request().url().toString();
        QString postMD5 = rp->property("postMD5").toString();
        QString postData = rp->property("data").toByteArray();

        if(postMD5.isEmpty())
        {
            //清理对应缓存
            mDownloadDataCache.remove(url);

            //解除正在处理状态
            mProcessingRq.remove(url);
        }
        else
        {
            //清理对应缓存
            mDownloadDataCache.remove(postMD5);


            //解除正在处理状态
            mProcessingRq.remove(postMD5);
        }

        mRedirectMap.remove(url);
        mRedirectMap.remove(postMD5);

        //qDebug() << "delete cache, url:" << url << " postMOD5:" << postMD5;
        rp->deleteLater();
    }
}

void HpptClient::onHttpGetRspProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    Q_UNUSED(bytesReceived)
    if(sender() == NULL)
    {
        return ;
    }

    QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
    if(rp == NULL)
    {
        return;
    }

    //qDebug() << "http get rsp progress:" << rp->url().toString() << bytesReceived << "/" << bytesTotal;
    if(bytesTotal <= 0)
    {
        return;
    }

    QString url = rp->url().toString();

    mDownloadDataCache[url].append(rp->readAll());

}

void HpptClient::onHttpGetRspFinished(QNetworkReply *reply) {
    QByteArray rpData;
    QString url = reply->url().toString();
    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    QString strUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();

    switch(statusCode) {
    case 200: {
        rpData = mDownloadDataCache[reply->url().toString()];
        QString redirectUrl = mRedirectMap[url];//重定向的url地址
        if(redirectUrl.isEmpty()) emit httpGetRspReady(url, rpData);
        else emit httpGetRspReady(redirectUrl, rpData);
    }
        break;
    case 301:
    case 302: {
        if(!strUrl.isEmpty()) {
            QString turl = mRedirectMap[url];
            if(turl.isEmpty()) mRedirectMap[strUrl] = url;
            else mRedirectMap[strUrl] = turl;
            httpGet(strUrl);
        }
    }
        break;
    default: // error
    {
        qDebug() << url << "[get error:" << statusCode << "]";
        QString redirectUrl = mRedirectMap[url];
        if(redirectUrl.isEmpty()) emit httpGetRspReady(url, QByteArray());
        else emit httpGetRspReady(redirectUrl, QByteArray());
    }
        break;
    }
    clearRp(reply);
}

void HpptClient::onHttpPostRspProgress(qint64, qint64 bytesTotal){
    if(sender() == NULL) return;
    QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
    if(rp == NULL) return;
    if(bytesTotal <= 0) return;
    mDownloadDataCache[rp->property("postMD5").toString()].append(rp->readAll());
}

void HpptClient::onHttpPostRspFinished(QNetworkReply *reply) {
    QString url = reply->url().toString();
    QString postMD5 = reply->property("postMD5").toString();

    int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    switch(statusCode) {
    case 200: {
            auto rpData = mDownloadDataCache[postMD5];
            QString redirectMD5 = mRedirectMap[postMD5];
            QString redirectUrl = mRedirectMap[url];
            if(redirectMD5.isEmpty() || redirectUrl.isEmpty()) emit httpPostRspReady(url, postMD5, rpData);
            else emit httpPostRspReady(redirectUrl, redirectMD5, rpData);
        }
        break;
    case 301:
    case 302: {
    auto redireUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
        if(!redireUrl.isEmpty()) {
            QString turl = mRedirectMap[url];//direct by
            if(turl.isEmpty()) mRedirectMap[redireUrl] = url;
            else mRedirectMap[redireUrl] = turl;

            QByteArray postData = reply->property("data").toByteArray();
            QByteArray d = redireUrl.toUtf8() + postData;
            QString md5 = QCryptographicHash::hash(d, QCryptographicHash::Md5);
            QString tPostMD5 = mRedirectMap[md5];//direct by
            if(tPostMD5.isEmpty()) mRedirectMap[md5] = postMD5;
            else mRedirectMap[md5] = tPostMD5;
            httpPost(redireUrl, postData);
        }
    }
        break;
    default:
        QString redirectMD5 = mRedirectMap[postMD5];
        QString redirectUrl = mRedirectMap[url];
        if(redirectMD5.isEmpty() || redirectUrl.isEmpty())
        {
            emit httpPostRspReady(url, postMD5, QByteArray());
        }
        else
        {
            emit httpPostRspReady(redirectUrl, redirectMD5, QByteArray());
        }
        break;
    }
    clearRp(reply);
}


void HpptClient::httpGet(const QString &url) {
    if(mProcessingRq.value(url, false)) return;//ignore when rq processing
    auto values = mRedirectMap.values();
    for(int i = 0; i < values.count(); i++) if(values[i] == url) return;//ignore when redirect processing
    mProcessingRq.insert(url, true);

    QNetworkRequest request{url};
    QSslConfiguration config = request.sslConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1SslV3);
    request.setSslConfiguration(config);

    QNetworkReply* rp = mNetAccessManager.get(request);
    connect(rp, &QNetworkReply::downloadProgress, this, &HpptClient::onHttpGetRspProgress);
}

QByteArray HpptClient::httpPost(const QString &url, const QByteArray &data) {
    QByteArray md5 = QCryptographicHash::hash(url.toUtf8() + data, QCryptographicHash::Md5);
    if(mProcessingRq.value(md5, false)) return md5;//ignore when rq processing
    auto values = mRedirectMap.values();
    for(int i=0; i < values.count(); i++) if(values[i] == md5) return md5; //ignore when redirect processing
    mProcessingRq.insert(md5, true);

    QNetworkRequest request{url};
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");//add by alahover 20200304
    QNetworkReply* reply = mNetAccessManager.post(request, data);
    reply->setProperty("postMD5", md5);
    reply->setProperty("url", url);
    reply->setProperty("data", data);
    connect(reply, &QNetworkReply::downloadProgress, this, &HpptClient::onHttpPostRspProgress);
    return md5;
}