#ifndef SOCKETCOMMUNICATE_H
#define SOCKETCOMMUNICATE_H

#include <QObject>
#include <QMap>
#include <QTcpSocket>
#include <QJsonObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QSslSocket>
#include <qabstractsocket.h>
#include <QApplication>
#include <QHostInfo>
#include <QThread>

#include "preDefine.h"
#include "Network\ThreadSocket.h"
#include "QsLog.h"
#include <WinSock.h>
#include "SslOperate/MacTool.h"
#include "SslOperate/certificateOperate.h"

#include "alertForm.h"


#define CLOSE_SOCKES(socket)   do{      \
    if ( socket != NULL ) {                            \
        if ( socket->isOpen() )                         \
            socket->close();                              \
                                                                   \
        delete socket;                                       \
        socket = NULL;                                   \
    }                                                              \
} while(0)


#define DATAHEAD_INIT(head, mgc, ver, seq, cmd, serize, len)  do{            \
    head.magic = mgc;                                   \
    head.version = ver;                                   \
    head.sequence = seq;                                \
    head.type = cmd;                                      \
    head.serialize = serize;                               \
    head.length = htonl(len);                            \
} while(0)


//防止内存对齐
#ifdef WIN32
    #pragma pack(push)
    #pragma pack(1)
#else
    __attribute__ ((aligned (push)))
    __attribute__ ((aligned (1)))
#endif
typedef struct
{
    quint16 magic;
    quint8 version;
    quint64 sequence;
    quint8 type;
    quint8 serialize;
    quint32 length;
} Data_Head;


//恢复内存对齐方式
#ifdef WIN32
    #pragma pack(pop)
#else
    __attribute__ ((aligned (pop)))
#endif

// Command Type Define;
#define CMD_MIN              0
#define CMD_AUTH_LOGIN_REQ            1
#define CMD_AUTH_LOGIN_RESP           2
// Plugin Send Heart Beat To Svr;
#define CMD_SEND_HEARTBEAT_REQ    3
#define CMD_SEND_HEARTBEAT_RESP   4
// Plugin Recv Heart Beat From Svr;
#define CMD_RECV_HEARTBEAT_REQ    3
#define CMD_RECV_HEARTBEAT_RESP   4
// Send / Recv Error Resp;
#define CMD_SEND_ERR_RESP   5
#define CMD_RECV_ERR_RESP   5
// Recv Svr  Request;
#define CMD_RECV_MSG_REQ             6
#define CMD_RECV_MSG_RESP           7

#define CMD_MAX              2000

// Default Version
#define DEFT_VERSION             1
#define DEFT_MAGIC                 0XAAAA

// 接受OMS订单推送消息, 阻塞模式必须拥有单独线程

class SocketCommunicate : public ThreadSocket
{
    Q_OBJECT
public:
    explicit SocketCommunicate(ThreadSocket *parent = 0);

    ~SocketCommunicate();

    void setPosMetaData(const QVariantMap &posMetaData);

    void networkOuttime(bool timeOut);

    private:

    //<1> 链接到服务器
    bool connectTcpSvr();

    //<2> 登录及身份信息验证
    bool loginTcpSvr();

    //<3> 长连接建立后 接受服务端发出的消息;
    void acceptTcpSvrSendMsg();

    //检查 Server 推送的 MSG 是否有效;
    bool checkRecvMsgValid(const QByteArray &msgData, int & actionId, QString &outOrderId, QString &outErrorMsg);

    //向 PosAgent 发送心跳请求, 校验socket通信链路的有效性;
    bool sendSocketHeart();

    private:
        bool GetSvrIpPort();

    public slots:
        //Socket Thread Recv Msg Entry
        void threadStart();

        void onFlowControlLoginSuccess( bool fcLoginResult, bool fcOpenTcpFlag, QString &storeId, QString &posIp, QString &posHostName, QString &posWorkStationNum );

    signals:
        void connected();

        void writeReady();

        void readReady();

        void showAlert(AlertForm::Type type, const QString& msg);

    private:
        //门店信息
        QVariantMap _posMetaData;
        //重新登录标识
        qint8 _needTcpLogin;
        //Http 门店POS插件登录结果;
        bool  _httpLoginResult;
        // Http 门店POS插件登录成功后，是否开通 TCP Scoket Channel;
        bool _openTcpChannel;
        //插件获取的POS门店编号;
        QString  _storeId;
        // Mac Address;
        QString  _macAddress;
        // HostName;
        QString _hostName;
        // WorkStation Id POS工作站编号;
        QString _workStationNum;
        // Pos Local Machine IP;
        QString  _localIp;
        // Pos Plugin Server Port;
        int _localPort;
        // PcsPluginVersion;
        QString  _pcsPluginVersion;
        // PosPlugin Socket HeartBeat Timer;
        QTimer *_socketHeartTimer;
        // PosPlugin Send Socket HeartBeat / PosAgent Send Socket Heartbeat  Time;
        QDateTime _socketHeartBeatDateTime;

