#include "NetWork/Socket.h"

#include <QObject>
#include <QTcpSocket>
#include <QMutexLocker>

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#  include <QJsonDocument>
#  include <QJsonArray>
#  include <QJsonObject>
#else
#  include <qjson/parser.h>
#endif

#include <QApplication>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>

#include "../Util/IniDataManger.h"
#include "PreDefined.h"
#include "../QsLog/QsLog.h"

int ZH_caclChkValue(char * pJsonData, int startPos, int endPos)
{
    int value = 0, loop = startPos, count = 0;
    for (; loop < endPos; loop++)
    {
        if( pJsonData[loop] == '"' || pJsonData[loop] == '\'')
            continue;

        if (2 > count){
            value += (int)pJsonData[loop];
        }
        else if  (2 == count){
            value *= (int)pJsonData[loop];

        }else{
            value -= (int)pJsonData[loop];
        }
        count = (count + 1) & 3;
    }
    return value;
}

Socket::Socket(QObject *parent)
    : QThread(parent),
      quit(false),
      m_retry_cnt(0),
      m_retry_max(0)
{
    qRegisterMetaType<Socket::RequstType>("Socket::RequstType");
    manger = NULL;
    m_retry_max = OBJREF(IniDataManger).GetMaxRetry();
    if (m_retry_max <= 0) {
        m_retry_max = 3;
    }
}

void Socket::setByTcp(QString _hostName, quint16 _port)
{
    hostName = _hostName;
    port = _port;
    agreementType = TCP;
}


void Socket::setByHttp(QString _httpUrl)
{
    httpUrl = _httpUrl;
    agreementType = HTTP;
}

QByteArray checkSendArray( QByteArray & jsonArray)
{
    char tmpBuf[4096*10] = {0};

    strcpy(tmpBuf, jsonArray.data());

    int strLength = strlen(tmpBuf);

    while('}' != tmpBuf[strLength - 1])
    {
        strLength--;
    }
    strLength--;

    sprintf(tmpBuf + strLength, ", \"checkValue\":");

    int aLength = strlen(tmpBuf + strLength);

    int nValue = ZH_caclChkValue( tmpBuf, 0, strLength + 2);
    strLength += aLength;

    sprintf(tmpBuf + strLength, " %d", nValue);

    aLength = strlen(tmpBuf + strLength);

    strLength += aLength;

    tmpBuf[strLength] = '}';
    tmpBuf[strLength + 1] = '\0';
    strLength ++;

    QByteArray array(tmpBuf);

    return array;
}

int tmp =0;
//void Socket::getNewJosn(const Socket::RequstType type,  const QJsonObject& json);
void Socket::getNewJosn(const RequstType type, const QByteArray &json)
{
    if (m_retry_cnt >= m_retry_max) {
        QLOG_INFO() << "request timeout: " << m_retry_cnt << "times.";
        emit timeout();
        return;
    }
    else {
        ++m_retry_cnt;
    }
    QMutexLocker locker(&mutex);  //锁

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QJsonDocument doc = QJsonDocument::fromJson(json);
    QByteArray array = doc.toJson(QJsonDocument::Compact);
#else
    QByteArray array = json;
#endif

    sendArray = checkSendArray(array);
    reType = type;

    if( !isRunning()){
        start();
    }else{
        cond.wakeOne();
    }
}

QByteArray Socket::getEncodeArray(const QByteArray &array)
{
    QByteArray codeArry;
    char ctrlStr[16] = {0};
    //ctrlStr[0] = 1;
    codeArry.append( ctrlStr, 16);

    codeArry.append( array);

    return codeArry;
}

bool Socket::isFullJson(const QByteArray &array)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QJsonParseError json_error;
    QJsonDocument::fromJson( array, &json_error);
    return  (json_error.error == QJsonParseError::NoError);
#else
    QJson::Parser p;
    QVariant json_var = p.parse(array);

    return !json_var.isNull() && json_var.isValid();
#endif

}

//QJsonObject Socket::getReturnJsonObject(const QByteArray &array)
//{
//    QJsonDocument doc = QJsonDocument::fromJson( array);

//    return doc.object();
//}

bool Socket::sendByHttp(QByteArray &returnJosn)
{
    if( NULL == manger ) {
        manger = new QNetworkAccessManager;
        QLOG_INFO() << "new instance of network manager started.";
    }

//    manger->clearAccessCache();
    manger->setNetworkAccessible(QNetworkAccessManager::Accessible);

    QNetworkRequest qRequset;
    qRequset.setUrl(httpUrl);
    qRequset.setRawHeader("Content-Type","text/json");

    QEventLoop eventLoop;
    connect(manger, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), &eventLoop, SLOT(quit()));
    connect(this, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
    QNetworkReply *reply = manger->post(qRequset , sendArray);
    connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &eventLoop, SLOT(quit()));
    eventLoop.exec();
    QLOG_INFO() << "request finish " << m_retry_cnt;
    //! Fixes infinite loop while retring
    m_retry_cnt = 0;

    if(reply->error() != QNetworkReply::NoError) {
        emit error(reply->error(), reply->errorString());
        manger->deleteLater();
        manger = NULL;
        return false;
    }

    QByteArray receArray = reply->readAll();

    //! If no data, assumes that error occurs.
    if(receArray.isEmpty()) {
        emit error(reply->error(), reply->errorString());
        manger->deleteLater();
        manger = NULL;
        return false;
    }

    reply->deleteLater();

//    returnJosn = getReturnJsonObject( receArray);
    returnJosn = receArray;

    return true;
}

bool Socket::sendByTcp(QByteArray &returnJosn)
{

    const int Timeout = 7 * 1000;

    QTcpSocket socket;
    //连接
    socket.connectToHost(hostName, port);
    if (!socket.waitForConnected(Timeout)) {
        emit error(socket.error(), socket.errorString());
        return false;
    }
    //发送

    QByteArray encodeArray = getEncodeArray( sendArray);

    /*int wLen = */socket.write( encodeArray);
    if ( !socket.waitForBytesWritten( Timeout)){
        emit error( socket.error(), socket.errorString());
        return false;
    }
    //接受
    QByteArray receArray;

    do{
        if (!socket.waitForReadyRead(Timeout)) {
            emit error(socket.error(), socket.errorString());
            return false;
        }
        receArray.append( socket.readAll());

    }while( !isFullJson( receArray));
    socket.close();

//    returnJosn = getReturnJsonObject( receArray);
    returnJosn = receArray;

    return true;
}

void Socket::run()
{
    while( !quit){
//        QJsonObject returnJson;
        QByteArray returnJson;
        if( agreementType == TCP){
            if( !sendByTcp(returnJson))
                return;
        }

        if( agreementType == HTTP){

            if( !sendByHttp(returnJson))
                return;
        }

        mutex.lock();
        emit recvReturn(reType, returnJson);   //将数据返回给界面
        m_retry_cnt = 0;
        cond.wait(&mutex);
        mutex.unlock();
    }
}
