#ifndef OrderPushWork_H
#define OrderPushWork_H

#include <QObject>
#include <QMap>
#include <QTcpSocket>
#include <QJsonObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QSslSocket>


#include <QDebug>
#include <WinSock.h>
#include "preDefine.h"

#define EVENTWAIT(timer) do{                                \
        QEventLoop loop;                                    \
        QTimer::singleShot(timer, &loop, SLOT(quit()));     \
        loop.exec();                                        \
    } while(0)

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

#define DATAHEADINIT(head,ver,cmd,mask,len)  do{            \
    head.version = ver;                                     \
    head.cmdByte = cmd;                                     \
    head.bodyLen = htonl(len);                              \
    head.maskByte = mask;                                   \
} while(0)

#define DATAHEADNLINIT(head,ver,cmd,mask,len,synSeq)  do{   \
    head.version = ver;                                     \
    head.cmdByte = cmd;                                     \
    head.bodyLen = htonl(len);;                             \
    head.synSeq = htonl(synSeq);                            \
    head.maskByte = mask;                                   \
} while(0)

#define DATAHEADINITHEART(head,ver,cmd,mask)  do{           \
    head.version = ver;                                     \
    head.cmdByte = cmd;                                     \
    head.bodyLen = htonl(1);                                \
    head.maskByte = mask;                                   \
    head.endflag = -128;                                    \
} while(0)

//防止内存对齐
#ifdef WIN32
    #pragma pack(push)
    #pragma pack(1)
#else
    __attribute__ ((aligned (push)))
    __attribute__ ((aligned (1)))
#endif
typedef struct
{
    quint8 version;
    quint8 cmdByte;
    quint8 maskByte;
    quint32 bodyLen;
} Data_Head;

typedef struct
{
    quint8 version;
    quint8 cmdByte;
    quint8 maskByte;
    quint32 synSeq;
    quint32 bodyLen;
} Data_Head_NL;

typedef struct
{
    quint8 version;
    quint8 cmdByte;
    quint8 maskByte;
    quint32 bodyLen;
    qint8 endflag;
} Data_Head_Heart;

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


#define COMMAND_UNKNOW              0
#define COMMAND_HANDSHAKE_REQ       1
#define COMMAND_HANDSHAKE_RESP      2
#define COMMAND_AUTH_REQ            3
#define COMMAND_AUTH_RESP           4
#define COMMAND_LOGIN_REQ           5
#define COMMAND_LOGIN_RESP          6
#define COMMAND_MSG_REQ             11
#define COMMAND_MSG_RESP            12
#define COMMAND_HEARDBEAT           13

#define DEFAULT_VERSION             1
#define DEFAULT_MASKBYTE            0


// 推单工作流 推送 s_need_get_order 事件 阻塞模式必须拥有单独线程
class OrderPushWork : public QObject
{
    Q_OBJECT
public:
    explicit OrderPushWork(QObject *parent = 0);

    ~OrderPushWork();

    void setStoreinfo(const QVariantMap &storeinfo);

    //void setServicelist(const QStringList &servicelist);

    QStringList servicelist() const;

    void setServer_index(int server_index);

    void setStopflag();

    virtual bool event(QEvent *e);

    void setToken(const QString &token);

    void setSerurl(const QString &serurl);

    private:
    //链接到服务器
    bool connectTcpServer();
    //身份信息验证
    bool loginTcpServer();
    //和服务端的心跳
    void heartBeatTcpService();
    //循环获取服务器,该函数会修改_server_index获取的服务器信息循环递增
    bool getHost(QString &ip, qint16 &port);
    //检查是否有效的MSG
    bool checkMsgData(const QByteArray &msgdata);

private:
    bool GetServiceList();

public slots:
    //推模式入口
    void workStart();

signals:
    void quit();

    void connected();

    void writeready();

    void readready();

private:
    //门店信息
    QVariantMap _storeinfo;
    //服务器列表
    QStringList _servicelist;
    //当前使用服务器列表下标
    int _server_index;
    //停止标志位置
    qint8 _stopflag;
    //重新登录标识
    qint8 _needlogin;
    //超时标识
    bool _istimeout;
    //长连接套接字
    //QTcpSocket *_socket;
    QSslSocket *_socket;
    //token
    QString _token;
    //token 锁
    QMutex _token_lock;
    //获取服务器列表地址
    QString _serurl;

};

