#include "SocketCommunicate.h"
#include <QEventLoop>
#include <QStringList>
#include <QSslConfiguration>
#include <QTimer>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include "QsLog.h"
#include "preDefine.h"
#include "Network/billSocket.h"
#include "configManger.h"
#include "flowControl.h"

SocketCommunicate::SocketCommunicate(ThreadSocket *parent) : ThreadSocket(parent)
{
    _tcpSvrIp = "";
    _tcpSvrPort = 0;

    _storeId = "";
    _macAddress = "";
     _hostName = "";
     _workStationNum = "0";
    _localIp  = "";
    _localPort = 24409;
    _httpLoginResult = false;
    _openTcpChannel = false;
    _pcsPluginVersion = APP_VERSION;
    _socketHeartBeatDateTime = QDateTime::currentDateTime();

    _sslSocket = NULL;
    _needTcpLogin = 1;
    _certPrivateKey = "";
    _certPublishKey = "";

    _domainName.clear();
    _isTimeout = true;
    _reconnectTimes = 0;
    _networkErrIndex = 0;
}


SocketCommunicate::~SocketCommunicate()
{
    while(!_stoped) {
        QLOG_INFO() << QString("SocketCommunicate:: ~SocketCommunicate()......");
        _stopFlag = true;

        if ( _sslSocket ) {
            _sslSocket->disconnectFromHost(); // free Host And Port Resource;
            // https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/network/QAbstractSocket.html#disconnectFromHost()
            // Will Emit signal: disconnected() , Then Trigger  Slot: deleteLater()
            // delete _sslSocket;  _sslSocket = nullptr;
        }

        emit quit();
        EVENTWAIT(20);
        this->threadClose();
        QLOG_INFO() << QString("SocketCommunicate:: ~SocketCommunicate()  Reset Flag  _stoped = TRUE");
        _stoped = true;
    }
}


void SocketCommunicate::setPosMetaData(const QVariantMap &posMetaData)
{
    _posMetaData = posMetaData;
}


