#include "control.h"
#include "global.h"
#include "fmtool.h"
#include "fmerror.h"
#include "QsLog.h"
#include "jsonfactory.h"
#include "rspfactory.h"
#include <QSemaphore>
#include <QtConcurrent>
#include <QSettings>
#include <QJsonObject>
#include <QEventLoop>
#include <QFont>
#include <QFontDatabase>
#include <QApplication>
#include <QSslSocket>
#include <QSslConfiguration>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QJsonDocument>
#include <Windows.h>
#include "DataProcess/tools.h"
#include "DataProcess/cretopt.h"
#include "DataProcess/fmnetwork.h"
#include <QUuid>

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

typedef int (__stdcall *SKBAPISend)(const char *indata, const char *guid, char *outdata, char *errMsg, const char *mode, const char *operation);

Control::Control(QObject *parent) : QObject(parent), _widget(NULL)
{
    QString path;
    ToolS::GetPath(path);

    _isinterrupt = false;
    _db = new FMPDataBase(path + DB_ORDER, QString("writeorder"));

    QString sql = QString("create table ") + DB_TABLE_NAME + " ("
                          "orderid varchar(60) primary key, "
                          "content text"
                          ")";

    QLOG_INFO() << "creat table : " << sql;

    _db->Init();
    _db->creat(sql);

    InitModel();
}

Control::~Control()
{
    if(_db != NULL)
    {
       delete _db;
       _db = NULL;
    }
}

void Control::Start(const char *indata, char *outdata)
{
    bool refundflag = false;
    QLOG_INFO() << "get data from pos:" << QString::fromLocal8Bit(indata);

    QString appPath;
    char pathStr[MAX_PATH] = { 0 };
    ToolS::GetProcPath(pathStr);
    appPath = QString::fromLocal8Bit(pathStr);
    appPath = appPath.replace("\\", "/");
    QFontDatabase::addApplicationFont(appPath + "msyh.ttf");
    QFont ft("Microsoft YaHei UI Light");
    qApp->setFont(ft);

    QLOG_INFO() << "Openssl support:" << QSslSocket::supportsSsl();
    qDebug() << "Openssl support:" << QSslSocket::supportsSsl() << QApplication::libraryPaths();

    QEventLoop loop;

    if(_widget == NULL)
        _widget = new HostWidget();

    InitPOSReqJsonObj(indata);

    connect(_widget, &HostWidget::Interrupt, this, &Control::OnInterrupt);

    connect(_widget, &HostWidget::RequestWithType, this, &Control::RequestWithType);

    connect(_widget, &HostWidget::Exits, this, [&loop, &refundflag] ()
    {
        QLOG_INFO() << "quit with normal";
        refundflag = true;
        loop.exit();
    });
    connect(_widget, &HostWidget::ExitWithMSG, this, [this, &loop] (QString code, QString message)
    {
        QLOG_INFO() << "quit with : " << message;
        SetResPonseWithMessage(code, message);
        loop.exit();
    });

    ReqType reqType = (ReqType)FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_REQTYPE).toInt();

//    _widget->ShowWithRequest(_request);

    _widget->ShowWithRequest(_posType, _posReqJsonObj);

    if(reqType != refund)
    {
        loop.exec();
    }else
    {
        if(refundflag == false)
            loop.exec();
    }

    _lock.lock();
    delete _widget;
    _widget = NULL;
    _lock.unlock();

//    memcpy(outdata, (char *)(&_response), sizeof(struct AlipayResponse));
//    outdata[sizeof(struct AlipayResponse)] = 0;
    QByteArray responseArray = QJsonDocument(_responseJsonObj).toJson(QJsonDocument::Compact);
    char *rspData = responseArray.data();
    char tmpbuf[MAX_BUF_LEN] = { 0 };
    int len = FMTool::UTF8ToGBK((unsigned char *)rspData, (unsigned char *)tmpbuf, MAX_PRINT_LEN);

    memcpy((char *)outdata, tmpbuf, len);

    QLOG_INFO() << "return data to pos : " << QString::fromLocal8Bit(outdata);
}

