#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "string.h"
#include "ccittcrc.h"
#include "qdebug.h"
#include <QThread>
#include <QFileDialog>
#include <QDateTime>
#include <QMessageBox>
#include <QtNetwork>
#include <hexvalidator.h>
#include <QListWidgetItem>
#include <newfavcmdwindow.h>
#include <QSettings>
#include <QLoggingCategory>

#include "xlsxdocument.h"
#include "xlsxworkbook.h"
#include "xlsxworksheet.h"
using namespace QXlsx;

//windeployqt --dir deploy C1-client.exe
#define DISCOVERY_PORT 63311


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

//    QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));

    tcpClientConnected = false;
    clientSocket = NULL;

    QString title = QString("C1-Client - v%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
    this->setWindowTitle(title);
    statusLabel = new QLabel(this);
    hardwareLabel = new QLabel(this);
    progressBar = new QProgressBar(this);

    statusLabel->setGeometry(0,0,160,20);
    hardwareLabel->setGeometry(0,0,160,20);
    progressBar->setGeometry(0,0,160,20);
    progressBar->setTextVisible(false);

    // add the two controls to the status bar
    ui->statusbar->addPermanentWidget(statusLabel,1);
    ui->statusbar->addPermanentWidget(hardwareLabel,1);
    ui->statusbar->addPermanentWidget(progressBar,1);
    progressBar->setValue(0);

    ui->gbCommands->setEnabled(false);
    ui->tabs->setEnabled(false);

    connect(&serialPort, &QSerialPort::readyRead, this, &MainWindow::handleSerialDataReady);
    connect(&serialPort, static_cast<void (QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error), this, &MainWindow::handleSerialDataError);
    connect(&binaryProtocol, &BinaryProtocol::frameError, this, &MainWindow::binaryProtocolFrameError);
    connect(&binaryProtocol, &BinaryProtocol::frameTimeout, this, &MainWindow::binaryProtocolFrameTimeout);
    connect(&binaryProtocol, &BinaryProtocol::frameComplete, this, &MainWindow::binaryProtocolFrameComplete);
    connect(&binaryProtocol, &BinaryProtocol::protocolWrite, this, &MainWindow::binaryProtocolWrite);

    connect(ui->ckbFilterGeneric, &QCheckBox::stateChanged, this, &MainWindow::loadCommands);
    connect(ui->ckbFilterMifare, &QCheckBox::stateChanged, this, &MainWindow::loadCommands);
    connect(ui->ckbFilterMifareUl, &QCheckBox::stateChanged, this, &MainWindow::loadCommands);
    connect(ui->ckbFilterMifareDesfire, &QCheckBox::stateChanged, this, &MainWindow::loadCommands);
    connect(ui->ckbFilterICode, &QCheckBox::stateChanged, this, &MainWindow::loadCommands);

    //IP validator stuff
    QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])";
    QRegExp ipRegex ("^" + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange + "$");

    QRegExpValidator *ipValidator = new QRegExpValidator(ipRegex, this);
    ui->leIp->setValidator(ipValidator);
    ui->leNetmask->setValidator(ipValidator);
    ui->leGateway->setValidator(ipValidator);
    ui->leDns->setValidator(ipValidator);

    serialPortRefreshList();

    oneShotTimer = new QTimer(this);
    connect(oneShotTimer, SIGNAL(timeout()), this, SLOT(oneShotTimeout()));
    oneShotTimer->setSingleShot(true);

    pollingTimer = new QTimer(this);
    connect(pollingTimer, SIGNAL(timeout()), this, SLOT(pollingTimeout()));
    pollingTimer->setSingleShot(true);

    config = new Config();

    //load jsonFile
    QFile jsonFile(":/json/commands.json");
    QJsonParseError parseError;

    if (jsonFile.open(QIODevice::ReadOnly) == false) {
        qDebug() << "Resource file not attached ?";
    }

    QTextStream stream(&jsonFile);
    QString content = stream.readAll();
    jsonFile.close();

    QJsonDocument jsonDoc = QJsonDocument::fromJson(content.toUtf8(), &parseError);
    QJsonObject jsonObject = jsonDoc.object();

    jsonCommandsArray = jsonObject["commands"].toArray();

    //udp client start
    udpSocket = new QUdpSocket(this);

    if(udpSocket->bind(DISCOVERY_PORT))
        qDebug() << "Bind on port" << udpSocket->localPort();
    else
        qDebug() << "Bind failed";

    connect(udpSocket,SIGNAL(readyRead()),this,SLOT(udpReceive()));

    loadCommands(0);
    reloadFavs();

    QSettings settings("Eccel_Ltd", "C1-client");
    ui->cbSaveToFile->setChecked(settings.value("OutputXlsEnabled").toBool());
    ui->leOutputXlsFile->setText(settings.value("OutputXlsFile").toString());
    retry_count = 0;
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::udpReceive()
{
    QByteArray buffer;
    QString address;
    QStringList info;

    buffer.resize(udpSocket->pendingDatagramSize());

    QHostAddress sender;
    quint16 senderPort;

    udpSocket->readDatagram(buffer.data(), buffer.size(),
                         &sender, &senderPort);

    QString answer = QString::fromStdString(buffer.toStdString());
    if (answer.contains("P_C1:") && !answer.contains("SCAN"))
    {
        //::ffff:172.16.16.153
        address = sender.toString();
        info = answer.split(":");
        if (address.contains(":"))
        {
            address = address.right(address.length() - address.lastIndexOf(":") - 1);
        }

        logd("Device " + info.at(1) + " found at address " + address + ". Version: " + info.at(2));
        qApp->processEvents();
        ui->cbNetworkPort->addItem(info.at(1) + " - " + address, address);
    }

//    QVariant *userdata = new QVariant(Qt::UserRole, address);
}

//return always two numbers eg. 09
QString MainWindow::byte2hexstring(uint8_t value)
{
    QString hexValue;

    if (value < 16)
    {
        hexValue = QString( "0%1" ).arg( value, 1, 16 ).toUpper();
    }
    else
    {
        hexValue = QString( "%1" ).arg( value, 1, 16 ).toUpper();
    }

    return hexValue.toUpper();
}