bool SocketCommunicate::connectTcpSvr()
{
    if( 0 == _tcpSvrIp.length() ||  0 == _tcpSvrPort ) {
        QLOG_ERROR() << "connectTcpSvr:: _tcpSvrIp / _tcpSvrPort  Is Invalid";
        return false;
    }

    QLOG_INFO() << QString("connectTcpSvr:: SvrIp & SvrPort: %1:%2 ").arg( _tcpSvrIp ).arg( _tcpSvrPort );
    _sslSocket = new QSslSocket();
    while ( !_sslSocket ) {
        _sslSocket = new QSslSocket();
    };

    // 绑定 sslSocket Connect成功后 触发 Lambda 槽函数:  <1> 重置超时标志: _isTimeout 为 False; <2> 发出连接成功信号中断阻塞模式;
    connect(_sslSocket, &QSslSocket::connected, [this] () { _isTimeout = false;   emit connected(); } );

    // 解除 sslSocket 长连接后 触发 Lambda 槽函数:  <1> 释放 _sslSocket 内存; 【服务端断开: 此时Socket会触发disconnect信号, 后续触发 deleteLater(), 慎重空指针 】
    connect(_sslSocket, SIGNAL( disconnected() ), _sslSocket, SLOT( deleteLater() )  );

    // 绑定 sslSocket 异常时 触发 SocketCommunicate::quit  信号函数
    connect(_sslSocket, static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error), this, &SocketCommunicate::quit);

    // 绑定 sslSocket 读数据成功后 触发 Lambda 槽函数:  <1> 重置超时标志: _isTimeout 为 False; <2> 发出读完成信号中断阻塞模式;
    connect(_sslSocket, &QSslSocket::readyRead, [this] () { _isTimeout = false;  emit readReady(); } );

    // 绑定 sslSocket 写数据成功后 触发 Lambda 槽函数:  <1> 重置超时标志: _isTimeout 为 False; <2> 发出写完成信号中断阻塞模式;
    connect(_sslSocket, &QSslSocket::bytesWritten, [this] (quint64 writebyte) { Q_UNUSED(writebyte);  _isTimeout = false; emit writeReady(); } );

    //下载证书接口(需要门店号 & 设备号)待调试, 评估是否需要保存证书文件:
    QLOG_INFO() << "connectTcpSvr:: Get Certificate From Url: ......................";

    std::string tempMacAddress = "";
    std::string tempIpAddress = "";
    int getMacRet = MacTool::GetMacByAdaptersAddresses( tempMacAddress , tempIpAddress );

    if ( !getMacRet) {
        QLOG_ERROR() << "connectTcpSvr:: MacTool::GetMacByAdaptersAddresses Falied";
        // 是否需要弹框提示 伙伴.
    }
    _macAddress = tempMacAddress.c_str();
    _localIp = tempIpAddress.c_str();

    // 组装请求头+请求体, 获取证书相关数据;
    QString reqCertificateRealUrl = _getCertificateUrl  + "storeId=" + _storeId  + "&deviceId=" + _macAddress;
    QByteArray outCertificateData;
    QJsonObject outCertificateJson;
    QString requestError;
    int requestErrorNum = 0;
    // 如果获取证书失败，需要循环获取，且频次需要越来越低;
    if ( 0 == _certPublishKey.length()  ||  0 == _certPrivateKey.length() ||  _reconnectTimes >= 5 ) {
        do {
            if ( S_GetRequest( reqCertificateRealUrl, outCertificateData, requestError) ) {
                outCertificateJson = QJsonDocument::fromJson(outCertificateData).object();
                if ( outCertificateJson.contains(JSON_DATA) && true == outCertificateJson[JSON_TCP_STA_RST].toBool()
                     && QString("200") == outCertificateJson[JSON_TCP_STA_CODE].toString()  ) {

                    QString encryptPublicKey = outCertificateJson[JSON_DATA].toObject()[JSON_KEY_PUBLIC_KEY].toString();
                    QString encryptPrivateKey = outCertificateJson[JSON_DATA].toObject()[JSON_KEY_PRIVATE_KEY].toString();

                    QLOG_INFO()<< QString("connectTcpSvr:: Get Certificate Url: %1 ,   encryptPublicKey: %2 , encryptPrivateKey: %3 ;")
                                  .arg( reqCertificateRealUrl ).arg( encryptPublicKey ).arg( encryptPrivateKey ) ;

                    if ( !SocketCommuProcess::getRealPubKeyPriKey( encryptPublicKey, encryptPrivateKey, _certPublishKey, _certPrivateKey ) ) {
                        requestErrorNum++;
                        QLOG_INFO()<< QString("connectTcpSvr:: getRealPubKeyPriKey  Failed,  encryptPublicKey: %1 , encryptPrivateKey: %2 ").arg( encryptPublicKey ).arg( encryptPrivateKey );
                        if (requestErrorNum  < 5 ) {
                            Sleep(2*60*1000);
                        } else {
                            Sleep(5*60*1000);
                        }

                    } else {
                        requestErrorNum = 0;
                        break;
                    }

                } else {
                    requestErrorNum++;
                    QLOG_ERROR() <<QString("connectTcpSvr:: Get Certificate Url: %1  ,  ErrorMsg: %2  ,  RspErrorMsg: %3  ,  requestErrorNum: %4 , outRspData: %5")
                                   .arg( reqCertificateRealUrl ).arg( requestError ).arg( outCertificateJson[JSON_MSG].toString() ).arg( requestErrorNum).arg( QString(outCertificateData)  );
                    if (requestErrorNum  < 5 ) {
                        Sleep(2*60*1000);
                    } else {
                        Sleep(5*60*1000);
                    }
                }

            } else {
                requestErrorNum++;
                QLOG_ERROR() <<QString("connectTcpSvr:: Get Certificate Url: %1 ,ErrorMsg: %2, outRspData: %3 ").arg( reqCertificateRealUrl ).arg( requestError ).arg( QString(outCertificateData) );
                if (requestErrorNum  < 5 ) {
                    Sleep(2*60*1000);
                } else {
                    Sleep(5*60*1000);
                }
            }

        } while(1);

    }

    _sslSocket->ignoreSslErrors();
    QSslConfiguration config = QSslConfiguration::defaultConfiguration();
    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    _sslSocket->setSslConfiguration(config);
    _isTimeout = true;
    // 无证书, 故无法使用 connectToHostEncrypted , 否则 提示: refuse connect;
    _sslSocket->connectToHost(_tcpSvrIp, _tcpSvrPort);

    {
        QEventLoop loop;
        QTimer timer;
        connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &SocketCommunicate::connected, &loop, &QEventLoop::quit);
        timer.start(20*1000);
        loop.exec();
    }

    // Connect Tcp Svr Success, Then Lambda Fuc  Will Change  Flag: _isTimeout = false;
    if ( _isTimeout ) {
        QLOG_ERROR() <<"connectTcpSvr:: ErrorMsg: "<< _sslSocket->errorString() << " ErrorCode: "<<_sslSocket->sslErrors() << " Timeout: "<< _isTimeout;
    } else {
        QLOG_INFO() <<QString("connectTcpSvr:: Success, _tcpSvrIp: %1  , _tcpSvrPort: %2 ").arg( _tcpSvrIp ).arg( _tcpSvrPort );
        //连接 Server IP + Port 成功后，设置MetaData;
        QVariantMap tmpMap;
        tmpMap.insert(JSON_TCP_DEVICE_ID , _macAddress);
        tmpMap.insert(JSON_TCP_DEVICE_HOST_NAME , _hostName);
        // tmpMap.insert(JSON_TCP_DEVICE_WORKS_NUM , _workStationNum);  // PCS Plugin Can't Self Define Works Num;
        tmpMap.insert(JSON_TCP_DEVICE_IP , _localIp);
        tmpMap.insert(JSON_TCP_DEVICE_PORT , _localPort);
        tmpMap.insert(JSON_TCP_DEVICE_TYPE , "pcsPlugin");
        tmpMap.insert(JSON_TCP_DEVICE_VER , _pcsPluginVersion);
        tmpMap.insert(JSON_LOGIN_WITH_CONTAINS_ORDER_DETAIL, true);
        tmpMap.insert(JSON_STOREID , _storeId);

        setPosMetaData(tmpMap);
    }

    return !_isTimeout;
}


void SocketCommunicate::networkOuttime(bool timeOut)
{
    if( !timeOut ) {
        _networkErrIndex = 0;
    } else {
        _networkErrIndex++;
        QLOG_INFO() << "SocketCommunicate::networkOuttime::  _networkErrIndex = "<< _networkErrIndex;
    }
}