void Control::InitPOSReqJsonObj(const char *indata)
{
    QJsonParseError parseError;
    QJsonDocument inDataDoc = QJsonDocument::fromJson(QString::fromLocal8Bit(indata).toUtf8(), &parseError);
    if(parseError.error == QJsonParseError::NoError) {
        _posReqJsonObj = inDataDoc.object();

        int type = FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_REQTYPE).toInt(SPCC_sign);

        if(type>=SPCC_sign && type<=SPCC_finds) {
            _posReqJsonObj[JSON_KEY_REQTYPE] = type - SPCC_sign;
            _posType = SPCC;
        } else if(type>=SIMPHONY_sign && type<=SIMPHONY_finds) {
            _posReqJsonObj[JSON_KEY_REQTYPE] = type - SIMPHONY_sign;
            _posType = SIMPHONY;
        } else {
            // TODO: Other type.
        }
    } else {
        qDebug() << "Json parse error: " << parseError.errorString();
        memcpy(&_request, indata, sizeof(struct AlipayRequest));
        ReqType type = (ReqType)(FMTool::GetString(_request.TransType, 2).toInt());
        JsonFactory::GetJsonWithType(type, _posReqJsonObj, _request);
        qDebug() << _posReqJsonObj;
        _posType = RES;
    }
}

void Control::SetResPonseWithMessage(QString code, const QString &message)
{
//    FMTool::SetString(_response.ResponseCode, 3, code);
//    FMTool::SetString(_response.ResponseMsg, 40, message);
    _responseJsonObj[JSON_KEY_STATUSCODE] = code.toInt();
    _responseJsonObj[JSON_KEY_MESSAGE] = message;
}

void Control::InitModel()
{
//    memset(&_request, ' ', sizeof(struct AlipayRequest));
//    memset(&_response, ' ', sizeof(struct AlipayResponse));
    _posReqJsonObj = QJsonObject();
    _responseJsonObj = QJsonObject();
}

bool Control::SendMessageToServer(const QJsonObject &json, QByteArray &outdata, QString &error)
{
    QJsonObject tmpjson = json;
    tmpjson[JSON_KEY_VER] = DEFAULT_JSON_VER_VALUE;
    CretOperate::GetMAC(tmpjson);
    CretOperate::GetSign(tmpjson);

    QLOG_INFO() << "send json to payment: " << tmpjson;
    qDebug() << "send json to payment: " << tmpjson;

    bool isOk = false;
    if(_posType==SPCC) {
        int timeout = _setting.GetValue(CONFIG_OLTP_TIMEOUT, 60).toInt();
        isOk = SendMessageToSBKAPI(tmpjson, outdata, error, timeout);
    } else {
        isOk = SendMessageToPayMent(tmpjson, outdata, error);
    }

    if(!_isinterrupt && !isOk) {
    	//如果没有点击取消支付按钮直接写数据库异步冲正
        if(tmpjson.contains(JSON_KEY_REQTYPE) && tmpjson[JSON_KEY_REQTYPE].toInt() == 72)
        {
            QByteArray data = QJsonDocument(tmpjson).toJson(QJsonDocument::Compact);
            QVariantHash hash;

            hash.insert(SQL_KEY_ORDERID, tmpjson[JSON_KEY_PARTORDERID].toString());
            hash.insert(SQL_KET_CONTENT, QString(data));


            _db->insert(DB_TABLE_NAME, hash);
        }
        return false;
    }

    return isOk;
}

bool Control::SendMessageToPayMent(const QJsonObject &json, QByteArray &outdata, QString &error)
{
    QByteArray array;
    QString path;
    ToolS::GetPath(path);
    QString url = QSettings(path + "\\" + USERCONFIG_NAME, QSettings::IniFormat).value(VALUE_URL).toString();

    QLOG_INFO() << "send json to payment: " << json;

    QByteArray data = QJsonDocument(json).toJson(QJsonDocument::Compact);

#ifdef MOCK_SIMPHONY
    QEventLoop loop;
    QFuture<void> future = QtConcurrent::run( [ this, &loop, json, &array]()
    {
        QLOG_DEBUG() << "=========================================== Read debug data. ========================================";
        int type = FMTool::GetJsonValue(json, JSON_KEY_REQTYPE).toInt(sign);
        QLOG_DEBUG() << "Test req type: " << type;
        QFile f(qApp->applicationDirPath()+QString("/test_simphony/%1.txt").arg(type));
        f.open(QIODevice::ReadOnly);
        array = f.readAll();
        Sleep(1000*10);
        QLOG_DEBUG() << "=========================================== End read debug data. ====================================";
    });
    QFutureWatcher<void> watcher;
    watcher.setFuture(future);
    connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));

    loop.exec();