void MainWindow::loadCommands(int state)
{
    Q_UNUSED(state);

    ui->lvCommands->clear();

    for (int k = 0; k < MAX_ARGS; k++) {
        argsEdit[k] = NULL;
        argsLabel[k] = NULL;
    }

    foreach (const QJsonValue & command, jsonCommandsArray) {
        QJsonArray args;
        QJsonObject obj = command.toObject();
        int value = obj["value"].toInt();
        QString hexValue;
        bool cmdVisible = false;

        if (ui->ckbFilterGeneric->isChecked() && value > CMD_ACK && value < CMD_MF_READ_BLOCK) cmdVisible = true;
        if (ui->ckbFilterMifare->isChecked() && value >= CMD_MF_READ_BLOCK && value < CMD_MFU_READ_PAGE) cmdVisible = true;
        if (ui->ckbFilterMifareUl->isChecked() && value >= CMD_MFU_READ_PAGE && value < CMD_MFDF_GET_VERSION) cmdVisible = true;
        if (ui->ckbFilterMifareDesfire->isChecked() && value >= CMD_MFDF_GET_VERSION && value < CMD_ICODE_INVENTORY_START) cmdVisible = true;
        if (ui->ckbFilterICode->isChecked() && value >= CMD_ICODE_INVENTORY_START && value <= CMD_ICODE_PICK_RANDOM_ID) cmdVisible = true;

        if (value >= 0xf0) {
            cmdVisible = true;
        }

        if (!cmdVisible)
            continue;

        hexValue = QString( "[%1] " ).arg(byte2hexstring(value));

        if (obj["visible"].toBool() != false || obj["visible"].isNull()) {
            QListWidgetItem* item = new QListWidgetItem(hexValue + obj["name"].toString());
            item->setData(Qt::UserRole, obj);
            ui->lvCommands->addItem(item);
        }
    }
}

void MainWindow::reloadFavs()
{
    config->reload();
    ui->lvFavCommands->clear();

    foreach (const Command & cmd, config->commands) {
            QListWidgetItem* item = new QListWidgetItem(cmd.name);
            item->setData(Qt::UserRole, cmd.hex);
            ui->lvFavCommands->addItem(item);
    }
}

QString MainWindow::getCommandName(int cmdValue)
{
    foreach (const QJsonValue & command, jsonCommandsArray) {
        QJsonObject obj = command.toObject();

        if (obj["value"].toInt() == cmdValue)
            return obj["name"].toString();
    }

    return "Unknown";
}

void MainWindow::logd(QString msg)
{
    QString timeHtml = "<font color=\"Black\">";
    QString endFontHtml = "</font>";
    QString endLineHtml = "<br>";
    QString text = timeHtml + QDateTime::currentDateTime().toString("hh:mm:ss: ") + msg + endFontHtml + endLineHtml;

    QTextCursor cursor = ui->teProtocol->textCursor();
    ui->teProtocol->insertHtml(text);
    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);
}

void MainWindow::protoLog(QByteArray msg, QString prefix)
{
    QString timeHtml = "<font color=\"Black\">";
    QString stxHtml = "<font color=\"Navy\">";
    QString lenHtml = "<font color=\"DarkGreen\">";
    QString cmdHtml = "<font color=\"Red\">";
    QString dataHtml = "<font color=\"DimGray\">";
    QString crcHtml = "<font color=\"Lime\">";
    QString addressHtml = "<font color=\"Blue\">";
    QString endFontHtml = "</font>";
    QString endLineHtml = "<br>";
    QString cmdName;
    uint8_t cmd;
    uint8_t rs485offset = 0;

    if (ui->gbRS485->isChecked())
        rs485offset = 1;

    cmd = (uint8_t)msg.at(5+rs485offset);
    cmdName = getCommandName(cmd);

    if (oneShotTimerState == TIMER_FIRMWARE_LOADING || ui->btStopPolling->isEnabled())
    { //too much frames per second
        return;
    }

    if (cmd == 0) {
        cmdName = getCommandName((uint8_t)msg.at(6+rs485offset)) + " - ACK";
    }
    if (cmd == 0xFE) {
        cmdName = getCommandName((uint8_t)msg.at(6+rs485offset)) + " - ASYNC";
    }
    if (cmd == 0xFF) {
        QString error_num;
        uint16_t error_val;

        error_val = ((uint8_t)msg.at(7+rs485offset)) << 8 | (uint8_t)msg.at(8+rs485offset);
        error_num =  QString( "0x%1" ).arg(error_val, 1, 16 );
        cmdName = getCommandName((uint8_t)msg.at(6+rs485offset)) + " - Error no. " + error_num;
    }

    QString text = timeHtml + QDateTime::currentDateTime().toString("hh:mm:ss: ") + prefix + " - " + cmdName + " " + endFontHtml + endLineHtml; //.zzz

    text += "&nbsp;&nbsp;&nbsp;&nbsp;Data:" + stxHtml;

    for (int k=0; k < msg.length(); k++)
    {
        uint8_t j = msg.at(k);
        QString aByte;

        aByte = QString( " %1" ).arg(byte2hexstring(j));

        text += aByte;

        if (rs485offset && k == 4) {
            text += endFontHtml + addressHtml;
        }else if (k == 0) {
            text += endFontHtml + lenHtml;
        }
        else if (k == 4+rs485offset) {
            text += endFontHtml + cmdHtml;
        }
        else if (k == msg.length() - 3) {
            text += endFontHtml + crcHtml;
        }
        else if (k == 5+rs485offset) {
            text += endFontHtml + dataHtml;
        }
    }

    QTextCursor cursor = ui->teProtocol->textCursor();

    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);

    text = text + endFontHtml + endLineHtml;
    ui->teProtocol->insertHtml(text);
    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);
}

void MainWindow::protoLogRaw(QByteArray msg, QString prefix)
{
    QString timeHtml = "<font color=\"Black\">";
    QString endFontHtml = "</font>";
    QString endLineHtml = "<br>";

    QString text = timeHtml + QDateTime::currentDateTime().toString("hh:mm:ss: ") + prefix + endFontHtml + endLineHtml; //.zzz

    text += "&nbsp;&nbsp;&nbsp;&nbsp;Data:";

    for (int k=0; k < msg.length(); k++)
    {
        uint8_t j = msg.at(k);
        QString aByte;

        aByte = QString( " %1" ).arg(byte2hexstring(j));

        text += aByte;
    }

    QTextCursor cursor = ui->teProtocol->textCursor();

    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);

    text = text + endFontHtml + endLineHtml;
    ui->teProtocol->insertHtml(text);
    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);
}