bool SocketCommunicate::loginTcpSvr()
{
    QByteArray requestdata;
    QJsonObject json;
    SocketCommuProcess::getJsonFormatLoginReq(_posMetaData, json);
    if ( !SocketCommuProcess::getByteFormatLoginReq(json, requestdata, _certPublishKey, _certPrivateKey)  ) {
        QLOG_ERROR() << "loginTcpSvr:: Get Tcp Login Request QByteArray Failed";
        _sslSocket->disconnectFromHost();
        return false;
    }

    QLOG_INFO() << "loginTcpSvr:: Tcp  Login  Request QByteArray : " << requestdata.toHex();

    _isTimeout = true;
    if ( _sslSocket && _sslSocket->isOpen() && ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) {
        _sslSocket->write(requestdata);
    } else {
        QLOG_INFO() << "loginTcpSvr:: _sslSocket Is Null  Or  _sslSocket->isOpen() Is False  Or  _sslSocket Is Not ConnectedState ";
        if ( _sslSocket )  _sslSocket->disconnectFromHost();
        return false;
    }

    {
        QEventLoop loop; QTimer timer;
        connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
        timer.start(10*1000);
        loop.exec();
    }

    // Login Tcp Svr Success, Then Lambda Fuc  Will  Change  Flag: _isTimeout = false;
    if(_isTimeout) {
        QLOG_ERROR() << "loginTcpServer:: Send Login Request Msg To PosAgent Failed: " << ( _sslSocket ? _sslSocket->errorString() : " _sslSocket Is Null ") ;
        if ( _sslSocket ) _sslSocket->disconnectFromHost();
        return false;
    } else {
        QLOG_INFO() << "loginTcpServer:: Send Login Request Msg To PosAgent Success";
    }

    _isTimeout = true;
    {
        QEventLoop loop;
        QTimer timer;
        connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &SocketCommunicate::readReady, &loop, &QEventLoop::quit);
        timer.start(30*1000);
        loop.exec();
    }

    if(_isTimeout) {
        QLOG_ERROR() << "loginTcpServer:: Receive Login Response Msg From PosAgent Failed, TimeOut: true   sslSocketError: " << ( _sslSocket ? _sslSocket->errorString() : " _sslSocket Is Null ");
        if ( _sslSocket ) _sslSocket->disconnectFromHost();
        return false;
    }

    QByteArray headByteData = ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ? _sslSocket->read(sizeof(Data_Head)) : "" ;
    Data_Head outDataHead = {0};
    if ( !SocketCommuProcess::getHeadAllData( headByteData, outDataHead ) )
    {
        if ( CMD_AUTH_LOGIN_RESP != outDataHead.type) {
            QLOG_ERROR() << "loginTcpServer:: Login Receive Head Failed / Not Login Response ";
            if ( _sslSocket ) _sslSocket->disconnectFromHost();
        } else {
            QLOG_INFO() << "loginTcpServer::  Login Receive Head Successed,  But headByteData Too Short Or Type Error ";
        }

        return false;
    }

    QByteArray body = ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ? _sslSocket->read(outDataHead.length) : "" ;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(body);

    if(jsonDocument.isNull()) {
        QLOG_ERROR() << "loginTcpServer:: Login Receive Body Is Null / Empty";
        if ( _sslSocket ) _sslSocket->disconnectFromHost();
        return false;
    }

    QJsonObject rspJson = jsonDocument.object();
    QLOG_INFO() << "loginTcpServer:: Receive Login Response Json Body : " << rspJson;

    if ( rspJson.contains(JSON_TCP_STA_RST) && rspJson[JSON_TCP_STA_RST].toBool() == TRUE ) {
        return true;
    } else {
        QLOG_ERROR() << "loginTcpServer:: Receive Login Response Json Body No Contains Key:'ok' Or Key:'ok'->value Not True";
        if ( _sslSocket ) _sslSocket->disconnectFromHost();
        return false;
    }

}


