#include "orderpushwork.h"
#include <QEventLoop>
#include <QStringList>
#include <QSslConfiguration>
#include <QTimer>
#include <QJsonObject>
#include <QJsonArray>

#include "preDefine.h"
#include "event/posevent.h"
#include "event/fmapplication.h"

#include "base/Network/billSocket.h"

#include "QsLog.h"

OrderPushWork::OrderPushWork(WorkObject *parent) : WorkObject(parent)
{
    _socket = NULL;
    //_stopflag = 1;
    _needlogin = 1;
    _server_index = -1;
    _token = QString("");
    _serurl.clear();
    _istimeout = true;
    _networkstatic_index = 0;

    FMApplication::subscibeEvent(this, PosEvent::s_token_change);
    FMApplication::subscibeEvent(this, PosEvent::s_login_storeinfo);
}

OrderPushWork::~OrderPushWork()
{
    qDebug() << "";
    while(!_stoped)
    {
        _stopflag = true;
        emit quit();
        EVENTWAIT(10);
    }
}

void OrderPushWork::setStoreinfo(const QVariantMap &storeinfo)
{
    _storeinfo = storeinfo;
}

QStringList OrderPushWork::servicelist() const
{
    return _servicelist;
}

void OrderPushWork::setServer_index(int server_index)
{
    _server_index = server_index;
}

bool OrderPushWork::event(QEvent *e)
{
    if(e->type() == PosEvent::s_token_change)
    {
        QString token;

        _token_lock.lock();
        GETEVENTINFO(token, e, QString);
        _token_lock.unlock();

        QLOG_DEBUG() << "$$$:" << token;

        _token = token;
        _needlogin = 1;

        emit quit();

        return true;
    }

    if(e->type() == PosEvent::s_login_storeinfo)
    {
        QVariantMap map;

        GETEVENTINFO(map, e, QVariantMap);

        _storeinfo = map;

        return true;
    }


    return QObject::event(e);
}

bool OrderPushWork::connectTcpServer()
{
    QString ip;
    qint16 port;

    if(!getHost(ip, port))
    {
        QLOG_ERROR() << "OrderPushWork::getHost failed";
        return false;
    }

    QLOG_INFO() << "ip:" << ip << "; port:" << port;

    _socket = new QSslSocket();

    connect(_socket, &QSslSocket::connected, [this] () {
        _istimeout = false;
        emit connected();
    });

    connect(_socket, static_cast<void (QSslSocket::*)(QAbstractSocket::SocketError)>(&QSslSocket::error), this, &OrderPushWork::quit);

    connect(_socket, &QSslSocket::readyRead, [this] () {
        _istimeout = false;
        emit readready();
    });

    connect(_socket, &QSslSocket::bytesWritten, [this] (quint64 writebyte) {
        _istimeout = false;
        emit writeready();
    });

    _socket->addCaCertificates(qApp->applicationDirPath() + "/" + "microwstest.sandload.cn.pem");

    QLOG_WARN() << _socket->errorString();

    _socket->ignoreSslErrors();

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

    _socket->setSslConfiguration(config);

    QLOG_WARN() << _socket->errorString();

    _istimeout = true;

    _socket->connectToHostEncrypted(ip, port);

    //bool flag = _socket->waitForConnected(10*1000);
    {
        QEventLoop loop;
        QTimer timer;

        connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &OrderPushWork::connected, &loop, &QEventLoop::quit);

        timer.start(10*1000);

        loop.exec();
    }

    QLOG_WARN() << _socket->errorString() << _istimeout;

    return !_istimeout;
}

void OrderPushWork::networkouttime(bool networkstatus)
{

    if(networkstatus)
    {
        _networkstatic_index=0;
        QVariantMap map;
        map.insert(EVENT_KEY_NETWORKSTATUS, networkstatus);
        POSTEVENTTYPE(PosEvent::s_network_outtime,map,QVariantMap);
    }else if(!networkstatus && _networkstatic_index == 0){
        _networkstatic_index++;
        QVariantMap map;
        map.insert(EVENT_KEY_NETWORKSTATUS, networkstatus);
        POSTEVENTTYPE(PosEvent::s_network_outtime,map,QVariantMap);
        QLOG_INFO() << "static:  s_network_outtime ...";
    }

}