void MainWindow::on_btShowLegend_clicked()
{
    QString timeHtml = "<font color=\"Black\">";
    QString stxHtml = "<font color=\"Navy\">";
    QString lenHtml = "<font color=\"DarkGreen\">";
    QString cmdHtml = "<font color=\"Red\">";
    QString dataHtml = "<font color=\"DimGray\">";
    QString crcHtml = "<font color=\"Lime\">";
    QString addressHtml = "<font color=\"Blue\">";
    QString endFontHtml = "</font>";
    QString endLineHtml = "<br>";


    QTextCursor cursor = ui->teProtocol->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);

    QString text = endLineHtml + stxHtml + "STX&nbsp;&nbsp;&nbsp;" + endFontHtml + " - Frame start" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    text = lenHtml + "Length" + endFontHtml + " - Frame length + XOR length (Addr + Cmd + Params)" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    text = addressHtml + "Addr&nbsp;&nbsp;" + endFontHtml + " - Address, optional for RS485" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    text = cmdHtml + "Cmd&nbsp;&nbsp;&nbsp;" + endFontHtml + " - Command number" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    text = dataHtml + "Params" + endFontHtml + " - Optional parameters" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    text = crcHtml + "CRC&nbsp;&nbsp;&nbsp;" + endFontHtml + " - Addr + Command + Data checksum" + endLineHtml;
    ui->teProtocol->insertHtml(text);

    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);
}

void MainWindow::serialPortClose(void)
{
    if (serialPort.isOpen())
    {
        serialPort.clear();
        serialPort.close();
    }

    ui->btSerialConnect->setText("Connect");
    ui->cbSerialPort->setEnabled(true);
    ui->tabNetworkConnection->setEnabled(true);
    ui->gbCommands->setEnabled(false);
    ui->tabs->setEnabled(false);
    qDebug() << QObject::tr("Port closed");
    statusLabel->setText("");
    hardwareLabel->setText("");
}

void MainWindow::serialPortRefreshList(void)
{
    const QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
    QString portName, msg;

    serialPortClose();

    ui->cbSerialPort->clear();

    for (const QSerialPortInfo &info : infos) {
        qDebug() << QObject::tr("Port found: ") + info.systemLocation();
        portName = info.systemLocation();
        ui->cbSerialPort->addItem(portName);
    }

    if (ui->cbSerialPort->count() > 0)
    {
        ui->cbSerialPort->setCurrentIndex(0);
    }
}

void MainWindow::serialPortOpen(void)
{
    const QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
    QString portName = ui->cbSerialPort->currentText(), msg;
    retry_count = 0;
    if (!portName.isEmpty())
    {
        serialPort.setPortName(portName);
        serialPort.setBaudRate(ui->cbConnectionSpeed->currentText().toInt());
        if (!serialPort.open(QIODevice::ReadWrite))
        {
            msg = QObject::tr("Failed to open port %1, error: %2").arg(portName).arg(serialPort.errorString());
            qDebug() << msg;
        }
        else
        {
            serialPort.setDataTerminalReady(false);
            qDebug() << QObject::tr("Port opened: %1").arg(portName) << endl;

            if (oneShotTimerState != TIMER_WAIT4SEARCH_DEVICES)
            {
                ui->btSerialConnect->setText("Disconnect");
                ui->cbSerialPort->setEnabled(false);
                ui->gbCommands->setEnabled(true);
                ui->tabNetworkConnection->setEnabled(false);
                ui->tabs->setEnabled(true);
            }
        }
    }
}

void MainWindow::handleSerialDataReady()
{
    QByteArray bytesReceived = serialPort.readAll();
    lastReceivedFrame.append(bytesReceived);
    binaryProtocol.parse((uint8_t *)bytesReceived.data(), bytesReceived.count());
}

void MainWindow::handleSerialDataError(QSerialPort::SerialPortError serialPortError)
{
    if (serialPortError == QSerialPort::ReadError) {
        qDebug() << QObject::tr("An I/O error occurred while reading the data from port %1, error: %2").arg(serialPort.portName()).arg(serialPort.errorString()) << endl;
        serialPort.close();
    }
}

void MainWindow::firmwareFlashStart(void)
{
    logd("Flash erased.");
    QThread::msleep(4000);
    logd("Writing firmware...");
    oneShotTimer->stop();

    firmwarePos = 0;

    QFile file(firmwareFileName);

    if (!file.open(QIODevice::ReadOnly))
    {
        logd("Unable to open firmware file!");
        oneShotTimer->stop();
        return;
    }

    firmwareBuff =  file.readAll();
    file.close();

    progressBar->setMaximum(firmwareBuff.size());
    progressBar->setValue(0);

    firmwareFlashFrame();
}

void MainWindow::firmwareFlashFrame(void)
{
    uint8_t output_buff[280];
    int data2write = FIRMWARE_PART_SIZE;

    oneShotTimer->stop();

    if (oneShotTimerState == TIMER_STATE_IDLE) {
        return;
    }

    if (firmwarePos == firmwareBuff.size())
    {
        firmwareFlashEnd();
        return;
    }

    if (firmwarePos + FIRMWARE_PART_SIZE > firmwareBuff.size())
    {
        data2write = firmwareBuff.size() % FIRMWARE_PART_SIZE;
    }

    output_buff[0] = CMD_OTA_FRAME;
    memcpy(&output_buff[1], firmwareBuff.data() + firmwarePos, data2write);
    firmwarePos += data2write;
    binaryProtocol.send(output_buff, data2write+1);

    progressBar->setValue(firmwarePos);

    oneShotTimer->start(3000);
}

void MainWindow::firmwareFlashEnd(void)
{
    uint8_t cmd[5];
    cmd[0] = CMD_OTA_FINISH;
    binaryProtocol.send(cmd, 1);
    logd("Firmware flashed.");
    oneShotTimerState = TIMER_STATE_IDLE;
    ui->btFlashFirmware->setText("Flash firmware");
    oneShotTimer->stop();
    progressBar->setValue(0);
}