#else
    if(!Control::HttpPost(url, array, data, "application/json;charset=utf-8", "application/json", error, 60))
    {
//        if(json.contains(JSON_KEY_PARTORDERID) && !json[JSON_KEY_PARTORDERID].toString().isEmpty())
//        {
//            QVariantHash hash;

//            hash.insert(SQL_KEY_ORDERID, json[JSON_KEY_PARTORDERID].toString());
//            hash.insert(SQL_KET_CONTENT, QString(data));


//            _db->insert(DB_TABLE_NAME, hash);
//        }

        return false;
    }
#endif

    outdata = array;

    return true;
}

bool Control::HttpPost(QString url, QByteArray &outdata, const QByteArray &indata, QString content, QString accept, QString  &error, int timeout)
{
    QString host = _setting.GetValue(VALUE_HOST, "").toString();
    QString action = _setting.GetValue(CONFIG_OLTP_ACTION, "FromPOS").toString();

    QEventLoop loop;
    QTimer timer;

    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);

    QNetworkAccessManager manger;
    QNetworkRequest request(url);

    request.setSslConfiguration(config);

    request.setRawHeader("Content-Type", content.toUtf8());
    request.setRawHeader("Accept", accept.toUtf8());
    request.setRawHeader("Host", host.toUtf8());
    request.setRawHeader("Authorization", "Basic dXBzLWNsaWVudDo2VGk4TjBXNzRyb1A=");
    request.setRawHeader("SOAPAction", action.toUtf8());


    QNetworkReply* reply = manger.post(request, indata);

    connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
    connect(&manger, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    connect(_widget, &HostWidget::Interrupt, &loop, &QEventLoop::quit);
    connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), &loop, &QEventLoop::quit);
    timer.start(timeout*1000);

    loop.exec();

    reply->deleteLater();


    if(_isinterrupt)
    {
        error = QString::fromLocal8Bit("收款被取消");
        return false;
    }


    if(reply->error() != QNetworkReply::NoError)
    {
        error = QString::fromLocal8Bit("网络异常,获取服务端返回数据失败");
        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    outdata = reply->readAll();

    if(outdata.isEmpty())
    {
        error = QString::fromLocal8Bit("网络异常,获取服务端返回数据为空");
        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    QLOG_INFO() << outdata.data();

    return true;
}


bool Control::RollHttpPost(QString url, QByteArray &outdata, const QByteArray &indata, QString content, QString accept, QString &error, int timeout)
{
    QString host = _setting.GetValue(VALUE_HOST, "").toString();
    QString action = _setting.GetValue(CONFIG_OLTP_ACTION, "FromPOS").toString();

    QEventLoop loop;
    QTimer timer;

    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);

    QNetworkAccessManager manger;
    QNetworkRequest request(url);

    request.setSslConfiguration(config);

    request.setRawHeader("Content-Type", content.toUtf8());
    request.setRawHeader("Accept", accept.toUtf8());
    request.setRawHeader("Host", host.toUtf8());
    request.setRawHeader("Authorization", "Basic dXBzLWNsaWVudDo2VGk4TjBXNzRyb1A=");
    request.setRawHeader("SOAPAction", action.toUtf8());

    QNetworkReply* reply = manger.post(request, indata);

    connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
    connect(&manger, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    connect(_widget, &HostWidget::Interrupt, &loop, &QEventLoop::quit);
    connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), &loop, &QEventLoop::quit);
    timer.start(timeout*1000);

    loop.exec();

    reply->deleteLater();

    if(reply->error() != QNetworkReply::NoError)
    {
        error = QString::fromLocal8Bit("网络异常,获取服务端返回数据失败");
        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    outdata = reply->readAll();

    if(outdata.isEmpty())
    {
        error = QString::fromLocal8Bit("网络异常,获取服务端返回数据为空");
        QLOG_INFO() << reply->errorString() << "Contents: " << reply->readAll();
        return false;
    }

    QLOG_INFO() << outdata.data();

    return true;
}