void SocketCommunicate::acceptTcpSvrSendMsg()
{
    _socketHeartTimer = new QTimer(this);
    connect(_socketHeartTimer,&QTimer::timeout,this,&SocketCommunicate::sendSocketHeart);
    _socketHeartTimer->start(  SOCKET_HEARTBEAT_INTERVAL * 1000 );

    while(!_stopFlag && !_needTcpLogin)
    {
        QLOG_INFO() << "acceptTcpSvrSendMsg:: Wait Recv Socket Msg ....................................";

        if ( _sslSocket && QAbstractSocket::ConnectedState != _sslSocket->state() ) {
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Before Waitting Recv Socket Msg,  Check _sslSocket->state()  != ConnectedState, _sslSocket Is Invalid, Wait Reconnect...... ";
            break;
        }

        _isTimeout = true;
        {
            QEventLoop loop;
            QTimer timer;
            connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
            connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
            connect(this, &SocketCommunicate::readReady, &loop, &QEventLoop::quit);
            timer.start(120*1000);
            loop.exec();
        }

        if(_isTimeout) {
            if ( !_sslSocket  ||  ( QAbstractSocket::ConnectedState != _sslSocket->state() )   ) {
                QLOG_INFO() << "acceptTcpSvrSendMsg:: Wait Recv Socket Msg,  _sslSocket Is Null Or Not ConnectedState, Maybe Had Been Closed By Remote PosAgent Service";
                break;
            }
            QDateTime tmpCurDateTime = QDateTime::currentDateTime();
            int seconds = tmpCurDateTime.secsTo(_socketHeartBeatDateTime);
            if ( abs(seconds) > ( SOCKET_HEARTBEAT_INTERVAL + 20 ) ) {
                QLOG_ERROR() << "acceptTcpSvrSendMsg:: Nothing Recv, Timeout , Wait Retry Connect Server...... ";
                break;
            } else {
                QLOG_INFO() << "acceptTcpSvrSendMsg:: Nothing Recv, Continue Wait Recv Msg Because Diff Last HeartBeat Stamps < ( SOCKET_HEARTBEAT_INTERVAL + 20 ) , Value: " << abs(seconds);
                continue;
            }
        }

        // <1> Read Socket Msg Head;
        QByteArray headByteData = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) ? _sslSocket->read( sizeof( Data_Head ) ) : "";
        if ( 0 == headByteData.size() ) {
            // 当定时器触发主动向 POSAgent 发送心跳请求后, 此处会接收到 0 字节 ReadReady 事件,应该忽略此种情况,继续接受数据.
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Receive Head Data Size Is Zore, Continue Recv...... ";
            continue;
        }

        Data_Head outDataHead = {0};
        if ( !SocketCommuProcess::getHeadAllData( headByteData, outDataHead ) ) {
            //清空内部缓冲器重新读
            QLOG_ERROR() << "acceptTcpSvrSendMsg:: Receive Head Data Failed / Not Normal Response ";
            ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) )  ? _sslSocket->readAll() : "" ;
            continue;
        }

        // Declare Common Variable;
        int actionId = 0;
        QString outErrorMsg = "";
        QString outOrderId = "";
        QByteArray data;
        data.clear();
        QJsonObject inputJsonObj;

        //<1.1> Case: Recv PosAgent Service HeartBeat Request Msg;
        if(outDataHead.type == CMD_RECV_HEARTBEAT_REQ)
        {
            ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ) ? _sslSocket->read(outDataHead.length) : "";
            // Prepare Response Server Heart Beat
            SocketCommuProcess::getByteFormatHeartResp(data);
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Prepare HeartBeat Response Data For PosAgnet, Sequence: " << outDataHead.sequence <<"  Heart Beat: " << data.toHex();
            int len = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) )  ? _sslSocket->write(data) : 0 ;

            _isTimeout = true;
            {
                QEventLoop loop;
                QTimer timer;
                connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
                timer.start(30*1000);
                loop.exec();
            }

            // Sync Heart Beat Communicate Sunccess Stamp;
            if ( !_isTimeout &&  ( 0 != len )  ) {
                _socketHeartBeatDateTime = QDateTime::currentDateTime();
            } else {
                if ( !_sslSocket  ||  ( _sslSocket && ( QAbstractSocket::ConnectedState != _sslSocket->state() ) ) ||  0 == len   ) {
                    QLOG_INFO() << "acceptTcpSvrSendMsg:: _sslSocket Is Null Or Not ConnectedState, Write Data Len : "<< len
                                <<" Maybe Had Been Closed By Remote PosAgent Service ";
                    break;
                }
            }
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Socket Write Heart Beat Response Data , Sequence: " << outDataHead.sequence <<"  Len: " << len
                        << ( _isTimeout ? "  TimeOut" : "  Success")<< "   syncHeartBeatStamp: "<<_socketHeartBeatDateTime.toString("yyyy-MM-dd hh:mm:ss");
            continue;
        }
        //<1.2> Case: Recv PosAgent Service Response Plugin'self  heartbeat Msg;
        else if ( outDataHead.type == CMD_RECV_HEARTBEAT_RESP )
        {
            QByteArray msgData = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) ? _sslSocket->read(outDataHead.length) : "";
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Recv HeartBeat Response From PosAgnet Success, Sequence: " << outDataHead.sequence << "  Msg: " << msgData.data();
            _socketHeartBeatDateTime = QDateTime::currentDateTime();
            continue;
        }
        //<1.3> Case: Recv Push OrderStatus Msg;
        else if(outDataHead.type == CMD_RECV_MSG_REQ)
        {
            // Recv Server Push OrderStatus Msg;
            QByteArray msgData = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) ? _sslSocket->read(outDataHead.length) : "";
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Recv PosAgnet Push OrderStatus, Sequence: " << outDataHead.sequence << "  Msg: " << msgData.data();

            // Check Server Push Msg Is Valid
            if ( checkRecvMsgValid(msgData, actionId, outOrderId, outErrorMsg) ) {
                inputJsonObj.insert("iscontinue", 1);
                inputJsonObj.insert("msg", "success");
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 100);
            } else {
                inputJsonObj.insert("iscontinue", 0);
                inputJsonObj.insert("msg", outErrorMsg);
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 0);
            }
            // Prepare Response Data For Server Push OrderStatus;
            SocketCommuProcess::getByteFormatMsgResp(data, outDataHead.sequence , inputJsonObj);
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Prepare Response Data For PosAgnet Push OrderStatus, Sequence: " << outDataHead.sequence << "  MsgData: " << data.toHex();
            int len = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ) ? _sslSocket->write(data) : 0 ;

            _isTimeout = true;
            {
                QEventLoop loop;
                QTimer timer;
                connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
                timer.start(30*1000);
                loop.exec();
            }
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Socket Write OrderStatus Resp Data, Sequence: " << outDataHead.sequence  <<"  Len: " << len << ( _isTimeout ? "  TimeOut" : "  Success" ) ;

            if  ( _isTimeout ) {
                if ( !_sslSocket  ||  ( QAbstractSocket::ConnectedState != _sslSocket->state() )   ) {
                    QLOG_INFO() << "acceptTcpSvrSendMsg::  _sslSocket Is Null Or Not ConnectedState, Maybe Had Been Closed By Remote PosAgent Service";
                    break;
                }
            }

            continue;
        }
        //<1.4> Case: Recv Auth Login Resp Msg [ impossible];
        else if(outDataHead.type == CMD_AUTH_LOGIN_RESP)
        {
            // Recv Server Response Msg Of  Self  Login  Request ;
            QByteArray msgData = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) ? _sslSocket->read(outDataHead.length) : "" ;
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Recv PosAgent Response Login Req , Sequence: " << outDataHead.sequence << "  Msg: " << msgData.data();
            // Check Server Push Msg Is Valid
            if ( checkRecvMsgValid(msgData, actionId, outOrderId, outErrorMsg) ) {
                inputJsonObj.insert("iscontinue", 1);
                inputJsonObj.insert("msg", "success");
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 100);
            } else {
                inputJsonObj.insert("iscontinue", 0);
                inputJsonObj.insert("msg", outErrorMsg);
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 0);
            }
            // Prepare Response Data For Server Response  Login Request;
            SocketCommuProcess::getByteFormatMsgResp(data, outDataHead.sequence, inputJsonObj);
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Prepare Response Data For PosAgent Response  Login Request, Sequence: " << outDataHead.sequence
                        << "  MsgData: "<<data.toHex();
            int len = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ) ? _sslSocket->write(data) : 0 ;

            _isTimeout = true;
            {
                QEventLoop loop;
                QTimer timer;
                connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
                timer.start(30*1000);
                loop.exec();
            }
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Socket Write Login Resp'Resp Data (Impossible Occur), Sequence: " << outDataHead.sequence <<" Len: "<< len
                        << ( _isTimeout ? "  TimeOut" : "  Success") ;

            if  ( _isTimeout ) {
                if ( !_sslSocket  ||  ( QAbstractSocket::ConnectedState != _sslSocket->state() )   ) {
                    QLOG_INFO() << "acceptTcpSvrSendMsg::  _sslSocket Is Null Or Not ConnectedState, Maybe Had Been Closed By Remote PosAgent Service";
                    break;
                }
            }

            continue;
        }
        //<1.5> Case: Recv Socket Msg Type No Process , DataHead.type Havn't Match Success [ Maybe ]; Need Response, Or  POS Agent Will Still Push;
        else {
            // Recv Pos Agent Pushed Undefine Action Id Msg;
            QByteArray msgData = ( _sslSocket &&  ( QAbstractSocket::ConnectedState == _sslSocket->state() )  ) ? _sslSocket->read(outDataHead.length) : "" ;
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Recv PosAgnet Push Undefine ActionId Msg , Sequence: " << outDataHead.sequence << "  Msg: " << msgData.data();

            // Check Server Push Msg Is Valid
            if ( checkRecvMsgValid(msgData, actionId, outOrderId, outErrorMsg) ) {
                inputJsonObj.insert("iscontinue", 1);
                inputJsonObj.insert("msg", "success");
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 100);
            } else {
                inputJsonObj.insert("iscontinue", 0);
                inputJsonObj.insert("msg", outErrorMsg);
                inputJsonObj.insert("orderId", outOrderId);
                inputJsonObj.insert("statusCode", 0);
            }

            // Prepare Response Data For PosAgnet Request;
            SocketCommuProcess::getByteFormatMsgResp(data, outDataHead.sequence, inputJsonObj);
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Prepare Response Data For PosAgnet Push Undefine Msg Type, Sequence: " << outDataHead.sequence << "  MsgData: "<<data.toHex();
            int len = ( _sslSocket &&  ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ) ? _sslSocket->write(data) : 0 ;

            _isTimeout = true;
            {
                QEventLoop loop;
                QTimer timer;
                connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
                timer.start(30*1000);
                loop.exec();
            }
            QLOG_INFO() << "acceptTcpSvrSendMsg:: Socket Write Resp Undefine Msg Type Data, Sequence: " << outDataHead.sequence <<" Len: "<< len
                        << ( _isTimeout ? "  TimeOut" : "  Success") ;

            if  ( _isTimeout ) {
                if ( !_sslSocket  ||   ( QAbstractSocket::ConnectedState != _sslSocket->state() )   ) {
                    QLOG_INFO() << "acceptTcpSvrSendMsg::  _sslSocket Is Null Or Not ConnectedState , Maybe Had Been Closed By Remote PosAgent Service";
                    break;
                }
            }

            continue;
        }

        QLOG_ERROR() << "acceptTcpSvrSendMsg:: Not Support This Command Type, Sequence: " << outDataHead.sequence << "  MsgType: "<< outDataHead.type;
        ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() )   ) ?  _sslSocket->readAll() : "";

        break;   // Occur  Once  Undefine CMD_TYPE , Will Break Loop Recive Msg, Until threadStart() Retry Entry;
    }

    _socketHeartTimer->deleteLater();
}