bool OrderPushWork::loginTcpServer()
{
    QByteArray requestdata;

    QJsonObject json;

    OrderPushDataProcess::getLoginRequest(_storeinfo, json, _token);

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

    OrderPushDataProcess::getCheckRequestData(json, requestdata);

    QLOG_INFO() << "tcp login request : " << requestdata.toHex();

    _istimeout = true;

    _socket->write(requestdata);

    {
        QEventLoop loop;
        QTimer timer;

        connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &OrderPushWork::writeready, &loop, &QEventLoop::quit);

        timer.start(10*1000);

        loop.exec();
    }

    //if(!_socket->waitForBytesWritten(10*1000))
    if(_istimeout)
    {
        QLOG_ERROR() << "send msg to service failed" << _socket->errorString();
        return false;
    }

    _istimeout = true;

    {
        QEventLoop loop;
        QTimer timer;

        connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
        connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
        connect(this, &OrderPushWork::readready, &loop, &QEventLoop::quit);

        timer.start(30*1000);

        loop.exec();
    }

    //if(!_socket->waitForReadyRead(30*1000))
    if(_istimeout)
    {
        QLOG_ERROR() << "receive msg from service failed" << _socket->errorString();
        return false;
    }

    QByteArray head = _socket->read(sizeof(Data_Head));

    QLOG_DEBUG() << head.toHex();

    quint32 len = 0;
    int cmd = -1;

    if(!OrderPushDataProcess::getHeadLen(head, len) ||
            !OrderPushDataProcess::getHeadCMD(head, cmd) || cmd != COMMAND_AUTH_RESP)
        return false;

    QByteArray body = _socket->read(len);

    QJsonDocument jsonDocument = QJsonDocument::fromJson(body);

    if(jsonDocument.isNull())
      return false;

    QJsonObject rspjson = jsonDocument.object();

    QLOG_INFO() << "login receive json : " << rspjson;

    if(rspjson.contains(TCP_JSON_KEY_STATUS) && rspjson[TCP_JSON_KEY_STATUS].toString().toInt() == 1)
        return true;

    return false;
}

void OrderPushWork::heartBeatTcpService()
{
    while(!_stopflag && !_needlogin)
    {
        QLOG_INFO() << "wait recv json ...";

        _istimeout = true;

        {
            QEventLoop loop;
            QTimer timer;

            connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
            connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
            connect(this, &OrderPushWork::readready, &loop, &QEventLoop::quit);

            timer.start(75*1000);

            loop.exec();
        }

        //if(!_socket->waitForReadyRead(4*60*1000))
        if(_istimeout)
            break ;

        quint32 tmplenandsynSeq = 0;

        QByteArray head = _socket->read(sizeof(Data_Head));
        int cmd = -1;

        QLOG_INFO() << "response head : " << head.toHex();

        if( !OrderPushDataProcess::getHeadLen(head, tmplenandsynSeq)||
            !OrderPushDataProcess::getHeadCMD(head, cmd))
        {
            //清空内部缓冲器重新读
            _socket->readAll();
            continue;
        }

        QLOG_DEBUG() << "tmplenandsynSeq : " << tmplenandsynSeq;

        QLOG_DEBUG() << "cmd : " << cmd;

        if(cmd == COMMAND_HEARDBEAT)
        {
            QLOG_INFO() << "receive heard beat message";

            _socket->read(tmplenandsynSeq);

            QByteArray  data;

            OrderPushDataProcess::getHeartResponseData(data);

            QLOG_INFO() << "heard beat response data : " << data.toHex();

            int len = _socket->write(data);

            QLOG_INFO() << "write len : " << len;

            //bool fl = _socket->waitForBytesWritten();

            _istimeout = true;

            {
                QEventLoop loop;
                QTimer timer;

                connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &OrderPushWork::writeready, &loop, &QEventLoop::quit);

                timer.start(30*1000);

                loop.exec();
            }

            QLOG_INFO() << "waitForBytesWritten : " << _istimeout;

            continue;
        }
        else if(cmd == COMMAND_MSG_REQ)
        {

            quint32 reallen = 0;

            QByteArray tmplen = _socket->read(4);

            OrderPushDataProcess::getRealLen(tmplen, reallen);

            QByteArray msgdata = _socket->read(reallen);

            checkMsgData(msgdata);

            QByteArray data;

            OrderPushDataProcess::getMSGResponseData(data, tmplenandsynSeq);

            QLOG_INFO() << "tcp response data : " << data.toHex();

            _socket->write(data);

            //_socket->waitForBytesWritten();

            _istimeout = true;

            {
                QEventLoop loop;
                QTimer timer;

                connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);
                connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
                connect(this, &OrderPushWork::writeready, &loop, &QEventLoop::quit);

                timer.start(30*1000);

                loop.exec();
            }

            continue;

        }
        QLOG_ERROR() << "not support cmdtype";
        _socket->readAll();
        break;
    }
}