bool Control::SendMessageToSBKAPI(const QJsonObject &json, QByteArray &outdata, QString &error, int timeout, bool isReversal)
{
    QByteArray reqXmlData = FMNetWork::CreateOLTPXML(json);

#ifdef MOCK
    QLOG_DEBUG() << "=========================================== Read debug data. ========================================";
    int type = FMTool::GetJsonValue(json, JSON_KEY_REQTYPE).toInt(sign);
    QLOG_DEBUG() << "Test req type: " << type;
    QFile f(qApp->applicationDirPath()+QString("/test_spcc/%1.txt").arg(type));
    f.open(QIODevice::ReadOnly);
    strcpy(out, f.readAll().data());
    Sleep(1000*3);
    QLOG_DEBUG() << "=========================================== End read debug data. ====================================";

    int result = 0;
#else

    QString url = _setting.GetValue(CONFIG_OLTP_URL, "http://172.170.4.207:8080/OLTP/WSPortal").toString();
    QString uuid = QUuid::createUuid().toString();
    bool httpIsOk = false;
    if(isReversal) {
        httpIsOk = RollHttpPost(url, outdata, reqXmlData, "application/soap+xml; charset=utf-8", uuid, error, timeout);
    } else {
        httpIsOk = HttpPost(url, outdata, reqXmlData, "application/soap+xml; charset=utf-8", uuid, error, timeout);
    }
#endif //! End def MOCK

    QString outXMlString = QString::fromUtf8(outdata);

    if(!httpIsOk) {
        error = "OLTP return error: \n" +error;
        QLOG_ERROR() << error;
        return false;
    } else {
        QString statcode;
        bool isOk = FMTool::SearchXMLNodeString(outXMlString, "ns0:STATCODE", statcode);
        if(!isOk) {
            error = "Can't find xml node: ns0:STATCODE";
            QLOG_ERROR() << error;
            return false;
        }
        if(statcode.compare("0000")!=0) {
            error = "OLTP return STATCODE is: " + statcode;
            QLOG_ERROR() << error;
            return false;
        }

        QString returnStr;
            isOk = FMTool::SearchXMLNodeString(outXMlString, "ns0:AP", returnStr);
            if(!isOk) {
                error = "Can't find xml node: ns0:AP";
                QLOG_ERROR() << error;
                return false;
            }
            outdata = returnStr.toUtf8();
            return true;
        }

    return false;
}

bool Control::GetRSA(QString &error)
{
    QJsonObject json;

    json.insert(JSON_KEY_VER, 1);
    json.insert(JSON_KEY_REQTYPE, sign);
    json.insert(JSON_KEY_STOREID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STOREID).toString());
    json.insert(JSON_KEY_STATIONID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_STATIONID).toString());
    json.insert(JSON_KEY_PARTNERID, FMTool::GetJsonValue(_posReqJsonObj, JSON_KEY_PARTNERID).toInt());

    CretOperate::GetDES3MAC(json);
    QByteArray array;

    QLOG_INFO() << "get mac sign request : " << json;

    QString path;
    ToolS::GetPath(path);
    QString url = QSettings(path + "\\" + USERCONFIG_NAME, QSettings::IniFormat).value(VALUE_URL_CRET).toString();
    QByteArray data = QJsonDocument(json).toJson(QJsonDocument::Compact);

    if(_posType == SPCC)
    {
        if(!SendMessageToSBKAPI(json, array, error, 60))
        {
            QLOG_ERROR() << "SendMessageToSBKAPI error: " << error;
            return false;
        }
    } else {
        if(!Control::HttpPost(url, array, data,"application/json;charset=utf-8", "application/json", error, 60))
        {
            QLOG_ERROR() << "httppos error :" << error;
            return false;
        }
    }

    if(CretOperate::SetRSACret(array) == 0)
    {
        error = QString::fromLocal8Bit("签名失败，请重新获取签名");
        return false;
    }

    return true;
}

void Control::RequestSign()
{
    QString error;
    bool rlt = GetRSA(error);

    if(rlt)
        SetResPonseWithMessage("100", QString::fromLocal8Bit("签名成功"));
    else
        SetResPonseWithMessage("22", QString::fromLocal8Bit("签名失败"));

    _lock.lock();
    if(_widget != NULL)
        _widget->ShowWiteMGS(sign, rlt, error);
    _lock.unlock();
}

