/*
 * The routeTcpMessage() receives messages from any TCP connection. The Daemon can be connected with server and deskApp at the same time, but it will know which connection received data.
 * Any message arrive by TCP must to have two parts. The first part defines the data size of the coming package (second part) and must to be an 8 bytes String (always 8 bytes).
 *
 * The algoritm works like this:
 * When some data arrive it verifies (using the hasPackage variable) if is the first part or the second one of the complete message;
 * If hasPackage is true then is especting the second part of the message, otherwise the dada just arrived is the size information.
 *
 * The information of size correspond to the size of the message package. So, when more data arrive it verifies if the size is greater than or equals to the expected size.
 * If true the message is all here and can go on, otherwise the message is coming and it will wait for more data.
 *
 * When all the message arrives it will interpret and route it (by message type) to the right way.
 *
 */
void RFIDMonitorDaemon::routeTcpMessage()
{
    QTcpSocket *connection = (QTcpSocket *) QObject::sender();

    static bool hasPackage = false;
    static quint64 packageSize = 0;

    if( ! hasPackage){

        if((quint64)connection->bytesAvailable() < sizeof(quint64))
            return;

        //    	m_tcpSocket->read((char *)&packageSize, sizeof(quint64));
        QString packageSizeStr(connection->read(sizeof(quint64)));
        packageSize = packageSizeStr.toULongLong();

        qDebug() <<  QString("Message = %1 - Size of coming package: %2").arg(packageSizeStr).arg(QString::number(packageSize));
        hasPackage = true;
    }

    if((quint64)connection->bytesAvailable() >=  packageSize){
        QByteArray data(connection->read(packageSize));

        json::NodeJSMessage nodeMessage;

        nodeMessage.read(QJsonDocument::fromJson(data).object());
        QString messageType(nodeMessage.type());

        qDebug() << QString("New Message Received: %1").arg(QString(data));


        if(messageType == "SYN-ALIVE"){

            tcpSendMessage(connection, buildMessage(m_configManager->identification(), "ACK-ALIVE").toJson());
            qDebug() << QString("New Message Received: %1").arg(messageType);
        }
        else if (messageType == "ACK-SYN") {

            QJsonObject obj(nodeMessage.jsonData());
            if(!obj.isEmpty()){
                m_configManager->setIdentification(obj);
            }

            bool statusDateTime = m_configManager->setDateTime(nodeMessage.dateTime());
            QJsonObject response = m_configManager->identification();
            response["success"] = QJsonValue(statusDateTime);

            tcpSendMessage(connection, buildMessage(response, "ACK").toJson());

            // Informe RFIDMonitor that server is now connected. Wait 2 seconds;
            if(connection->objectName() == "server"){
                isConnected = true;
                QTimer *timer = new QTimer();
                timer->setSingleShot(true);
                timer->setInterval(2000);
                connect(timer, &QTimer::timeout, [=](){
                    ipcSendMessage(buildMessage(QJsonObject(), "SYNC").toJson());
                    timer->deleteLater();
                });
                timer->start();
            }
        }else if (messageType == "GET-CONFIG") {
            tcpSendMessage(connection, buildMessage(m_configManager->currentConfig(), "CONFIG").toJson());

        }else if (messageType == "READER-COMMAND") {

            QJsonObject command(nodeMessage.jsonData());
            /*
             * When a 'reader command' message is received is because someone is sending a command to the reader. So it needs to send also who is doing this.
             * To perform this it uses a field called 'sender' that carry the name of who is sending the 'command message'.
             * And based on that, it will respond to the server connection or to the deskApp connection
             *
             * see reoutIcpMessage (messageType == "READER-RESPONSE")
             */
            if(connection->objectName() == "server")
                command.insert("sender", QString("server"));
            else
                command.insert("sender", QString("app"));

            ipcSendMessage(buildMessage(command, "READER-COMMAND").toJson());

        }else if (messageType == "NEW-CONFIG") {

            QJsonObject newConfig(nodeMessage.jsonData());
            bool ackConf;
            if(m_configManager->newConfig(newConfig))
            {
                // Send a message to stop the RFIDMonitor
                emit restartMonitor();
                ackConf = true;
            }else{
                ackConf = false;
            }
            QJsonObject dataObj;
            dataObj.insert("success", QJsonValue(ackConf));
            tcpSendMessage(connection, buildMessage(dataObj, "ACK-NEW-CONFIG").toJson());

            if(m_tcpAppSocket->isOpen())
                m_tcpAppSocket->close();

        }else if (messageType == "DATETIME") {

            QJsonObject dataObj;
            dataObj["success"] =  QJsonValue(m_configManager->setDateTime(nodeMessage.dateTime()));
            tcpSendMessage(connection, buildMessage(dataObj, "ACK").toJson());

        }else if (messageType == "ACK-DATA") {
            // A ACK-DATA message means that the server is trying to inform the RFIDMonitor that some data is now synced. So, it just send this message to the RFIDMonitor.
            ipcSendMessage(data);

        }else if (messageType == "GET-NET-CONFIG") {
            // Only return the network configuration.
            tcpSendMessage(connection, buildMessage(m_configManager->netConfig(), "NET-CONFIG").toJson());

        }else if (messageType == "NEW-NET") {

            QJsonObject network = nodeMessage.jsonData();
            // Receive a new configuration for the network (ssid and password).
            QJsonObject dataObj;
            dataObj.insert("success", QJsonValue(m_configManager->setNetConfig(network)));

            // returns a message ACK-NET to inform the sender that the new configuration was set
            tcpSendMessage(connection, buildMessage(dataObj, "ACK-NET").toJson());

            // Try to restar the network service to already use the new configuration
            bool resetNet = m_configManager->restartNetwork();
            qDebug() <<  QString(resetNet? "Network restarted" : "Networkt Don't restarted");

        }else if (messageType == "ACK-UNKNOWN") {
            QJsonDocument unknown(nodeMessage.jsonData());
            QJsonObject oldMessage(unknown.object().value("unknownmessage").toObject());
            qDebug() <<  "The server don't understand the message type: " << oldMessage.value("type").toString();
            qDebug() <<  "ERROR message: " << unknown.object().value("errorinfo").toString();

        }else if (messageType == "FULL-READ"){
            ipcSendMessage(data);

        }else{
            /* When receives a message that can't be interpreted like any type is an unknown message.
             * In this case an ACK-UNKNOWN message is built and sent to the connection that received this message
             */
            qDebug() <<  "UNKNOWN MESSAGE";
            QJsonObject unknownObj;
            unknownObj.insert("unknownmessage", QJsonValue(QJsonDocument::fromJson(data).object()));
            unknownObj.insert("errorinfo", QString("Unknown message received"));
            tcpSendMessage(connection, buildMessage(unknownObj, "ACK-UNKNOWN").toJson());
        }

        /* when all the process is done, reset the expecting message size to zero and the haspackage to false.
         * Then when more data arrive it must to be the size information again.
         */
        packageSize = 0;
        hasPackage = false;
    }
}