bool OrderPushWork::checkMsgData(const QByteArray &msgdata)
{

    QLOG_INFO() << "recv order info : " << msgdata;
    //接口未能完善
    //推送新订单事件
    DEFAULTPOSTEVENT(PosEvent::s_need_get_order, QString::fromUtf8(msgdata));
    return false;
}

bool OrderPushWork::GetServiceList()
{
    if(_serurl.isEmpty())
    {
        QLOG_ERROR() << "_serurl is empty";
        return false;
    }

    QLOG_DEBUG() << "_serurl : " << _serurl;

    QJsonObject json;

    json.insert(JSON_KEY_PUSH_PARTNERID, _storeinfo[JSON_KEY_PARTNERID].toString());
    json.insert(JSON_KEY_PUSH_STOREID, _storeinfo[JSON_STOREID].toString());
    json.insert(JSON_KEY_PUSH_PROTOCPL, QString("0"));

    QLOG_INFO() << "get service list request json : " << json;

    QString error;
    QJsonObject recvjson;

    if(!BillSocket::S_Request(json, recvjson, _serurl, error, QByteArray("application/json")))
    {
        error = QString::fromLocal8Bit("net work error : ").append(error);
        QLOG_ERROR() << "get service list failed : " << error;
        return false;
    }

    QLOG_INFO() << "recv json : " << recvjson;

    if(recvjson.contains(JSON_KEY_CODE) && recvjson[JSON_KEY_CODE].toString().compare("ok", Qt::CaseInsensitive) == 0 &&
            recvjson.contains(JSON_KEY_LISTDATA) && recvjson[JSON_KEY_LISTDATA].isArray())
    {
        QJsonArray array = recvjson[JSON_KEY_LISTDATA].toArray();
        for(int i = 0; i < array.size(); ++ i)
        {
            QJsonObject tmp = array[i].toObject();

            qDebug() << tmp;

            if(tmp.contains(JSON_KEY_PUSH_PORT) &&
               tmp.contains(JSON_KEY_PUSH_HOST) &&
               !tmp[JSON_KEY_PUSH_HOST].toString().isEmpty())
            {
                QString tmpsock  = tmp[JSON_KEY_PUSH_HOST].toString();
                tmpsock = tmpsock.append(":").append(QString::number(tmp[JSON_KEY_PUSH_PORT].toInt()));
                _servicelist.append(tmpsock);
            }
        }
    }

    if(_servicelist.isEmpty())
        return false;
    return true;
}

bool OrderPushWork::getHost(QString &ip, qint16 &port)
{
    if(_servicelist.size() == 0)
        return false;

    (++ _server_index) %= _servicelist.size();

    QStringList list = _servicelist[_server_index].split(":");

    if(list.size() == 2)
    {
        ip = list[0];
        port = list[1].toInt();
        return true;
    }

    return false;
}

void OrderPushWork::workstart()
{
    QLOG_INFO() << "OrderPushWork::workStart start : " << QThread::currentThreadId();

    _token_lock.lock();
    if(_token.isEmpty())
    {
        _token_lock.unlock();

        QEventLoop loop;

        connect(this, &OrderPushWork::quit, &loop, &QEventLoop::quit);

        loop.exec();
    }
    else
        _token_lock.unlock();

    QLOG_INFO() << "get token success : " << _token;

    while(!_stopflag && !GetServiceList())
    {
        QLOG_WARN() << "GetServiceList failed timeout 10 sec";

        QEventLoop loop;
        connect(this, &OrderPushWork::quit, &loop,&QEventLoop::quit);
        QTimer::singleShot(10000, &loop, SLOT(quit()));
        loop.exec();
    }

    QLOG_INFO() << "GetServiceList success : " << _servicelist << _stopflag;

    while(!_stopflag)
    {
        do
        {
            if(!connectTcpServer())
            {
                networkouttime(false);
                QLOG_ERROR() << "connectTcpServer failed";
                break;
            }
            else
            {
                networkouttime(true);
            }

            if(!loginTcpServer())
            {
                QLOG_ERROR() << "loginTcpServer failed";
                break ;
            }

            _needlogin = 0;

            heartBeatTcpService();
            networkouttime(false);
        }while(0);

        //网络异常断开重连
        CLOSESOCKES(_socket);

        if(_stopflag)
            break;

        EVENTWAIT(60000);
    }

    _stoped = true;
}

void OrderPushWork::setSerurl(const QString &serurl)
{
    _serurl = serurl;
}

void OrderPushWork::setToken(const QString &token)
{
    _token = token;
}