bool SocketCommunicate::checkRecvMsgValid(const QByteArray &msgData, int &actionId, QString &outOrderId, QString &outErrorMsg)
{
    QLOG_INFO() << "checkRecvMsgValid:: Begin Check msgData ......";
    QJsonDocument document = QJsonDocument::fromJson(msgData);
    bool checkVaildRet = false;

    if ( ! _httpLoginResult ) {
        outErrorMsg =  QString("checkRecvMsgValid:: msgData:  Store Pos Plugin Havn't Login Success, Need Waitting......");
        QLOG_INFO() << outErrorMsg;
        emit showAlert(AlertForm::MSGERROR, QString::fromLocal8Bit("PosPlugin Not Yet Login Succeed") );
        return false;
    }

    if ( document.isNull() ) {
        outErrorMsg =  QString("checkRecvMsgValid::  QJsonDocument::fromJson(*) Failed, document Is Null ");
        QLOG_ERROR() << outErrorMsg;
    } else {
        // Parse Doc Key And Value;
        QJsonObject orderStatusObject = document.object();
        if ( orderStatusObject.contains("data") ) {
            QString orderDataString = orderStatusObject["data"].toString();
            QJsonObject orderData = QJsonDocument::fromJson( orderDataString.toUtf8() ).object() ;
            if ( !orderData.contains("actionId") ) {
                outErrorMsg = QString("checkRecvMsgValid::  Oms Push Order Status Havn't Contain  Import Key -> 'actionId' ");
                QLOG_ERROR() << outErrorMsg<< orderData;
                return false;
            }

            actionId = orderData["actionId"].toString().toInt();
            bool recvSocketMsgRet = false;
            if ( 11 == actionId ) {
                if ( orderData.contains("channel") && orderData.contains("storeId") && orderData.contains("orderId") && orderData.contains("status")  ) {
                    outOrderId = orderData["orderId"].toString();
                    //Emit Signal Maybe Miss Msg,  Direct Call Slot Function;
                    QByteArray TempOrderAbstract= QJsonDocument(orderData).toJson();
                    recvSocketMsgRet = FlowControl::GetInstance().onRecvSocketPushedMsg( TempOrderAbstract, actionId );
                    if ( recvSocketMsgRet ) {
                        checkVaildRet =  true;
                    } else {
                        outErrorMsg = QString("checkRecvMsgValid::  PosPlugin Occour Error When Oms Push Order Status");
                        QLOG_ERROR() << outErrorMsg;
                    }
                } else {
                    outErrorMsg = QString("checkRecvMsgValid::  Oms Push Order Status Havn't Contain  Import Key ");
                    QLOG_ERROR() << outErrorMsg;
                }
            } else if ( 21 == actionId ) {
                if ( orderData.contains("orderId") ) {
                    outOrderId = orderData["orderId"].toString();
                    //Emit Signal Maybe Miss Msg,  Direct Call Slot Function;
                    QByteArray TempOrderAbstract= QJsonDocument(orderData).toJson();
                    recvSocketMsgRet = FlowControl::GetInstance().onRecvSocketPushedMsg( TempOrderAbstract, actionId );
                    if ( recvSocketMsgRet ) {
                        checkVaildRet =  true;
                    } else {
                        outErrorMsg = QString("checkRecvMsgValid::  PosPlugin Occour Error When Oms Push Order Status' ");
                        QLOG_ERROR() << outErrorMsg;
                    }
                } else {
                    outErrorMsg = QString("checkRecvMsgValid::  Oms Push Order Status Havn't Contain  Import Key ");
                    QLOG_ERROR() << outErrorMsg;
                }
            } else {
                outErrorMsg = QString("checkRecvMsgValid::  OMS Push Plugin Action Out Of Range");
                QLOG_ERROR() << outErrorMsg;
            }

        } else {
            outErrorMsg =  QString("checkRecvMsgValid::  Oms Push Order Status Havn't Contain Key -> 'data' ");
            QLOG_ERROR() << outErrorMsg;
        }

    }

    return checkVaildRet;
}