        //超时标识
        bool _isTimeout;
        //发起长连接重连次数;
        int _reconnectTimes;
        //长连接套接字
        QSslSocket *_sslSocket;
        //获取服务器列表地址
        QString _domainName;
        //获取证书秘钥地址
        QString _getCertificateUrl;
        // 通过域名解析得到 Host Ip ; 默认端口: 8888
        QString _tcpSvrIp;
        int _tcpSvrPort;
        //网络异常编号;
        int _networkErrIndex;
        // Certificate PrivateKey;
        QString  _certPrivateKey;
        // Certificate PublishKey;
        QString  _certPublishKey;
};


class SocketCommuProcess {

public:
        friend class SocketCommunicate;

private:

        static bool getRealLen(QByteArray array, quint32 &len) {
            if(array.size() != sizeof(len))
                return false;

            quint32 tmplen = 0;
            memcpy((void *)&tmplen, (void *)array.data(), sizeof(len));
            len = ntohl(tmplen);
            return true;
        }

        //获取 解密后真实的 公钥(服务端) / 秘钥(客户端)
        static bool getRealPubKeyPriKey(QString encryptPubKey, QString encryptPriKey, QString &decryptPubKey, QString &decryptPriKey )
        {
            QString aesSecretKey = QString(AES_SECRET_KEY);
            QString aesSecretKeyIv = QString(AES_SECRET_KEY_IVP);

            QLOG_INFO() << "Get Real Public Key -----------------------------------------> ";
            if ( !CertificateOperate::GetSecrtKeyAESDecode(aesSecretKey, aesSecretKeyIv, encryptPubKey, decryptPubKey )  ) {
                QLOG_ERROR() << "getRealPubKeyPriKey  Get Real Public Key Failed ";
                return false;
            }

            QLOG_INFO() << "Get Real Private Key -----------------------------------------> ";
            if ( !CertificateOperate::GetSecrtKeyAESDecode(aesSecretKey, aesSecretKeyIv, encryptPriKey, decryptPriKey )  ) {
                QLOG_ERROR() << "getRealPubKeyPriKey  Get Real Private  Key Failed ";
                return false;
            }

            QLOG_INFO() << "Get Real Public & Private Key  End1-----------------------------------------> ";
            QLOG_INFO() << QString( "Get Real Publick Key= %1 , Real Private Key= %2 " ).arg( decryptPubKey ).arg( decryptPriKey );
            QLOG_INFO() << "Get Real Public & Private Key  End2-----------------------------------------> ";
            return true;
        }


        //获取 Login 请求数据
        static bool getByteFormatLoginReq(QJsonObject &json, QByteArray &outData, QString &publicKey, QString &privateKey )
        {
            Data_Head head = {0};
            //需要将 json 先进行自然序 进行排序,  加签,  然后在进行 新数据组装与拷贝;
            QJsonObject realReqJson;
            QString iv = QString(AES_SECRET_KEY_IVP);
            if ( !CertificateOperate::GetCertifiSign(json, iv, publicKey, privateKey, realReqJson)  ) {
                QLOG_ERROR() << "getByteFormatLoginReq  GetCertifiSign Failed ";
                return false;
            }
            QLOG_INFO() << "Get RSA Sign, Real Request Msg : " << realReqJson;

            QByteArray tmpInputData = QString(QJsonDocument( realReqJson ).toJson(QJsonDocument::Compact)).toUtf8();
            int lenMsg = tmpInputData.size();

            char *buf = (char *)calloc(1, sizeof(Data_Head) + lenMsg + 1);

            if ( ! buf ) {
                QLOG_ERROR() << "getByteFormatLoginReq:: calloc Memory Space Failed " << ( sizeof(Data_Head) + lenMsg + 1  );
                return false;
            }

            DATAHEAD_INIT( head, DEFT_MAGIC , DEFT_VERSION, 0, CMD_AUTH_LOGIN_REQ, 1, lenMsg );

            // Copy Data Head;
            memcpy(buf, (void *)&head, sizeof(Data_Head));
            // Copy Data Body;
            memcpy(buf + sizeof(Data_Head), tmpInputData.data(), lenMsg );
            outData = QByteArray(buf, sizeof(Data_Head) + lenMsg );
            free(buf);

            return true;
        }