void MainWindow::binaryProtocolFrameComplete(uint8_t *buff, size_t len)
{
    uint8_t cmd[255];
    buff[len] = 0;
    int cmdLen = 0;

    protoLog(lastReceivedFrame, "[RX]");
    lastReceivedFrame.clear();

    if (buff[0] ==  CMD_ACK)
    {
        retry_count = 0;
        switch (buff[1])
        {
        case CMD_GET_VERSION:
        {
            char *version = (char*)&buff[2];
            QString msg = version;

            if (oneShotTimerState == TIMER_WAIT4SEARCH_DEVICES)
            {
                QString itemText;

                itemText = QString( "%1 - %2 " ).arg(byte2hexstring(searchAddress), version);
                ui->cbDetectedDevices->addItem(itemText);
                logd("Device found: " + itemText);
                scanNext();
            }
            else
            {
                hardwareLabel->setText("Version:" + msg);
                oneShotTimer->stop();
            }
            break;
        }
        case CMD_GET_TAG_COUNT:
            if (buff[2] > 0)
            {
                cmd[0] = CMD_GET_UID;
                cmd[1] = 0;
                binaryProtocol.send(cmd ,2);
                break;
            }

            if (ui->btStopPolling->isEnabled())
            {
                pollingTimer->start(ui->sbPollingTimeout->value());
            }
            break;
        case CMD_GET_UID:
        {
            QString uid;
            for (int k=4; k < len; k++)
            {
                uid += QString( "%1" ).arg(byte2hexstring(buff[k]));
            }

            logd("Tag is found: " + uid);
            storeTag2File(uid);
            qApp->processEvents();
            QThread::msleep(200);
            if (ui->btStopPolling->isEnabled())
            {
                pollingTimer->start(ui->sbPollingTimeout->value());
            }
        }
        break;
        case CMD_OTA_BEGIN:
            firmwareFlashStart();
            break;
        case CMD_OTA_FRAME:
            firmwareFlashFrame();
            break;
        case CMD_OTA_FINISH:
            oneShotTimer->stop();
            logd("Restarting device...");
            qApp->processEvents();
            cmd[0] = CMD_REBOOT;
            binaryProtocol.send(cmd ,1);
            hardwareLabel->setText("");
            break;
        case CMD_FACTORY_RESET:
        case CMD_REBOOT:
            oneShotTimer->stop();
            logd("Device is restarting. Please wait...");
            hardwareLabel->setText("");
            qApp->processEvents();
            QThread::msleep(3000);

            if (clientSocket == NULL)
            {
                cmd[0] = CMD_GET_VERSION;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(500);
            }
            else
            {
                disconnectHost();
                on_btNetworkConnect_clicked();
                //logd("Please connect to device again.");
            }
            break;
        case CMD_SET_NET_CFG:
            if (oneShotTimerState == TIMER_READING_NETWORK)
            {
                oneShotTimer->stop();
                switch (buff[2]) {
                case 0:
                    ui->cbWifiMode->setCurrentIndex(buff[3]);
                    break;
                case 1:
                    ui->cbAuthMode->setCurrentIndex(buff[3]);
                    break;
                case 2:
                    ui->sbChannel->setValue(buff[3]);
                    break;
                case 3:
                    ui->leSsid->setText((char*)&buff[3]);
                    break;
                case 4:
                    ui->leWifiPassword->setText((char*)&buff[3]);
                    break;
                case 5:
                    ui->cbAddressType->setCurrentIndex(buff[3]);
                    break;
                case 6:
                    ui->leIp->setText(QString("%1.%2.%3.%4").arg(buff[3]).arg(buff[4]).arg(buff[5]).arg(buff[6]));
                    break;
                case 7:
                    ui->leNetmask->setText(QString("%1.%2.%3.%4").arg(buff[3]).arg(buff[4]).arg(buff[5]).arg(buff[6]));
                    break;
                case 8:
                    ui->leGateway->setText(QString("%1.%2.%3.%4").arg(buff[3]).arg(buff[4]).arg(buff[5]).arg(buff[6]));
                    break;
                case 9:
                    ui->leDns->setText(QString("%1.%2.%3.%4").arg(buff[3]).arg(buff[4]).arg(buff[5]).arg(buff[6]));
                    break;
                default:
                    logd("Reading network done.");
                    oneShotTimerState = TIMER_STATE_IDLE;
                    return;
                }

                cmd[0] = CMD_SET_NET_CFG;
                cmd[1] = buff[2] + 1;
                binaryProtocol.send(cmd ,2);
                oneShotTimer->start(1000);
            }
            else if (oneShotTimerState == TIMER_WRITING_NETWORK)
            {
                oneShotTimer->stop();
                switch (buff[2]) {
                case 0:
                    if (ui->cbAuthMode->isEnabled())
                    {
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 1;
                        cmd[cmdLen++] = ui->cbAuthMode->currentIndex();
                        logd("Writing auth mode...");
                        break;
                    }
                case 1:
                    if (ui->sbChannel->isEnabled())
                    {
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 2;
                        cmd[cmdLen++] = ui->sbChannel->value();
                        logd("Writing channel...");
                        break;
                    }
                case 2:
                    if (ui->leSsid->isEnabled())
                    {
                        std::string str = ui->leSsid->text().toStdString();
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 3;
                        strcpy((char*)&cmd[cmdLen] , str.c_str());
                        cmdLen += ui->leSsid->text().length();
                        logd("Writing SSID...");
                        break;
                    }
                    break;
                case 3:
                    if (ui->leWifiPassword->isEnabled())
                    {
                        std::string str = ui->leWifiPassword->text().toStdString();

                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 4;
                        strcpy((char*)&cmd[cmdLen] , str.c_str());
                        cmdLen += ui->leWifiPassword->text().length();
                        logd("Writing password...");
                        break;
                    }
                case 4:
                    cmd[cmdLen++] = CMD_SET_NET_CFG;
                    cmd[cmdLen++] = 5;
                    cmd[cmdLen++] = ui->cbAddressType->currentIndex();
                    logd("Writing address type...");
                    break;
                case 5:
                    if (ui->leIp->isEnabled())
                    {
                        QStringList ipParts = ui->leIp->text().split(".");
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 6;
                        cmd[cmdLen++] = ipParts.at(0).toInt();
                        cmd[cmdLen++] = ipParts.at(1).toInt();
                        cmd[cmdLen++] = ipParts.at(2).toInt();
                        cmd[cmdLen++] = ipParts.at(3).toInt();
                        logd("Writing IP address...");
                        break;
                    }
                case 6:
                    if (ui->leNetmask->isEnabled())
                    {
                        QStringList ipParts = ui->leNetmask->text().split(".");
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 7;
                        cmd[cmdLen++] = ipParts.at(0).toInt();
                        cmd[cmdLen++] = ipParts.at(1).toInt();
                        cmd[cmdLen++] = ipParts.at(2).toInt();
                        cmd[cmdLen++] = ipParts.at(3).toInt();
                        logd("Writing netmask address...");
                        break;
                    }
                case 7:
                    if (ui->leGateway->isEnabled())
                    {
                        QStringList ipParts = ui->leGateway->text().split(".");
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 8;
                        cmd[cmdLen++] = ipParts.at(0).toInt();
                        cmd[cmdLen++] = ipParts.at(1).toInt();
                        cmd[cmdLen++] = ipParts.at(2).toInt();
                        cmd[cmdLen++] = ipParts.at(3).toInt();
                        logd("Writing gateway address...");
                        break;
                    }
                case 8:
                    if (ui->leDns->isEnabled())
                    {
                        QStringList ipParts = ui->leDns->text().split(".");
                        cmd[cmdLen++] = CMD_SET_NET_CFG;
                        cmd[cmdLen++] = 9;
                        cmd[cmdLen++] = ipParts.at(0).toInt();
                        cmd[cmdLen++] = ipParts.at(1).toInt();
                        cmd[cmdLen++] = ipParts.at(2).toInt();
                        cmd[cmdLen++] = ipParts.at(3).toInt();
                        logd("Writing DNS address...");
                        break;
                    }
                default:
                    logd("Writing network done.");
                    oneShotTimerState = TIMER_STATE_IDLE;
                    return;
                }
                binaryProtocol.send(cmd ,cmdLen);
                oneShotTimer->start(1000);
            }
            break;
        }//end if switch
    }

    if (buff[0] == CMD_ERROR)
    {
        QString msg;
        if (buff[1] == CMD_OTA_FINISH)
        {
            msg = "Firmware verification filed - wrong firmware file ???";
        }
        logd(msg);

        oneShotTimer->stop();
    }
}