bool SocketCommunicate::sendSocketHeart()
{
    // <0> Check Whether Need Send HeatBeat;
    if ( _stopFlag ||  _needTcpLogin ) {
        QLOG_INFO() << "sendSocketHeart:: Now No Need Send Heart Beat By Self ............";
    } else {
        QLOG_INFO() << "sendSocketHeart:: PosPlugin Begin Send Heart Beat ............";
    }

    // <0.1> Long Time No Recv PosAgnet Push Socket Heart Beat Request, Then PosPlugin Will Send HeartBeat By Self.
    QDateTime tmpCurDateTime = QDateTime::currentDateTime();
    int seconds = tmpCurDateTime.secsTo(_socketHeartBeatDateTime);
    if ( abs(seconds) < SOCKET_HEARTBEAT_INTERVAL ) {
        QLOG_INFO() << "sendSocketHeart:: No Need Send HeartBeat By Plugin , currentDateTime(): "
                    << tmpCurDateTime.toString("yyyy-MM-dd hh:mm:ss")<< " Seconds Diff From Server Send Heart Time: "<<abs(seconds);
        return true;
    }

    // Test Remote Server Is Down , _sslSocket->isOpen() == true  But  _sslSocket->state() == UnconnectedState ;
    QLOG_INFO() << "sendSocketHeart:: _sslSocket->state(3:ConnectedState): " << ( _sslSocket ? _sslSocket->state() : -1 )
                << " _sslSocket->errorString:  " << ( _sslSocket ?  _sslSocket->errorString() : "" )
                <<" _sslSocket->isOpen: "<<( _sslSocket ? _sslSocket->isOpen() : -1 ) ;

    if ( !_sslSocket ||  ( QAbstractSocket::ConnectedState !=_sslSocket->state() )  ) {
        QLOG_INFO() << "sendSocketHeart:: Check Socket Before Write,  _sslSocket Is Null Or Not ConnectedState , Maybe Had Been Closed By Remote PosAgent Service ";

        if ( _sslSocket && _sslSocket->isOpen() ) {
            QLOG_INFO() << "sendSocketHeart:: Check Socket Before Write, _sslSocket Is Open , But Not ConnectedState , Must disconnectFromHost ";
            _sslSocket->disconnectFromHost();
        }

        _needTcpLogin = 1;
        return false;
    }

    // <1> Prepare Send Heart Beat Request Data;
    QByteArray sendHeartBeatReq;
    SocketCommuProcess::getByteFormatHeartReq(sendHeartBeatReq);

    // <2> Send Heart Beat Request;
    QLOG_INFO() << "sendSocketHeart:: Send HeartBeat Request By Self QByteArray : " << sendHeartBeatReq.toHex();

    _isTimeout = true;
    int len = ( _sslSocket && ( QAbstractSocket::ConnectedState == _sslSocket->state() ) ) ? _sslSocket->write(sendHeartBeatReq) : 0;

    {
        QEventLoop loop; QTimer timer;
        connect(this, &SocketCommunicate::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &SocketCommunicate::writeReady, &loop, &QEventLoop::quit);
        timer.start(10*1000);
        loop.exec();
    }

    // Send HeartBeat Success, Then Lambda Fuc  Will  Change  Flag: _isTimeout = false;
    if(_isTimeout ||  0 == len) {
        QLOG_ERROR() << "sendSocketHeart:: Send HeartBeat To PosAgnet Failed: "<< " len: "<< len << ( _sslSocket ? _sslSocket->errorString() : " _sslSocket Is Null " );
        if ( _sslSocket ) _sslSocket->disconnectFromHost();
        _needTcpLogin = 1;
        return false;
    } else {
        QLOG_INFO() << "sendSocketHeart:: Send HeartBeat To PosAgnet Success , len: "<< len ;
        _socketHeartBeatDateTime = QDateTime::currentDateTime();
    }

    return true;
}