class OrderPushDataProcess
{
public:
    friend class OrderPushWork;
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 void getCheckRequestData(const QJsonObject &json, QByteArray &data)
        {
            Data_Head head = {0};

            QByteArray tmpdata = QString(QJsonDocument(json).toJson(QJsonDocument::Compact)).toUtf8();

            char *buf = (char *)calloc(1, sizeof(Data_Head) + tmpdata.size() + 1);

            DATAHEADINIT(head,1,COMMAND_AUTH_REQ,0,tmpdata.size());

            memcpy(buf, (void *)&head, sizeof(Data_Head));

            memcpy(buf + sizeof(Data_Head), tmpdata.data(), tmpdata.size());

            data = QByteArray(buf, sizeof(Data_Head) + tmpdata.size());

            qDebug() << data.toHex();

            free(buf);
        }

        //获取心跳响应包
        static void getHeartResponseData(QByteArray &data)
        {
            Data_Head_Heart head = {0};

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

            DATAHEADINITHEART(head, 1, COMMAND_HEARDBEAT, 0);

            memcpy(buf, (void *)&head, sizeof(Data_Head_Heart));

            data = QByteArray(buf, sizeof(Data_Head_Heart));

            free(buf);
        }

        //获取消息响应
        static void getMSGResponseData(QByteArray &data, quint32 synSeq)
        {
            Data_Head_NL head = {0};

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

            DATAHEADNLINIT(head, 1, COMMAND_MSG_RESP, 1, 0, synSeq);

            memcpy(buf, (void *)&head, sizeof(Data_Head_NL) + 1);

            data = QByteArray(buf, sizeof(Data_Head_NL));

            free(buf);
        }

        //是否是心跳包
        static bool isHeartBeat(const QByteArray &data)
        {
           if(data.size() < sizeof(Data_Head))
               return false;

           Data_Head head = {0};

           memcpy(&head, data.data(), sizeof(Data_Head));

           return (COMMAND_HEARDBEAT == head.cmdByte &&
                   head.version == DEFAULT_VERSION && head.maskByte == DEFAULT_MASKBYTE &&
                   head.bodyLen > 0);

        }
        //检查头部数据
        static bool checkHeadData(const QByteArray &data)
        {
            if(data.size() < sizeof(Data_Head))
                return false;

            Data_Head head = {0};

            memcpy(&head, data.data(), sizeof(Data_Head));

            return ((head.cmdByte >= COMMAND_UNKNOW && head.cmdByte <= COMMAND_HEARDBEAT) &&
                    head.version == DEFAULT_VERSION && head.maskByte == DEFAULT_MASKBYTE &&
                    head.bodyLen > 0);
        }

        //获取请求长度
        static bool getHeadLen(const QByteArray &data, quint32 &len)
        {
            if(data.size() < sizeof(Data_Head))
                return false;

            Data_Head head = {0};

            memcpy(&head, data.data(), sizeof(Data_Head));

            len = ntohl(head.bodyLen);

            return ((head.cmdByte >= COMMAND_UNKNOW && head.cmdByte <= COMMAND_HEARDBEAT) &&
                    head.version == DEFAULT_VERSION);
        }

        //检查并获取头部
        static bool getHeadCMD(const QByteArray &data, int &cmd)
        {
            if(data.size() < sizeof(Data_Head))
                return false;

            Data_Head head = {0};

            memcpy(&head, data.data(), sizeof(Data_Head));

            cmd = head.cmdByte;

            return ((head.cmdByte >= COMMAND_UNKNOW && head.cmdByte <= COMMAND_HEARDBEAT) &&
                    head.version == DEFAULT_VERSION);
        }

        static bool getLoginRequest(QVariantMap map, QJsonObject &json, QString token)
        {
            if(!map.contains(JSON_STOREID))
                return false;

            json.insert(TCPJSON_KEY_STOREID, map[JSON_STOREID].toString());
            json.insert(TCPJSON_KEY_TOKEN, token);

            return true;
        }
};

#endif // OrderPushWork_H