void MainWindow::oneShotTimeout(void)
{
    QString msg;

    switch (oneShotTimerState)
    {
    case TIMER_STATE_IDLE:
        break;
    /*case TIMER_WAIT4VERSION:
            msg = "No response from the hardware...";
            serialPortClose();
            disconnectHost();
            logd(msg);
            statusLabel->setText("Connection timeout");
            hardwareLabel->setText(msg);
        break;*/
    case TIMER_WAIT4SEARCH_DEVICES:
        scanNext();
        break;
    default:
        //if (oneShotTimerState == TIMER_FIRMWARE_LOADING)
        if (retry_count++ < 5)
        {
            logd("Repeating last frame...");
            binaryProtocol.repeat();
            break;
        }
        oneShotTimerState = TIMER_STATE_IDLE;
        statusLabel->setText("Timeout");
        hardwareLabel->setText("No response to bootloader");
        logd("Timeout - Please recconnect device and try again.");
        oneShotTimer->stop();
        serialPortClose();
        disconnectHost();
        break;
    }
}

void MainWindow::binaryProtocolWrite(uint8_t *buff, size_t len)
{
    if (serialPort.isOpen())
    {
        QByteArray ba((char*)buff, len);
        protoLog(ba , "[TX]");
        serialPort.write((const char*)buff, len);
    }
    if (tcpClientConnected && clientSocket != NULL)
    {
        QByteArray ba((char*)buff, len);
        protoLog(ba , "[TX]");
        clientSocket->write(ba);
        clientSocket->flush();
    }
}

void MainWindow::binaryProtocolFrameTimeout()
{
    protoLogRaw(lastReceivedFrame, "[RX-timeout]");
    lastReceivedFrame.clear();
}

void MainWindow::binaryProtocolFrameError()
{
    protoLogRaw(lastReceivedFrame, "[RX-error]");
    lastReceivedFrame.clear();
}


void MainWindow::on_btSerialConnect_clicked()
{
    uint8_t address;
    bool bStatus = false;

    if (ui->gbRS485->isChecked())
    {
        address = ui->leTargetAddress->text().toUInt(&bStatus,16);
        if (bStatus == false || address == 0)
        {
            logd("Please enter the correct device address!");
            ui->leTargetAddress->setFocus();
            return;
        }
    }
    else
    {
        address = 0;
    }

    binaryProtocol.setAddress(address);
    if (serialPort.isOpen())
    {
        serialPortClose();
    }
    else
    {
        oneShotTimerState = TIMER_WAIT4VERSION;
        serialPortOpen();
        requestVersion();
        oneShotTimer->start(500);
    }
}

void MainWindow::on_btFlashFirmware_clicked()
{
    uint8_t cmd[5];

    if (oneShotTimerState == TIMER_FIRMWARE_LOADING)
    {
        oneShotTimerState = TIMER_STATE_IDLE;
        ui->btFlashFirmware->setText("Flash firmware");
        oneShotTimer->stop();
        progressBar->setValue(0);
        logd("Flash firmware aborted.");
        return;
    }

    firmwareFileName =  QFileDialog::getOpenFileName(NULL, tr("Select firmware file"));

    if (firmwareFileName.isEmpty())
    {
        logd("Firmware not selected!");
        return;
    }

    logd("Firmware file " + firmwareFileName);

    oneShotTimerState = TIMER_FIRMWARE_LOADING;
    cmd[0] = CMD_OTA_BEGIN;
    binaryProtocol.send(cmd ,1);

    ui->btFlashFirmware->setText("Abort...");
    oneShotTimer->start(1000);
}

void MainWindow::on_btRefresComPorts_clicked()
{
    serialPortRefreshList();
}

void MainWindow::on_btClearLogs_clicked()
{
    ui->teProtocol->clear();
}

void MainWindow::requestVersion(void)
{
    if (serialPort.isOpen() || tcpClientConnected)
    {
        uint8_t cmd[5];
        cmd[0] = CMD_GET_VERSION;
        binaryProtocol.send(cmd ,1);
        statusLabel->setText("Connected");
        hardwareLabel->setText("");
    }
}

void MainWindow::requestHardwareVersion(void)
{
    if (serialPort.isOpen() || tcpClientConnected)
    {
        uint8_t cmd[5];
        cmd[0] = CMD_GET_VERSION;
        binaryProtocol.send(cmd ,1);
        statusLabel->setText("Connected");
        hardwareLabel->setText("");
    }
}

void MainWindow::scanNext(void)
{
    oneShotTimer->stop();

    if (searchAddress == 255)
    {
        logd("Done.");
        ui->btScanRs485->setText("Scan");
        progressBar->setValue(0);
        serialPortClose();
        ui->btSerialConnect->setEnabled(true);
        return;
    }

    searchAddress++;
    binaryProtocol.setAddress(searchAddress);
    progressBar->setValue(searchAddress);
    qApp->processEvents();

    requestHardwareVersion();
    oneShotTimerState = TIMER_WAIT4SEARCH_DEVICES;
    oneShotTimer->start(SEARCH_TIMEOUT);
}