bool SocketCommunicate::GetSvrIpPort()
{
    // Get Url Address: Host Ip + Port;
    // stgUrl = posagent.stg.starbucks.net [ 10.92.222.118 ]   23001
    // uatUrl = posagent.uat.starbucks.net [ 10.92.222.119 ]   23001
    // proUrl = posagent.starbucks.net [ 10.92.222.48 ]         23001
    _tcpSvrPort = 23001;

    QString  httpLoginUrl = ConfigManger::GetInstance().GetLoginServerUrl();
    if ( httpLoginUrl.contains(".stg.") ) {
        _domainName = "posagent.stg.starbucks.net";
        _getCertificateUrl = GET_V1_STG_CERTIFICATE_URL;    // STG环境 V1 版本地址;
    } else if ( httpLoginUrl.contains(".uat.") ) {
        _domainName = "posagent.uat.starbucks.net";
        _getCertificateUrl = GET_V1_UAT_CERTIFICATE_URL;    // UAT环境 V1 版本地址;
    }else if ( httpLoginUrl.contains(".gray.") ) {
        _domainName = "posagent.gray.starbucks.net";
        _getCertificateUrl = GET_V1_GRAY_CERTIFICATE_URL;    // GRAY环境 V1 版本地址;
    }else if ( httpLoginUrl.contains(".dev.") ) {
        _domainName = "posagent.dev.starbucks.net";
        _getCertificateUrl = GET_V1_DEV_CERTIFICATE_URL;    // DEV环境 V1 版本地址;
    } else {
        _domainName = "posagent.starbucks.net";
        _getCertificateUrl = GET_V1_PRO_CERTIFICATE_URL;    // PRO环境 V1 版本地址;
    }

    QHostInfo info=QHostInfo::fromName(_domainName);
    foreach(QHostAddress address, info.addresses()) {
        if(address.protocol() == QAbstractSocket::IPv4Protocol) {
            QLOG_INFO()<<QString("[<<<<---SocketCommunicate:: Foreach GetSvrIpPort = %1:%2--->>>>]").arg( address.toString() ).arg( _tcpSvrPort );
            //1.1.1.1 > length > 7
            if(address.toString().length()>7 && address.toString()!= "127.0.0.1" ) {
                _tcpSvrIp = address.toString();
                QLOG_INFO()<<QString("[<<<<---SocketCommunicate:: Find Correct  Ip&Port,  _domainName: %1 , _getCertificateUrl: %2 , SvrIp: %3 , Port: %4--->>>>]")
                             .arg( _domainName ).arg( _getCertificateUrl ).arg( address.toString() ).arg( _tcpSvrPort );
                break;
            }
        }
    }

    if ( !_tcpSvrIp.length() &&  !_tcpSvrPort) {
        QLOG_INFO()<<"GetSvrIpPort Failed, Can't  Analysis  _domainName: "<< _domainName<< "   / error  _tcpSvrPort: "<<_tcpSvrPort;
        return false;
    }

    return true;
}