        //获取 Socket心跳请求包 字节序数据;
        static void getByteFormatHeartReq(QByteArray &data)
        {
            Data_Head head = {0};
            //  sizeof(Data_Head) + 4:  通信头+四个字节的请求：ping
            char *buf = (char *)calloc(1, sizeof(Data_Head) + 4 + 1);

            if ( ! buf ) {
                QLOG_ERROR() << "getByteFormatHeartReq:: calloc Memory Space Failed " << ( sizeof(Data_Head) + 4 + 1  );
                return;
            }

            DATAHEAD_INIT( head, DEFT_MAGIC , DEFT_VERSION, 0, CMD_SEND_HEARTBEAT_REQ, 0, 4 );

            memcpy(buf, (void *)&head, sizeof(Data_Head));
            // 在消息头后, 填充消息内容
            memcpy(buf + sizeof(Data_Head) , "ping", 4);

            data = QByteArray(buf, sizeof(Data_Head) + 4);
            free(buf);
        }

        //获取心跳响应包
        static void getByteFormatHeartResp(QByteArray &data)
        {
            Data_Head head = {0};
            //  sizeof(Data_Head) + 4:  通信头+四个字节的响应：pong
            char *buf = (char *)calloc(1, sizeof(Data_Head) + 4 + 1);

            if ( ! buf ) {
                QLOG_ERROR() << "getByteFormatHeartResp:: calloc Memory Space Failed " << ( sizeof(Data_Head) + 4 + 1  );
                return;
            }

            DATAHEAD_INIT( head, DEFT_MAGIC , DEFT_VERSION, 0, CMD_RECV_HEARTBEAT_RESP, 0, 4 );

            memcpy(buf, (void *)&head, sizeof(Data_Head));
            // 在消息头后, 填充消息内容
            memcpy(buf + sizeof(Data_Head) , "pong", 4);

            data = QByteArray(buf, sizeof(Data_Head) + 4);
            free(buf);
        }


        //获取消息响应
        static void getByteFormatMsgResp(QByteArray &outData, quint64 sequence, const QJsonObject &inputJsonObj)
        {
            Data_Head head = {0};
            QByteArray tmpInputData = QString(QJsonDocument( inputJsonObj ).toJson(QJsonDocument::Compact)).toUtf8();
            int lenMsg = tmpInputData.size();
            // calloc heap Memory Space;
            char *buf = (char *)calloc(1, sizeof(Data_Head) + lenMsg + 1);

            if ( ! buf ) {
                QLOG_ERROR() << "getByteFormatHeartResp:: calloc Memory Space Failed " << ( sizeof(Data_Head) + lenMsg + 1  );
                return;
            }

            DATAHEAD_INIT( head, DEFT_MAGIC , DEFT_VERSION, sequence, CMD_RECV_MSG_RESP, 1, lenMsg );

            // Copy Data Head;
            memcpy( buf, (void *)&head, sizeof(Data_Head) );
            // Copy Data Body;
            memcpy( buf + sizeof(Data_Head), tmpInputData.data(), lenMsg );

            outData = QByteArray(buf, sizeof(Data_Head) + lenMsg );
            free(buf);
        }


        //检查并获取头部全部信息
        static bool getHeadAllData(const QByteArray &data, Data_Head &outHead)
        {
            if(data.size() < sizeof(Data_Head)) {
                QLOG_ERROR() << "getHeadAllData:: Origin Data Size Too Short :" << data.toHex() << "  data.size(): "<<data.size();
                return false;
            }

            Data_Head head = {0};
            memcpy(&head, data.data(), sizeof(Data_Head));
            outHead.magic = head.magic;
            outHead.version = head.version;
            outHead.sequence = head.sequence;
            outHead.type = head.type;
            outHead.serialize = head.serialize;
            outHead.length = ntohl(head.length);
            QLOG_INFO() << "getHeadAllData:: Origin Data : " << data.toHex() <<"  ;\n  magic: "<<outHead.magic
                        <<" version: "<<outHead.version<<" sequence: "<<outHead.sequence << " type: "<<outHead.type << " serialize: "
                       <<outHead.serialize<<" length: "<<outHead.length;

            return ( (head.type > CMD_MIN && head.type < CMD_MAX) && head.version == DEFT_VERSION);
        }


        static bool getJsonFormatLoginReq(QVariantMap map, QJsonObject &json )
        {
            if(!map.contains(JSON_STOREID))
                return false;
            json = QJsonDocument::fromJson( QJsonDocument::fromVariant( QVariant(map) ).toJson() ).object();
            /*  JSON_TCP_DEVICE_ID   JSON_TCP_DEVICE_IP  JSON_TCP_DEVICE_PORT  JSON_TCP_DEVICE_TYPE
             *  JSON_TCP_DEVICE_VER  JSON_STOREID */
            QLOG_INFO() << "getJsonFormatLoginReq:: "<< json;
            return true;
        }
    };

#endif // SOCKETCOMMUNICATE_H