void MainWindow::on_btScanRs485_clicked()
{

    oneShotTimer->stop();
    progressBar->setValue(1);
    progressBar->setMaximum(255);
    qApp->processEvents();

    if (serialPort.isOpen())
    {
        logd("Searching aborted.");
        progressBar->setValue(0);
        serialPortClose();
        ui->btSerialConnect->setEnabled(true);
        ui->btScanRs485->setText("Scan");
    }
    else
    {
        searchAddress = 0;
        binaryProtocol.setAddress(searchAddress);
        ui->cbDetectedDevices->clear();

        logd("Searching for devices. Please wait...");

        ui->btSerialConnect->setEnabled(false);
        ui->btScanRs485->setText("Cancel");
        oneShotTimerState = TIMER_WAIT4SEARCH_DEVICES;
        serialPortOpen();
        requestHardwareVersion();

        oneShotTimer->start(SEARCH_TIMEOUT);
    }
}

void MainWindow::on_cbDetectedDevices_currentIndexChanged(const QString &arg1)
{
    Q_UNUSED(arg1);
    ui->leTargetAddress->setText(ui->cbDetectedDevices->currentText().left(2));
}


void MainWindow::on_btCommnadExecute_clicked()
{
    uint8_t cmd[1024];
    uint8_t len = 0, idx = 0;
    QListWidgetItem *item = ui->lvCommands->currentItem();

    if (item == NULL) {
        QMessageBox::information(this, "Waring", "Please select command first", QMessageBox::Ok);
        return;
    }
    //execute commands
    QJsonObject obj = item->data(Qt::UserRole).toJsonObject();
    int value = obj["value"].toInt();
    QJsonArray args = obj["args"].toArray();

    if (args.isEmpty())
    {
        cmd[0] = value;
        binaryProtocol.send(cmd ,1);
    }
    else
    {
        cmd[len++] = value;
        foreach (const QJsonValue & arg, args) {
            QJsonObject obj = arg.toObject();
            QString type = obj["type"].toString().toLower();
            bool optional = obj["optional"].toBool(false);

            if (type == "uint8") {
                bool isOk;
                int val;

                val = argsEdit[idx]->text().toInt(&isOk);
                if (isOk == false && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }
                cmd[len++] = val & 0xff;
            }

            if (type == "uint16") {
                bool isOk;
                uint16_t val;

                val = (uint16_t)argsEdit[idx]->text().toInt(&isOk);
                if (isOk == false && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }
                cmd[len++] = val & 0x00ff;
                cmd[len++] = (val >> 8) & 0x00ff;
            }

            if (type == "uint24") {
                bool isOk;
                uint32_t val;

                val = (uint32_t)argsEdit[idx]->text().toInt(&isOk);
                if (isOk == false && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }
                cmd[len++] = val & 0x000000ff;
                cmd[len++] = (val >> 8) & 0x000000ff;
                cmd[len++] = (val >> 16) & 0x000000ff;
            }

            if (type == "uint32") {
                bool isOk;
                uint32_t val;

                val = (uint32_t)argsEdit[idx]->text().toInt(&isOk);
                if (isOk == false && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }
                cmd[len++] = val & 0x000000ff;
                cmd[len++] = (val >> 8) & 0x000000ff;
                cmd[len++] = (val >> 16) & 0x000000ff;
                cmd[len++] = (val >> 24) & 0x000000ff;
            }

            if (type == "hex") {
                bool isOk;

                QStringList hexParts = argsEdit[idx]->text().split(' ');

                if (hexParts.size() == 1 && hexParts.at(0).isEmpty()) {
                    hexParts.clear();
                }

                if (hexParts.size() == 0 && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }

                for (int k = 0; k < hexParts.size(); k++) {
                    int hexVal = hexParts.at(k).toInt(&isOk, 16);
                    cmd[len++] = hexVal & 0xff;
                }
            }

            idx++;
        }
        binaryProtocol.send(cmd ,len);
    }
}

void MainWindow::on_lvCommands_currentRowChanged(int currentRow)
{
    Q_UNUSED(currentRow);

    int idx = 0;
    int xpos = 5, ypos = 20, xposSecond = 0;
    QListWidgetItem *i = ui->lvCommands->currentItem();

    if (i == NULL) {
        return;
    }

    for (int k = 0; k < MAX_ARGS; k++) {
        if (argsEdit[k] || argsLabel[k]) {
            //store old values
            lastValue[k] = argsEdit[k]->text();

            delete argsEdit[k];
            delete argsLabel[k];

            argsEdit[k] = NULL;
            argsLabel[k] = NULL;
        }
    }

    //load arguments
    QJsonObject obj = i->data(Qt::UserRole).toJsonObject();
    QJsonArray args = obj["args"].toArray();

    if (args.isEmpty()) {
        return;
    }

    foreach (const QJsonValue & arg, args) {
        QJsonObject obj = arg.toObject();
        int width, maxlen;

        width = obj["width"].toInt(120);
        maxlen = obj["maxlen"].toInt(255);

        argsLabel[idx] = new QLabel(obj["name"].toString() + ":",ui->gbCommands);
        argsLabel[idx]->setGeometry(xpos,ypos, 120, 25);
        argsLabel[idx]->setAlignment(Qt::AlignRight);
        argsLabel[idx]->show();

        argsEdit[idx] = new QLineEdit(ui->gbCommands);
        argsEdit[idx]->setGeometry(xpos + 120, ypos - 4, width, 20);
        argsEdit[idx]->setMaxLength(maxlen);
        argsEdit[idx]->show();

        argsEdit[idx]->setText(lastValue[idx]);

        if (xposSecond < xpos + 140 + width && idx < 3)
        {
            xposSecond = xpos + 140 + width;
        }
        else if (xposSecond < xpos + 140 + width && idx < 6)
        {
            xposSecond = xpos + 140 + width;
        }
        ypos += 30;

        if (obj["type"].toString().toLower() == "uint8") {
            argsEdit[idx]->setValidator( new QIntValidator(0, 255, this) );
        }
        if (obj["type"].toString().toLower() == "uint16") {
            argsEdit[idx]->setValidator( new QIntValidator(0, 65000, this) );
        }
        if (obj["type"].toString().toLower() == "uint24") {
            argsEdit[idx]->setValidator( new QIntValidator(0, 16777216, this) );
        }
        if (obj["type"].toString().toLower() == "uint32") {
            argsEdit[idx]->setValidator( new QIntValidator(0, 4294967295/2, this) );
        }
        if (obj["type"].toString().toLower() == "hex") {
            argsEdit[idx]->setValidator( new HexValidator(this) );
        }

        idx++;
        if (idx == 3) {
            xpos = xposSecond;
            ypos = 20;
        }
        if (idx == 6) {
            xpos = xposSecond;
            ypos = 20;
        }
    }
}