void SocketCommunicate::onFlowControlLoginSuccess(bool fcLoginResult, bool fcOpenTcpFlag, QString &storeId, QString &posIp, QString &posHostName, QString &posWorkStationNum ) {

    // 只有 FlowControl  Http 登录成功后, FlowControl 才会发生信号 过来(跨线程);
    // 存在一定概率出现跨线程 发送信号 失败;
    QLOG_INFO() << QString("SocketCommunicate::onFlowControlLoginSuccess::  fcLoginResult: %1, fcOpenTcpFlag: %2 , storeId: %3 , posIp: %4 , posHostName: %5 , posWorkStationNum: %6")
                   .arg( fcLoginResult?1:0 ).arg( fcOpenTcpFlag?1:0 ).arg( storeId ).arg( posIp ).arg( posHostName ).arg( posWorkStationNum );
    _httpLoginResult = fcLoginResult;
    _openTcpChannel = fcOpenTcpFlag;
    _storeId = storeId;
    // _pcsPluginVersion = APP_VERSION;
    _hostName = posHostName;
    _workStationNum = posWorkStationNum;
    _localIp = posIp;
}


void SocketCommunicate::threadStart()
{
    QLOG_INFO() << "SocketCommunicate::threadStart:: " << QThread::currentThreadId() << "  , _stopFlag: "<< _stopFlag << ",  _stoped: " <<_stoped ;

    // <1> 获取长连接的目标 IP + 端口;
    while( !_httpLoginResult || !_openTcpChannel  ||  !_stopFlag && !GetSvrIpPort())
    {
        if ( _stoped ) {
            QLOG_INFO() << QString("threadStart()  This Thread Had Been Set Need Quit Flag, Now Return. ");
            return;
        }
        QLOG_INFO() << QString("Waitting HttpLogin / openTcpChannel / GetSvrIpPort Failed, Wait 60 Seconds,  _httpLoginResult:%1, _openTcpChannel:%2, _stopFlag:%3")
                        .arg( _httpLoginResult?1:0 ).arg(_openTcpChannel?1:0).arg(_stopFlag?1:0);
        Sleep(20*1000);
        FlowControl::GetInstance()._GetFcMajorInfo(_httpLoginResult, _openTcpChannel, _storeId, _localIp,  _hostName, _workStationNum);
    }

    // <2> 向目标 IP + 端口发起长连接 与 登录请求, 登录成功后接受 Server Response Msg;
    while ( !_stopFlag ) {

        // Reconnet Pos Agent Server Socket Rate;
        if ( _reconnectTimes  < 5 ) {
            Sleep( 1*60*1000 );
        } else if ( _reconnectTimes  < 20 ) {
            Sleep( 5*60*1000 );
        } else {
            Sleep( 10*60*1000 );
        }

        do {
            // <2.1> Connect Svr , Establish Connection;
            if( !connectTcpSvr() ) {
                networkOuttime(true);
                QLOG_ERROR() << "threadStart:: connectTcpSvr  Failed";
                _reconnectTimes++;
                break;
            } else {
                networkOuttime(false);
                _reconnectTimes = 0;
                QLOG_INFO() << "threadStart:: connectTcpSvr  Success, Will Login Tcp PosAgent......";
            }

            // <2.2> Send Login Request;
            if( !loginTcpSvr() ) {
                QLOG_ERROR() << "threadStart:: loginTcpSvr  Failed";
                _reconnectTimes++;
                break ;
            } else {
                _reconnectTimes = 0;
                QLOG_INFO() << "threadStart:: loginTcpSvr Success, Will  Accept Tcp Svr Send Msg...... _reconnectTimes: "<< _reconnectTimes;
            }

            _needTcpLogin = 0;

            // <2.3> Accept PosAgnet Send Msg In While(*);
            acceptTcpSvrSendMsg();

            networkOuttime(true);
            _reconnectTimes++;
        } while(0);

        //网络异常断开重连, 释放资源 _sslSocket;
        QLOG_INFO() << QString("threadStart:: _reconnectTimes = %1 ").arg( _reconnectTimes )<<", Check Socket Is Error, Will Close  _ssSocket: "
                    <<_sslSocket <<"  _sslSocket.isopen() = "<<_sslSocket->isOpen() <<"  _sslSocket.state(3:ConnectedState) = "<<_sslSocket->state();

        CLOSE_SOCKES(_sslSocket);

        if(_stopFlag)
            break;

        {
            QEventLoop loop;
            connect(this, &SocketCommunicate::quit, &loop,&QEventLoop::quit);
            QTimer::singleShot(60000, &loop, SLOT(quit()));
            loop.exec();
        }

    }

    QLOG_INFO() << "SocketCommunicate::threadStart:: End And Exit......";
    _stoped = true;
}