bool Control::GetJson(ReqType type,QJsonObject &json, const QByteArray array, QString &error)
{
    QJsonParseError parseError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(array.data(), &parseError);

    if( jsonDocument.isNull() )
    {
        QLOG_ERROR() << "server return not json" << parseError.errorString();
        error = QString::fromLocal8Bit("服务端返回数据异常");
        return false;
    }

    json = jsonDocument.object();

    if(json.contains(JSON_KEY_MESSAGE) && json.contains(JSON_KEY_STATUSCODE) && (json[JSON_KEY_STATUSCODE].toInt() == 307 || json[JSON_KEY_STATUSCODE].toInt() == 303))
    {
        error = QString::fromLocal8Bit("签名过期，请重新获取签名");
        json.insert(JSON_KEY_MESSAGE, QString::fromLocal8Bit("签名过期，请重新获取签名"));
    }

    if(type == againprint)
        json.insert(JSON_KEY_FMID, _fmId);

    if(type == refund && json.contains(JSON_KEY_STATUSCODE) && json[JSON_KEY_STATUSCODE].toInt() == 100)
        json.insert(JSON_KET_CLIENTREF, FMTool::GetString(_request.Amount, 12));

//    RspFactory::GetResPonseJson(type, _response, json);
    RspFactory::GetResPonseJson(type, _responseJsonObj, json);

    if(json.contains(JSON_KEY_MESSAGE))
        error = json[JSON_KEY_MESSAGE].toString();

    if(json.contains(JSON_KEY_STATUSCODE) && json[JSON_KEY_STATUSCODE].toInt() == 100)
        return true;

    return false;
}

void Control::Request(ReqType type, QStringList list)
{
    QJsonObject json, rtjson;
    QString error;
    QByteArray outdata;

    if(type == againprint)
        _fmId = list[0];

    JsonFactory::GetJsonWithType(type, json, _posReqJsonObj, list);

    QJsonDocument(json).toJson(QJsonDocument::Compact);

    bool rlt = SendMessageToServer(json, outdata, error);

    if(rlt)
        rlt = Control::GetJson(type, rtjson, outdata, error);
    else
        SetResPonseWithMessage("23", error);

    //如果没有点击取消按钮走正常的退出或显示流程
    if(!_isinterrupt)
    {
        _lock.lock();
        if(_widget != NULL && type == finds)
        {
            _widget->ShowWiteJson(rlt, rtjson, error);
        }
        else
        {
            _widget->ShowWiteMGS(type, rlt, error);
        }
        _lock.unlock();
    }
    else //如果点了取消支付按钮我们开始阻塞冲正3次，3次不成功写数据库异步冲正
    {
        int i = 0;
        bool tmpflag = false;

        QByteArray tmparray;
        QString tmperror;

        QJsonObject tmpjson = json;
        tmpjson[JSON_KEY_VER] = DEFAULT_JSON_VER_VALUE;
        tmpjson[JSON_KEY_REQTYPE] = 3;
        CretOperate::GetSign(tmpjson);

        QLOG_INFO() << "Send reversal json: " << tmpjson;

        QByteArray tmpdata = QJsonDocument(tmpjson).toJson(QJsonDocument::Compact);

        while(i < 3)
        {
            if(_posType == SPCC) {
                if((tmpflag = SendMessageToSBKAPI(tmpjson, tmparray, tmperror, 19, true)) == true) {
                    break;
                }
            } else {
//                if((tmpflag = RollHttpPost(url, tmparray, tmpdata,"application/json;charset=utf-8", "application/json", tmperror, 15)) == true) {
//                    break;
//                }
            }
            QLOG_ERROR() << "rollback failed : " << tmperror;
            ++i;
        }
        error = QString::fromLocal8Bit("支付取消成功.如已扣款将会自动返还");
        if(tmpflag)
            SetResPonseWithMessage("23", error);
        else
        {

            QVariantHash hash;

            hash.insert(SQL_KEY_ORDERID, json[JSON_KEY_PARTORDERID].toString());
            hash.insert(SQL_KET_CONTENT, QString(tmpdata));

            QLOG_ERROR() << "roll back faile , move request to database : " << json;

            _db->insert(DB_TABLE_NAME, hash);
            SetResPonseWithMessage("23", error);
        }

        _lock.lock();
        if(_widget != NULL && type == finds)
        {
            _widget->ShowWiteJson(rlt, rtjson, error);
        }
        else
        {
            _widget->ShowWiteMGS(type, rlt, error);
        }
        _lock.unlock();

    }
}

void Control::RequestWithType(ReqType type, QStringList list)
{
    if(type == sign)
    {
        RequestSign();
        return ;
    }
    Request(type, list);
}

void Control::setIsinterrupt(bool isinterrupt)
{
    _isinterrupt = isinterrupt;
}

void Control::OnInterrupt()
{
    _isinterrupt = true;
//    if(_posType==SPCC && _widget != NULL) {
//        _widget->StopPay();
//    }
}