void MainWindow::on_lvCommands_itemDoubleClicked(QListWidgetItem *item)
{
    Q_UNUSED(item);
    on_btCommnadExecute_clicked();
}

void MainWindow::on_btAddFavCommand_clicked()
{
    NewFavCmdWindow *p = new NewFavCmdWindow(this, -1, "", "");

    connect(p, &NewFavCmdWindow::addNewFavCommand, this, &MainWindow::addNewFavCommand);

    p->show();
}

void MainWindow::addNewFavCommand(int idx, QString name, QString hex)
{
    if (idx == -1) {
        config->addCmd(name,hex);
    }
    else
    {
        Command cmd;
        cmd.name = name;
        cmd.hex = hex;
        config->commands.replace(idx, cmd);
    }

    config->save();
    reloadFavs();

    if (idx != -1) {
        ui->lvFavCommands->setCurrentRow(idx);
    }
}

void MainWindow::on_btEditFavCommand_clicked()
{
    if (ui->lvFavCommands->selectedItems().size() == 0) {
        return;
    }

    int idx = ui->lvFavCommands->currentRow();

    NewFavCmdWindow *p = new NewFavCmdWindow(this, idx, config->commands.at(idx).name, config->commands.at(idx).hex);

    connect(p, &NewFavCmdWindow::addNewFavCommand, this, &MainWindow::addNewFavCommand);

    p->show();
}

void MainWindow::on_lvFavCommands_itemDoubleClicked(QListWidgetItem *item)
{
    QString hex = item->data(Qt::UserRole).toString();
    QStringList hexParts = hex.split(' ');
    bool isOk;
    uint8_t cmd[1024];
    int len = 0;

    if (hexParts.size() == 1 && hexParts.at(0).isEmpty()) {
        hexParts.clear();
    }

    for (int k = 0; k < hexParts.size(); k++) {
        int hexVal = hexParts.at(k).toInt(&isOk, 16);
        cmd[len++] = hexVal & 0xff;
    }

    binaryProtocol.writeRaw(cmd, len);
}

void MainWindow::on_btDeleteFavCommand_clicked()
{
    if (ui->lvFavCommands->selectedItems().size() == 0) {
        return;
    }

    config->deleteCmd(ui->lvFavCommands->currentRow());
    config->save();
    reloadFavs();
}

void MainWindow::on_btFavDown_clicked()
{
    int index = ui->lvFavCommands->currentRow();
    if (index == -1) {
        return;
    }

    if (index != 0) {
        config->commands.move(index,index-1);
        config->save();
        reloadFavs();
        ui->lvFavCommands->setCurrentRow(index -1);
    }
}

void MainWindow::on_btFavUp_clicked()
{
    int index = ui->lvFavCommands->currentRow();
    if (index == -1) {
        return;
    }

    if (index != config->commands.size()) {
        config->commands.move(index,index+1);
        config->save();
        reloadFavs();
        ui->lvFavCommands->setCurrentRow(index+1);
    }
}


void MainWindow::on_cbDetectedDevices_activated(const QString &arg1)
{
    if (arg1.isEmpty()) {
        return;
    }

    ui->leTargetAddress->setText(arg1.mid(0,2));
}


void MainWindow::on_btReadNetwork_clicked()
{
    uint8_t cmd[5];

    oneShotTimerState = TIMER_READING_NETWORK;
    oneShotTimer->start(1000);

    logd("Reading network settings...");

    cmd[0] = CMD_SET_NET_CFG;
    cmd[1] = 0;
    binaryProtocol.send(cmd ,2);
}

void MainWindow::on_btWriteNetwork_clicked()
{
    uint8_t cmd[5];

    //validating data
    if (ui->cbAddressType->currentIndex() == 1){
        if (ui->leIp->text().split(".").count() != 4) {
            QMessageBox::information(this, "Waring", "Please enter correct IP address", QMessageBox::Ok);
            ui->leIp->setFocus();
            return;
        }
        if (ui->leNetmask->text().split(".").count() != 4) {
            QMessageBox::information(this, "Waring", "Please enter correct netmask address", QMessageBox::Ok);
            ui->leNetmask->setFocus();
            return;
        }
        if (ui->leGateway->text().split(".").count() != 4) {
            QMessageBox::information(this, "Waring", "Please enter correct gateway address", QMessageBox::Ok);
            ui->leGateway->setFocus();
            return;
        }
        if (ui->leDns->text().split(".").count() != 4) {
            QMessageBox::information(this, "Waring", "Please enter correct DNS address", QMessageBox::Ok);
            ui->leDns->setFocus();
            return;
        }
    }


    oneShotTimerState = TIMER_WRITING_NETWORK;
    oneShotTimer->start(1000);

    logd("Writing network settings...");

    cmd[0] = CMD_SET_NET_CFG;
    cmd[1] = 0;
    cmd[2] = ui->cbWifiMode->currentIndex();
    binaryProtocol.send(cmd ,3);
}

void MainWindow::on_cbAddressType_currentIndexChanged(int index)
{
    ui->leIp->setEnabled(index != 0);
    ui->leNetmask->setEnabled(index != 0);
    ui->leGateway->setEnabled(index != 0);
    ui->leDns->setEnabled(index != 0);
}

void MainWindow::on_cbWifiMode_currentIndexChanged(int index)
{
    switch (index) {
    case 0: //AP
        ui->cbAuthMode->setEnabled(true);
        ui->sbChannel->setEnabled(true);
        ui->leSsid->setEnabled(true);
        ui->leWifiPassword->setEnabled(true);
        break;
    case 1: //Client
        ui->cbAuthMode->setEnabled(false);
        ui->sbChannel->setEnabled(false);
        ui->leSsid->setEnabled(true);
        ui->leWifiPassword->setEnabled(true);
        break;
    case 2:
        ui->cbAuthMode->setEnabled(false);
        ui->sbChannel->setEnabled(false);
        ui->leSsid->setEnabled(false);
        ui->leWifiPassword->setEnabled(false);
        break;
    }
}

void MainWindow::on_tabs_currentChanged(int index)
{
    ui->gbCommands->setVisible(index == 0);
}

void MainWindow::on_btRestartDevice_clicked()
{
    uint8_t cmd[5];

    if (QMessageBox::question(this, "Question", "Do you really want to restart device?", QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) {
        cmd[0] = CMD_REBOOT;
        binaryProtocol.send(cmd ,1);
    }
}

void MainWindow::connectToHost(QString address, int port)
{
    int try_count = 0;
    clientSocket = new QTcpSocket(this);

    while (1)
    {
        logd("Connecting to " + address + " ...");
        qApp->processEvents();
        clientSocket->connectToHost(QHostAddress(address), port);

        if(clientSocket->waitForConnected(2500)== false) {
            if (try_count++ == 2)
            {
                logd("TCP connection filed");
                delete clientSocket;
                clientSocket = NULL;
                return;
            }
        }
        else
        {
            break;
        }
    }
    connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
    connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpClientError(QAbstractSocket::SocketError)));
    logd("New TCP client connected");

    ui->btNetworkConnect->setText("Disconnect");
    ui->tabSerialConnection->setEnabled(false);
    ui->gbCommands->setEnabled(true);
    ui->tabs->setEnabled(true);
    ui->btNetworkRefresh->setEnabled(false);
    tcpClientConnected = true;

    oneShotTimerState = TIMER_WAIT4VERSION;
    requestVersion();
    oneShotTimer->start(2500);
}

void MainWindow::disconnectHost()
{
    if (tcpClientConnected)
    {
        clientSocket->close();
        tcpClientConnected = false;
    }

    ui->btNetworkConnect->setText("Connect");
    ui->btFlashFirmware->setText("Flash firmware");
    ui->tabSerialConnection->setEnabled(true);
    ui->gbCommands->setEnabled(false);
    ui->tabs->setEnabled(false);
    ui->btNetworkRefresh->setEnabled(true);
    statusLabel->setText("");
    hardwareLabel->setText("");
}

void MainWindow::onSocketStateChanged(QAbstractSocket::SocketState socketState)
{
    if (socketState == QAbstractSocket::UnconnectedState)
    {
        tcpClientConnected = false;
        clientSocket = NULL;
    }
}

void MainWindow::onReadyRead()
{
    QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());

    QByteArray bytesReceived = sender->readAll();
    lastReceivedFrame.append(bytesReceived);
    binaryProtocol.parse((uint8_t *)bytesReceived.data(), bytesReceived.count());
}

void MainWindow::tcpClientError(QAbstractSocket::SocketError socketError)
{
    uint8_t err = (uint8_t)socketError;
    QByteArray ba((char*)&err, 1);
    protoLogRaw(ba, "TCP socker error");
    disconnectHost();
}

void MainWindow::on_cbSaveToFile_stateChanged(int arg1)
{
    Q_UNUSED(arg1);
    ui->leOutputXlsFile->setEnabled(ui->cbSaveToFile->isChecked());
    ui->btSelectOutputXlsFile->setEnabled(ui->cbSaveToFile->isChecked());

    QSettings settings("Eccel_Ltd", "C1-client");
    settings.setValue("OutputXlsEnabled", ui->cbSaveToFile->isChecked());
}

void MainWindow::on_btSelectOutputXlsFile_clicked()
{
    QString file =  QFileDialog::getSaveFileName(this, "Save xlsx", "", "Export file (*.xlsx)");

    if (file.isEmpty())
    {
        return;
    }

    ui->leOutputXlsFile->setText(file);
    QSettings settings("Eccel_Ltd", "C1-client");
    settings.setValue("OutputXlsFile", ui->leOutputXlsFile->text());
}

void MainWindow::pollingTimeout(void)
{
    if (serialPort.isOpen() || tcpClientConnected)
    {
        uint8_t cmd[5];
        cmd[0] = CMD_GET_TAG_COUNT;
        binaryProtocol.send(cmd ,1);
    }
}


void MainWindow::on_btStartPolling_clicked()
{
    pollingTimer->start(ui->sbPollingTimeout->value());
    ui->btStartPolling->setEnabled(false);
    ui->btStopPolling->setEnabled(true);
    qApp->processEvents();
}

void MainWindow::on_btStopPolling_clicked()
{
    pollingTimer->stop();
    ui->btStartPolling->setEnabled(true);
    ui->btStopPolling->setEnabled(false);
}

void MainWindow::storeTag2File(QString uid)
{
    QFile file(ui->leOutputXlsFile->text());

    QString time = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
    if (ui->cbSaveToFile->isChecked() == false)
        return;


    Document xlsx(file.fileName());

    if (file.exists() && xlsx.load() == false)
    {
        QMessageBox::information(this, "Error", "Unable to open output file", QMessageBox::Ok);
        return;
    }

    // get full cells of current sheet
    // get current sheet
    AbstractSheet* currentSheet = xlsx.sheet("Sheet1");
    int maxRow = 1;
    int maxCol = 0;

    if ( NULL != currentSheet )
    {
        // get full cells of current sheet
        currentSheet->workbook()->setActiveSheet(0);
        Worksheet* wsheet = (Worksheet*) currentSheet->workbook()->activeSheet();
        if ( NULL == wsheet )
            return;

        QVector<CellLocation> clList = wsheet->getFullCells(&maxRow, &maxCol);
    }
    else
    {
        xlsx.write(1, 1, "Date");
        xlsx.write(1, 2, "Time");
        xlsx.write(1, 3, "UID");
    }

    xlsx.write(maxRow+1, 1, QDateTime::currentDateTime().date());
    xlsx.write(maxRow+1, 2, time);
    xlsx.write(maxRow+1, 3, uid);

    xlsx.save();
}

void MainWindow::on_btNetworkConnect_clicked()
{
    if (tcpClientConnected)
    {
        disconnectHost();
    }
    else
    {
        QString address = ui->cbNetworkPort->itemData(ui->cbNetworkPort->currentIndex()).toString();
        if (address.isEmpty())
        {
            logd("Please refresh network device list and select device first");
            return;
        }
        //connect to host here
        connectToHost(address, 1234);
    }

    hardwareLabel->setText("");
}

void MainWindow::on_btNetworkRefresh_clicked()
{
    QUdpSocket *udpSocket = new QUdpSocket(this);
    QString mIpAddress;
    QString dest;

    ui->cbNetworkPort->clear();

    QByteArray datagram = "P_C1:SCAN";

    const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost);

    logd("Searching network...");

    for (const QHostAddress &address: QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost)
        {
            QStringList fields;
            fields = address.toString().split(".");
            for (int k=0; k< 255; k++)
            {

                dest = QString("%1.%2.%3.%4").arg(fields.at(0)).arg(fields.at(1)).arg(fields.at(2)).arg(k);
                udpSocket->writeDatagram(datagram, QHostAddress(dest), DISCOVERY_PORT);
            }
        }
    }
}

void MainWindow::on_tabWidget_currentChanged(int index)
{
    if (tcpClientConnected == false && serialPort.isOpen() == false && index == 1)
    {
        on_btNetworkRefresh_clicked();
    }
}
