#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 <pollingwindow.h>
#include <uartconfigwindow.h>
#include <QSettings>
#include <QLoggingCategory>
#include <QTime>
#include <QDesktopServices>
#include <QtConcurrent>
#include <QFuture>
#include "winsock.h"
#include <winsock2.h>
#include <stdio.h>
#include <string.h>

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

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

bool isUdpRunning = true;
bool logsVisible = false;
pollingwindow *pw = NULL;

template <class T> static QVariant toVariant(const QList<T> &list)
{
    QVariantList variantList;
    variantList.reserve(list.size());
    for (const auto& v : list)
    {
        variantList.append(v);
    }
    return variantList;
}

template <class T> static QList<T> toList(const QVariant &qv)
{
    QList <T> dataList;
    foreach(QVariant v, qv.value<QVariantList>()) {
        dataList << v.value<T>();
    }
    return dataList;
}

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("Cx-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);

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

    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);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(updateListTimer()));
    timer->start(1000);

    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(QHostAddress::AnyIPv4, DISCOVERY_PORT, QUdpSocket::ShareAddress) == false)
    {
        qInfo() << "Bind failed";
        return;
    }

    udpSocket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 1250000);

    connect(udpSocket,&QUdpSocket::readyRead,this,&MainWindow::udpReceive);
    connect(udpSocket,SIGNAL(aboutToClose()),this,SLOT(udpAboutToClose()));
*/

    loadCommands(0);
    reloadFavs();
    ui->teLogs->setVisible(false);
    ui->labLogLabel->setVisible(false);

    QPalette p = ui->teLogs->palette(); // define pallete for textEdit..
    p.setColor(QPalette::Base, Qt::black); // set color "Red" for textedit base
    p.setColor(QPalette::Text, Qt::green); // set text color which is selected from color pallete
    ui->teLogs->setPalette(p); // change textedit palette

    QSettings settings("Eccel_Ltd", "C1-client");
    ui->cbSaveToFile->setChecked(settings.value("OutputXlsEnabled").toBool());
    ui->leOutputXlsFile->setText(settings.value("OutputXlsFile").toString());
    restoreGeometry(settings.value("windowGeometry").toByteArray());
    restoreState(settings.value("windowState").toByteArray());

    serialPortRefreshList(settings.value("lastComPort").toString());

    retry_count = 0;

    logd("Network search engine is running in the background, new devices will be automatically detected.");

#ifdef RELEASE_BUILD
    ui->gbAddressTag->setVisible(false);
    ui->gbTests->setVisible(false);
#else
    ui->gbAddressTag->setVisible(true);
    ui->gbTests->setVisible(true);
#endif

    udpClientTask = QtConcurrent::run(this, &MainWindow::udpNetworkScanTask);
    udpServerTask = QtConcurrent::run(this, &MainWindow::udpNetworkServerTask, this);

    ui->labDetected->setVisible(false);
    ui->lvDetectedDevices->setVisible(false);
}

MainWindow::~MainWindow()
{
    udpClientTask.cancel();
    udpServerTask.cancel();
    isUdpRunning = false;
    udpNetworkServerTerminate();
    //save windows settings
    QSettings settings("Eccel_Ltd", "C1-client");

    settings.setValue("windowGeometry", saveGeometry());
    settings.setValue("windowState", saveState());

    if (logsVisible)
    {
        QVariant v = toVariant(ui->splitter->sizes());
        settings.setValue("right_sizes", v);
    }

    qInfo() << "Terminate done";
    delete ui;
}

void MainWindow::udpAboutToClose()
{
    logd("UDP socket closed");
}

void MainWindow::udpMessageReceive(QString msg, QString ip)
{
    //logd("Device " + ip + ", answer: " + msg);
    if (msg.contains("P_C1:") && !msg.contains("SCAN"))
    {
        bool already_added = false;
        //::ffff:172.16.16.153

        QStringList info = msg.split(":");
        if (ip.contains(":"))
        {
            ip = ip.right(ip.length() - ip.lastIndexOf(":") - 1);
        }

        QStringList addr_parts = ip.split(".");
        QString leAddress;
        leAddress.sprintf( "%03d.%03d.%03d.%03d", addr_parts.at(0).toInt(), addr_parts.at(1).toInt(), addr_parts.at(2).toInt(), addr_parts.at(3).toInt());

        for (int k=0; k < ui->lvDetectedDevices->count(); k++)
        {
            QString addr = ui->lvDetectedDevices->item(k)->data(Qt::UserRole).toString();
            if (addr == ip)
            {
                int last_time_found = QTime::currentTime().msecsSinceStartOfDay();
                //logd("Device " + address + " already on the list");
                already_added = true;
                ui->lvDetectedDevices->item(k)->setData(Qt::UserRole+4, last_time_found); //kiedy pojawil sie na liscie
                QFont font = ui->lvDetectedDevices->item(k)->font();
                if (font.italic() == true)
                {
                    font.setItalic(false);
                    logd("Device " + info.at(1) + "(" + ip + ") is online now.");
                }
                ui->lvDetectedDevices->item(k)->setFont(font);
                break;
            }
        }

        if (already_added == false)
        {
            int last_time_found = QTime::currentTime().msecsSinceStartOfDay();
            logd(info.at(1) + " found at address " + ip + ". Version: " + info.at(2));

            QListWidgetItem* item = new QListWidgetItem();
            item->setText(leAddress + " - " + info.at(1) + " (v"  + info.at(2) + ")");

            item->setData(Qt::UserRole, ip);
            item->setData(Qt::UserRole+1, info.at(1)); //name
            item->setData(Qt::UserRole+2, info.at(2)); //ver
            item->setData(Qt::UserRole+3, leAddress); //address z zerami
            item->setData(Qt::UserRole+4, last_time_found); //kiedy pojawil sie na liscie
            ui->lvDetectedDevices->addItem(item);

            ui->labDetected->setText("Detected devices (" + QString::number(ui->lvDetectedDevices->count()) + ")");

        }
        qApp->processEvents();
        //oneShotTimerState = TIMER_STATE_IDLE;
        //oneShotTimer->stop();
    }
}

void MainWindow::binaryProtocolSendSignal(uint8_t *buff, int len)
{
    binaryProtocol.send(buff,len);
}

void MainWindow::udpReceive()
{
    QByteArray datagram;
/*
    while (udpSocket->hasPendingDatagrams())
    {
        QHostAddress sender;
        datagram.resize(int(udpSocket->pendingDatagramSize()));
        udpSocket->readDatagram(datagram.data(), datagram.size(), &sender);
        QByteArray buffer =datagram.data();

        QString answer = QString::fromStdString(buffer.toStdString());

        udpMessageReceive(answer, sender.toString());

    }*/
}

//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++) {
        if (argsEdit[k])
        {
            delete argsEdit[k];
        }

        if (argsLabel[k])
        {
            delete argsLabel[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_FOPEN) cmdVisible = true;

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

#ifdef RELEASE_BUILD
        if (value >= 243) {
            cmdVisible = false;
        }
#endif

        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 text = QDateTime::currentDateTime().toString("hh:mm:ss.zzz - ") + msg;
    ui->teProtocol->append(text);
    QTextCursor cursor = ui->teProtocol->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->teProtocol->setTextCursor(cursor);
    ui->teProtocol->append("");

   /*
    QString timeHtml = "<font color=\"Black\">";
    QString endFontHtml = "</font>";
    QString endLineHtml = "<br>";
    QString text = timeHtml + QDateTime::currentDateTime().toString("hh:mm:ss.zzz: ") + 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() || oneShotTimerState == TIMER_STATE_RFID_LOOP ||
         //   oneShotTimerState == TIMER_WAIT4SEARCH_DEVICES)
            oneShotTimerState == TIMER_WAIT4SEARCH_DEVICES || oneShotTimerState == TIMER_FOPEN_READ  ||
            oneShotTimerState == TIMER_FOPEN_WRITE || oneShotTimerState == TIMER_WRITE ||
            oneShotTimerState == TIMER_READ || oneShotTimerState == TIMER_CLOSE || oneShotTimerState == TIMER_PN_FIRMWARE_LOADING)
    { //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.zzz: ") + 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.zzz: ") + 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::setControlsEnabled(bool enabled)
{
    if (serialPort.isOpen() == false && tcpClientConnected == false)
    {
        ui->tabNetworkConnection->setEnabled(true);
        ui->tabSerialConnection->setEnabled(true);
    }
    else if (serialPort.isOpen() == false)
    {
        ui->tabNetworkConnection->setEnabled(true);
        ui->tabSerialConnection->setEnabled(false);
    }
    else
    {
        ui->tabNetworkConnection->setEnabled(false);
        ui->tabSerialConnection->setEnabled(true);
    }

    if (enabled)
    {
        ui->btNetworkConnect->setText("Disconnect");
        ui->btSerialConnect->setText("Disconnect");
    }
    else
    {
        ui->btSerialConnect->setText("Connect");
        ui->btNetworkConnect->setText("Connect");
        ui->btFlashFirmware->setText("Flash firmware");
        ui->btPNUpdate->setText("RFID chip update");
        statusLabel->setText("");
        hardwareLabel->setText("");
        progressBar->setValue(0);
    }
    ui->cbSerialPort->setEnabled(!enabled);
    ui->gbCommands->setEnabled(enabled);
    ui->tabs->setEnabled(enabled);

    ui->btFlashFirmware->setEnabled(enabled);
    ui->btUploadConfig->setEnabled(enabled);
    ui->btDownloadConfig->setEnabled(enabled);
    ui->btRestartDevice->setEnabled(enabled);
    ui->btPollingConfiguration->setEnabled(enabled);
    ui->btUartConfiguration->setEnabled(enabled);
    ui->btPNUpdate->setEnabled(enabled);
}


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

    setControlsEnabled(false);

    qDebug() << QObject::tr("Port closed");
    statusLabel->setText("");
    hardwareLabel->setText("");
    ui->btFlashFirmware->setText("Flash firmware");
    ui->btPNUpdate->setText("RFID chip update");
    progressBar->setValue(0);

    return;
}

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

    serialPortClose();

    ui->cbSerialPort->clear();

    for (const QSerialPortInfo &info : infos) {
        qDebug() << QObject::tr("Port found: ") + info.systemLocation();
        qDebug() << QObject::tr("Vendor: ") + QString::number(info.vendorIdentifier());
        //if (info.hasVendorIdentifier()) //  && (info.vendorIdentifier() == 4292 || info.vendorIdentifier() == 6790  || info.vendorIdentifier() == 1027)
        {
            portName = info.systemLocation();
            if (portName == com)
            {
                lastPort = ui->cbSerialPort->count();
            }
            ui->cbSerialPort->addItem(portName);
        }
    }

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

void MainWindow::serialPortOpen(void)
{
    const QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
    QString portName = ui->cbSerialPort->currentText(), msg;
    retry_count = 0;
    if (!portName.isEmpty())
    {
        //save windows settings
        QSettings settings("Eccel_Ltd", "C1-client");
        settings.setValue("lastComPort", portName);

        serialPort.setPortName(portName);
        serialPort.setBaudRate(ui->cbConnectionSpeed->currentText().toInt());

        switch (ui->cbParity->currentIndex())
        {
            case 0:
                serialPort.setParity(QSerialPort::NoParity);
            break;
            case 1:
                serialPort.setParity(QSerialPort::EvenParity);
            break;
            case 2:
                serialPort.setParity(QSerialPort::OddParity);
            break;
        }

        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)
            {
                setControlsEnabled(true);
            }
        }
    }
}

void MainWindow::handleSerialDataReady()
{
    QByteArray bytesReceived = serialPort.readAll();
    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.");
    if (serialPort.isOpen())
    {
        serialPortClose();
        QThread::msleep(4000);
        serialPortOpen();
    }
    else
    {
        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);
}

#define ANSI_COLOR_RED     "\x1b[0;31m"
#define ANSI_COLOR_GREEN   "\x1b[0;32m"
#define ANSI_COLOR_YELLOW  "\x1b[0;33m"
#define ANSI_COLOR_BLUE    "\x1b[0;34m"
#define ANSI_COLOR_MAGENTA "\x1b[0;35m"
#define ANSI_COLOR_CYAN    "\x1b[0;36m"
#define ANSI_COLOR_RESET   "\x1b[0m"

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

    lastReceivedFrame.append((char*)header, hlen);
    lastReceivedFrame.append((char*)buff, len);

    len -= 2; //remove CRC
    buff[len] = 0;

    if (buff[0] ==  CMD_LOG)
    {
        if (logsVisible == false)
        {
            logsVisible = true;
            QSettings settings("Eccel_Ltd", "C1-client");
            QList<int> sizes =  toList<int>(settings.value("right_sizes"));
            ui->teLogs->setVisible(true);
            ui->labLogLabel->setVisible(true);
            ui->splitter->setSizes(sizes);
        }

        //forward frame to log
        static QString lastUsedColor = "<font color=\"White\">";
        QString endFontHtml = "</font>";
        QString endLineHtml = "<br>";
        QString input = QString::fromLocal8Bit((const char*)buff+1);

        input.replace(ANSI_COLOR_GREEN, "<font color=\"Green\">");
        input.replace(ANSI_COLOR_RED, "<font color=\"Red\">");
        input.replace(ANSI_COLOR_YELLOW, "<font color=\"Yellow\">");
        input.replace(ANSI_COLOR_BLUE, "<font color=\"Blue\">");
        input.replace(ANSI_COLOR_MAGENTA, "<font color=\"Magenta\">");
        input.replace(ANSI_COLOR_CYAN, "<font color=\"Cyan\">");
        input.replace(ANSI_COLOR_RESET, "<font>");


        QString text = lastUsedColor + input + endFontHtml + endLineHtml;
        QTextCursor cursor = ui->teLogs->textCursor();

        ui->teLogs->insertHtml(text);
        cursor.movePosition(QTextCursor::End);
        ui->teLogs->setTextCursor(cursor);
        return;
    }

    protoLog(lastReceivedFrame, "[RX]");

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

            sscanf(version, "%d.%d", &major, &minor);

            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();
                logd("Version:" + msg);
               /* if (strstr(version, "10xGenomics") != 0)
                {
                    logd("10xGenomics device found");
                    ui->gbAddressTag->setVisible(true);
                }
                else
                {
                    ui->gbAddressTag->setVisible(false);
                }*/

                if (msg.contains("C2"))
                {
                    ui->btPollingConfiguration->setVisible(true);
                }
                else
                {
                    ui->btPollingConfiguration->setVisible((major * 1000) + minor >= 2035);
                }

                if ((major * 1000) + minor >= 2038)
                {
                    cmd[0] = CMD_LOG;
                    cmd[1] = 1;
                    binaryProtocol.send(cmd ,2);
                }
                else
                {
                    //ui->teLogs->setVisible(false);
                    //ui->labLogLabel->setVisible(false);
                }
            }
            break;
        }
        case CMD_GET_TAG_COUNT:
            oneShotTimer->stop();
            if (buff[2] > 0)
            {
                if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
                {
                    tagInFieldCount++;
                    updateStats();
                }
                cmd[0] = CMD_GET_UID;
                cmd[1] = 0;
                binaryProtocol.send(cmd ,2);
                oneShotTimer->start(3000);
                break;
            }
            else
            {
                logd("No tag detected");
                if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
                {
                    cmd[0] = CMD_HALT;
                    binaryProtocol.send(cmd ,1);
                    oneShotTimer->start(3000);
                    noTagCount++;
                    updateStats();
                    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);
            oneShotTimer->stop();

            if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
            {
                QString msg;

                if (ui->cbWriteTest->isChecked())
                {
                    if (buff[2] != 0x01) //ultralight
                    {
                        logd("Ultralight TAG needed for this test. Test aborted.");
                        oneShotTimerState = TIMER_STATE_IDLE;
                        ui->btLoopTest->setText("Start loop test");
                        break;
                    }

                    cmd[0] = CMD_MFU_WRITE_PAGE;
                    cmd[1] = ui->spWritePageAddr->value();
                    cmd[2] = 1;
                    testWriteValue = rand();
                    memcpy(&cmd[3], &testWriteValue, 4);
                    msg.sprintf("Writing %d value to page %d", testWriteValue, ui->spWritePageAddr->value());
                    logd(msg);
                    binaryProtocol.send(cmd ,7);
                    oneShotTimer->start(3000);
                }
                else if (ui->cbReadPage->isChecked())
                {
                    if (buff[2] != 0x01) //ultralight
                    {
                        logd("Ultralight TAG needed for this test. Test aborted.");
                        oneShotTimerState = TIMER_STATE_IDLE;
                        ui->btLoopTest->setText("Start loop test");
                        break;
                    }
                    logd("Reading page 4");
                    cmd[0] = CMD_MFU_READ_PAGE;
                    cmd[1] = ADDRESS_INFO_PAGE;
                    cmd[2] = 1;
                    binaryProtocol.send(cmd ,3);
                    oneShotTimer->start(3000);
                }
                else
                {
                    cmd[0] = CMD_HALT;
                    binaryProtocol.send(cmd ,1);
                    oneShotTimer->start(3000);
                }
                break;
            }

            storeTag2File(uid);
            qApp->processEvents();
            //QThread::msleep(200);
            if (ui->btStopPolling->isEnabled())
            {
                pollingTimer->start(ui->sbPollingTimeout->value());
            }

            if (oneShotTimerState == TIMER_WAIT4WRITE_TAG || oneShotTimerState == TIMER_WAIT4READ_TAG)
            {
                if (buff[2] == 0x01) //ultralight
                {
                    logd("Ultralight TAG detected. Activating TAG...");
                    cmd[0] = CMD_ACTIVATE_TAG;
                    cmd[1] = 0;
                    binaryProtocol.send(cmd ,2);
                    oneShotTimer->start(3000);
                }
                else
                {
                    logd("Ultralight TAG needed for address tag.");
                    oneShotTimer->stop();
                }
            }
        }
        break;
        case CMD_ACTIVATE_TAG:
        {
            QString msg;
            uint8_t struct_size;
            oneShotTimer->stop();
            logd("Tag activated.");
            if (oneShotTimerState == TIMER_WAIT4WRITE_TAG)
            {
                logd("Writing address tag...");

                // byte 0 - block num
                // byte 1 - count
                // byte 2-x - data to write
                memset(cmd,0,sizeof(cmd));
                struct_size = sizeof(tagData);

                cmd[0] = CMD_MFU_WRITE_PAGE;
                cmd[1] = ADDRESS_INFO_PAGE;
                cmd[2] = struct_size / 4;
                memcpy(&cmd[3], &tagData, sizeof(tagData));
                if (struct_size % 4)
                {
                    cmd[2]++;
                }

                binaryProtocol.send(cmd ,cmd[2]*4+3);

                oneShotTimerState = TIMER_WAIT4WRITE_TAG;
                oneShotTimer->start(3000);
            }
            else
            {
                uint8_t struct_size;
                oneShotTimer->stop();
                logd("Reading tag info");

                struct_size = sizeof(tagData);

                cmd[0] = CMD_MFU_READ_PAGE;
                cmd[1] = ADDRESS_INFO_PAGE;
                cmd[2] = struct_size / 4;
                if (struct_size % 4)
                {
                    cmd[2]++;
                }

                binaryProtocol.send(cmd ,3);

                oneShotTimerState = TIMER_WAIT4READ_TAG;
                oneShotTimer->start(3000);
            }
            break;
        }
        case CMD_MFU_WRITE_PAGE:

            if (oneShotTimerState == TIMER_STATE_RFID_LOOP && ui->cbWriteTest->isChecked())
            {
                if (ui->cbReadPage->isChecked())
                {
                    logd("Reading test page");
                    cmd[0] = CMD_MFU_READ_PAGE;
                    cmd[1] = ui->spWritePageAddr->value();
                    cmd[2] = 1;
                    binaryProtocol.send(cmd ,3);
                    oneShotTimer->start(3000);
                }
                else
                {
                    cmd[0] = CMD_HALT;
                    binaryProtocol.send(cmd ,1);
                    oneShotTimer->start(3000);
                }
                break;
            }

            if (oneShotTimerState == TIMER_WAIT4WRITE_TAG)
            {
                logd("Address tag written successful.");
                cmd[0] = CMD_HALT;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                logd("Sending HALT command to disable RF field.");
            }
            break;
        case CMD_MFU_READ_PAGE:
        {
            QString text;
            AddressTagData td;
            oneShotTimer->stop();

            if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
            {
                logd("Read page OK");

                if (ui->cbWriteTest->isChecked())
                {
                    uint32_t val;
                    memcpy(&val, &buff[2], sizeof(val));
                    if (val == testWriteValue)
                    {
                        logd("Write test passed");
                    }
                    else
                    {
                        logd("Write test ERROR - data is not valid");
                        errorCount++;
                    }
                }

                cmd[0] = CMD_HALT;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                break;
            }

#ifndef RELEASE_BUILD
            if (oneShotTimerState != TIMER_STATE_IDLE)
            {
                memcpy(&td, &buff[2], sizeof(td));

                if (td.header != HEADER_ADDRESS_TAG)
                {
                    logd("Wrong address header");
                    break;
                }

                if (td.PayloadCRC16 != GetCCITTCRC((uint8_t*)&td.header, (sizeof(td)-2)))
                {
                    logd("Wrong payload CRC - it is not a valid address tag");
                    break;
                }


                logd("Valid address tag found.");

                ui->cbTagTerminated->setChecked((bool)td.IsTerminated);
                text.sprintf("%02X", td.RS485_Address);
                ui->leDeviceAddress->setText(text);
                logd("Address: 0x" + text);
                if (td.IsTerminated)
                {
                    logd("Terminated: true");
                }
                else
                {
                    logd("Terminated: false");
                }
                cmd[0] = CMD_HALT;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                logd("Sending HALT command to disable RF field.");
            }
#endif
            break;
        }
        case CMD_HALT:
            logd("HALT done");
            oneShotTimer->stop();
            if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
            {
                quint64 t = QDateTime::currentMSecsSinceEpoch() - testTime;
                QString tStr = QString::number(t);

                logd("Test time: " + tStr);

                logd("Get tag count req...");
                cmd[0] = CMD_GET_TAG_COUNT;
                testTime = QDateTime::currentMSecsSinceEpoch();
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                break;
            }
            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_PN_OTA_BEGIN:
        case CMD_PN_OTA_FRAME:
            pnFirmwareFrame();
            break;
        case CMD_PN_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("Restarting the device. Please wait..");
            hardwareLabel->setText("");
            qApp->processEvents();
            if (serialPort.isOpen())
            {
                serialPortClose();
                QThread::msleep(3000);
                serialPortOpen();
            }
            else
            {
                QThread::msleep(3000);
            }

            if (clientSocket == NULL)
            {
                cmd[0] = CMD_GET_VERSION;
                oneShotTimerState = TIMER_WAIT4VERSION;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(500);
            }
            else
            {
                disconnectHost();
                on_btNetworkConnect_clicked();
                //logd("Please connect to device again.");
            }
            break;
        case CMD_FOPEN:
            oneShotTimer->stop();
            if (oneShotTimerState == TIMER_FOPEN_READ)
            {
                fileContent.clear();
                logd("File openned. Downloading...");
                qApp->processEvents();
                cmd[0] = CMD_FREAD;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                oneShotTimerState = TIMER_READ;
            }
            else if (oneShotTimerState == TIMER_FOPEN_WRITE)
            {
                int k=0, toSend;
                logd("Config file openned. Uploading...");
                qApp->processEvents();
                cmd[0] = CMD_FWRITE;

                toSend = fileContent.length();
                if (toSend > 128)
                {
                    toSend = 128;
                }
                for (k=0; k < toSend; k++)
                {
                    cmd[k+1] = (uint8_t)fileContent.at(k);
                }

                oneShotTimerState = TIMER_WRITE;
                binaryProtocol.send(cmd ,toSend+1);
                oneShotTimer->start(3000);
                fileContent.remove(0,toSend);
            }
            break;
        case CMD_FWRITE:
            {
                if (oneShotTimerState == TIMER_WRITE)
                {
                    if (fileContent.size() > 0)
                    {
                        int k=0, toSend;
                        //logd("Uploading. Bytes left " + QString::number(fileContent.size()) + "...");
                        qApp->processEvents();
                        cmd[0] = CMD_FWRITE;

                        toSend = fileContent.length();
                        if (toSend > 128)
                        {
                            toSend = 128;
                        }
                        for (k=0; k < toSend; k++)
                        {
                            cmd[k+1] = (uint8_t)fileContent.at(k);
                        }

                        binaryProtocol.send(cmd ,toSend+1);
                        oneShotTimer->start(3000);
                        oneShotTimerState = TIMER_WRITE;
                        fileContent.remove(0,toSend);
                    }
                    else
                    {
                        oneShotTimer->stop();
                        logd("End of file. Closing the file...");
                        qApp->processEvents();
                        cmd[0] = CMD_FCLOSE;
                        binaryProtocol.send(cmd ,1);
                        oneShotTimer->start(3000);
                        oneShotTimerState = TIMER_CLOSE_AFTER_WRITE;
                    }
                }
            }
            break;
        case CMD_FREAD:
            if (len > 2)
            {
                oneShotTimer->stop();
                qApp->processEvents();
                cmd[0] = CMD_FREAD;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                oneShotTimerState = TIMER_READ;
                fileContent.append((char*)buff+2, len-2);
            }
            else
            {
                oneShotTimer->stop();
                logd("End of file. Closing the file...");
                qApp->processEvents();
                cmd[0] = CMD_FCLOSE;
                binaryProtocol.send(cmd ,1);
                oneShotTimer->start(3000);
                oneShotTimerState = TIMER_CLOSE;
            }
            break;
        case CMD_FCLOSE:
            oneShotTimer->stop();
            if (oneShotTimerState == TIMER_CLOSE_AFTER_WRITE)
            {
                logd("File closed. Restarting...");
                qApp->processEvents();
                cmd[0] = CMD_REBOOT;
                binaryProtocol.send(cmd ,1);
            }
            else
            {
                logd("File closed. Size " + QString::number(fileContent.length()) + " bytes.");
                qApp->processEvents();
                oneShotTimerState = TIMER_STATE_IDLE;
                if (downloaderState == DL_STATE4BACKUP)
                {
                    QFile file(dlFileName);
                    file.remove();
                    file.open(QIODevice::WriteOnly);
                    file.write(fileContent);
                    file.close();
                }
                else if (downloaderState == DL_STATE4POLLING)
                {
                    pw = new pollingwindow(this, fileContent);

                    connect(pw, &pollingwindow::updateConfiguration, this, &MainWindow::updateConfigSignal);
                    connect(pw, &pollingwindow::binaryFrameSend, this, &MainWindow::binaryProtocolSendSignal);
                    connect(&binaryProtocol, &BinaryProtocol::frameComplete, pw, &pollingwindow::binaryProtocolFrameComplete);

                    pw->exec();
                    disconnect(&binaryProtocol, &BinaryProtocol::frameComplete, pw, &pollingwindow::binaryProtocolFrameComplete);
                    delete pw;
                }
                else if (downloaderState == DL_STATE4UART)
                {
                    UartConfigWindow *p = new UartConfigWindow(this, fileContent);

                    connect(p, &UartConfigWindow::updateConfiguration, this, &MainWindow::updateConfigSignal);

                    p->show();
                }
            }
            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;
                    }
                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;

        msg.sprintf("Error 0x%02X%02X received for command - ", buff[2], buff[3]);
        msg += getCommandName(buff[1]);
        logd(msg);

        if (buff[1] == CMD_MF_SET_UID)
        {
            logd("This command may only be executed on special tags with changeable UID");
        }
        else
        if (buff[1] == CMD_OTA_BEGIN || buff[1] == CMD_OTA_FINISH || buff[1] == CMD_OTA_FRAME)
        {
            logd("Firmware writing error - please restart the board and try again");
            oneShotTimerState = TIMER_STATE_IDLE;
            ui->btFlashFirmware->setText("Flash firmware");
            oneShotTimer->stop();
            progressBar->setValue(0);
            logd("Flash firmware aborted.");
        }

        if (buff[1] == CMD_PN_OTA_BEGIN || buff[1] == CMD_PN_OTA_FINISH || buff[1] == CMD_PN_OTA_FRAME)
        {
            logd("Firmware writing error - please restart the board and try again");
            oneShotTimerState = TIMER_STATE_IDLE;
            ui->btPNUpdate->setText("RFID chip update");
            oneShotTimer->stop();
            progressBar->setValue(0);
            logd("Flash firmware aborted.");
        }

        if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
        {
            errorCount++;
            updateStats();

            cmd[0] = CMD_HALT;
            binaryProtocol.send(cmd ,1);
            oneShotTimer->start(3000);
            logd("HALT request");
        }

        oneShotTimer->stop();
    }

    if (buff[0] == CMD_ASYNC)
    {
        switch (buff[1])
        {
        case CMD_DUMMY_COMMAND:
            cmd[0] = CMD_GET_TAG_COUNT;
            binaryProtocol.send(cmd ,1);
            oneShotTimer->start(500);
            break;
        }
    }
}

void MainWindow::updateListTimer(void)
{
    int actual_time = QTime::currentTime().msecsSinceStartOfDay();

    for (int k=0; k < ui->lvDetectedDevices->count(); k++)
    {
        int itemTime = ui->lvDetectedDevices->item(k)->data(Qt::UserRole+4).toInt();
        QFont font = ui->lvDetectedDevices->item(k)->font();
        if (actual_time - itemTime > 10000)
        {
            if (font.italic() == false)
            {
                font.setItalic(true);
                ui->lvDetectedDevices->item(k)->setFont(font);
                logd("Device " + ui->lvDetectedDevices->item(k)->data(Qt::UserRole+1).toString() + "(" + ui->lvDetectedDevices->item(k)->data(Qt::UserRole).toString() + ") is offline now.");
            }
        }
        else
        {
            font.setItalic(false);
            ui->lvDetectedDevices->item(k)->setFont(font);
        }
    }
}

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

    switch (oneShotTimerState)
    {
    case TIMER_STATE_IDLE:
        break;
    case TIMER_WAIT4VERSION:
            msg = "No response from the hardware...";
            if (retry_count++ < 5)
            {
                logd("Repeating get version frame...\n");
                requestHardwareVersion();
                oneShotTimer->start(500);
                break;
            }
            else
            {
                oneShotTimerState = TIMER_STATE_IDLE;
                statusLabel->setText("Timeout");
                hardwareLabel->setText("No response from device");
                logd("Timeout - Please recconnect device and try again.");
                oneShotTimer->stop();
                serialPortClose();
                disconnectHost();
            }
        break;
    case TIMER_SEARCH_NETWORK_DEVICES:
            oneShotTimerState = TIMER_STATE_IDLE;
            //logd(QStringLiteral("Searching finished. Devices found: %1.").arg(ui->cbNetworkPort->count()));
        break;
    case TIMER_WAIT4SEARCH_DEVICES:
        scanNext();
        break;
    case TIMER_STATE_RFID_LOOP:
        if (serialPort.isOpen() || tcpClientConnected)
        {
            logd("Repeating last frame...\n");
            binaryProtocol.repeat();
            oneShotTimer->start(3000);
        }
        break;
    default:
        //if (oneShotTimerState == TIMER_FIRMWARE_LOADING)
        if (retry_count++ < 5)
        {
            logd("Repeating last frame...\n");
            binaryProtocol.repeat();
            oneShotTimer->start(1500);
            break;
        }
        oneShotTimerState = TIMER_STATE_IDLE;
        statusLabel->setText("Timeout");
        hardwareLabel->setText("No response from device");
        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()
{
    logd("[RX-timeout]");
}

void MainWindow::binaryProtocolFrameError()
{
    logd("[RX-error]");
}


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;
    }

    oneShotTimer->stop();
    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();
    ui->teLogs->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 == "text") {
                QString text = argsEdit[idx]->text();
                const char *str = text.toStdString().c_str();
                if (argsEdit[idx]->text().length() == 0 && optional == false){
                    QMessageBox::information(this, "Waring", "Please enter correct value", QMessageBox::Ok);
                    argsEdit[idx]->setFocus();
                    return;
                }

                for (size_t k = 0; k < strlen(str); k++)
                {
                    cmd[len++] = ((uint8_t)str[k]) & 0xff;
                }
            }

            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++;
        }
        oneShotTimerState = TIMER_STATE_IDLE;
        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;
        }
    }

    if (ui->cbAuthMode->currentIndex() == 1 && ui->cbWifiMode->currentIndex() == 0)
    {
        QMessageBox::information(this, "Waring", "WEP mode is not allowed for AP modew", 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(5000)== false) {
            if (try_count++ == 2)
            {
                logd("TCP connection filed. Remeber to enable TCP server in the device settings to user TCP connection from C1 client.");
                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");

    tcpClientConnected = true;
    setControlsEnabled(true);

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

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

    setControlsEnabled(false);
}

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();
    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);
        //oneShotTimer->start(100);
        //oneShotTimerState = TIMER_STATE_RFID_LOOP;
    }
}


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->leDeviceAddr->text();
        if (address.isEmpty())
        {
            logd("Please enter device IP address or select from device list");
            return;
        }
        //connect to host here
        connectToHost(address, ui->sbPort->value());
    }

    hardwareLabel->setText("");
}

SOCKET ReceivingSocket = 0;

void MainWindow::udpNetworkServerTerminate(void)
{
    if (ReceivingSocket)
    {
        shutdown(ReceivingSocket, 2);
        closesocket(ReceivingSocket);
        ReceivingSocket = 0;
        qInfo() << "udpNetworkServerTerminate: Closing socket...";
    }
}

void MainWindow::udpNetworkServerTask(QObject *parent)
{
    WSADATA wsaData;
    SOCKADDR_IN ReceiverAddr;
    int Port = DISCOVERY_PORT;
    char ReceiveBuf[1024];
    int BufLength = 1024;
    SOCKADDR_IN SenderAddr;
    int SenderAddrSize = sizeof(SenderAddr);
    int ByteReceived = 5;

      // Initialize Winsock version 2.2
      if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0){

           qInfo() << "Server: WSAStartup failed with error: " << WSAGetLastError();

           return;
      }
      else{
           qInfo() << "Server: The Winsock DLL status is: " << wsaData.szSystemStatus;
      }

        // Create a new socket to receive datagrams on.
        ReceivingSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

        if (ReceivingSocket == INVALID_SOCKET){
             //Print error message
             qInfo() << "Server: Error at socket(): " << WSAGetLastError();
             // Clean up
             WSACleanup();
             // Exit with error
             return;
        }
        else{
             qInfo() << "Server: socket() is OK!";
        }

        /*Set up a SOCKADDR_IN structure that will tell bind that
        we want to receive datagrams from all interfaces using port 5150.*/
        // The IPv4 family
        ReceiverAddr.sin_family = AF_INET;

        // Port no. (8888)
        ReceiverAddr.sin_port = htons(Port);

        // From all interface (0.0.0.0)
        ReceiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

      // Associate the address information with the socket using bind.
      // At this point you can receive datagrams on your bound socket.

      if (bind(ReceivingSocket, (SOCKADDR *)&ReceiverAddr, sizeof(ReceiverAddr)) == SOCKET_ERROR){

           // Print error message
           qInfo() << "Server: Error! bind() failed!";

           // Close the socket
           closesocket(ReceivingSocket);

           // Do the clean up
           WSACleanup();

           // and exit with error
           return;
        }
        else{
           qInfo() << "Server: bind() is OK!";
        }


      // Print some info on the receiver(Server) side...
      //getsockname(ReceivingSocket, (SOCKADDR *)&ReceiverAddr, (int *)sizeof(ReceiverAddr));


      //qInfo() << "Server: Receiving IP(s) used: " << inet_ntoa(ReceiverAddr.sin_addr);

      //qInfo() << "Server: Receiving port used: " << htons(ReceiverAddr.sin_port);

      //qInfo() << "Server: I\'m ready to receive data packages. Waiting...";


      // At this point you can receive datagrams on your bound socket.
      while (isUdpRunning){ // Server is receiving data until you will close it.(You can replace while(1) with a condition to stop receiving.)

           ByteReceived = recvfrom(ReceivingSocket, ReceiveBuf, BufLength, 0, (SOCKADDR *)&SenderAddr, &SenderAddrSize);

           if (ByteReceived > 0){ //If there are data
               //Print information for received data
               ReceiveBuf[ByteReceived] = 0;
               //qInfo() << "Server: Total Bytes received: " << ByteReceived;
               QString msg = ReceiveBuf;
               QString ip = inet_ntoa(SenderAddr.sin_addr);
               //qInfo() << "Server: The data is: " << msg;
               //qInfo() << "Server: Sending IP used: " << ip;

               QMetaObject::invokeMethod(parent, "udpMessageReceive", Qt::QueuedConnection,
                         Q_ARG(QString, msg), Q_ARG(QString, ip));

           }
           else if (ByteReceived <= 0){ //If the buffer is empty
               //Print error message
               qInfo() << "Server: Connection closed with error code: " << WSAGetLastError();
           }
           else{ //If error
               //Print error message
               qInfo() << "Server: recvfrom() failed with error code: " << WSAGetLastError();
           }
      }


      // When your application is finished receiving datagrams close the socket.
      qInfo() << "Server: Finished receiving. Closing the listening socket...";
      if (ReceivingSocket != 0 && closesocket(ReceivingSocket) != 0){
           qInfo() << "Server: closesocket() failed! Error code: " << WSAGetLastError();
      }
      else{
           qInfo() << "Server: closesocket() is OK";
      }


      if(WSACleanup() != 0){
           qInfo() << "Server: WSACleanup() failed! Error code: " << WSAGetLastError();
      }
      else{
           qInfo() << "Server: WSACleanup() is OK";
      }

      return;
}

void MainWindow::udpNetworkScanTask()
{
    QUdpSocket *udpClient = new QUdpSocket();
    QString mIpAddress;
    QString dest;

    QByteArray datagram = "P_C1:SCAN";

    qInfo() << "scan on different thread";

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

    udpClient->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 1250000);

    while (isUdpRunning)
    {
        for (const QHostAddress &address: QNetworkInterface::allAddresses())
        {
            if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost)
            {
                QStringList fields;
                fields = address.toString().split(".");
                //logd("Scanning " + address.toString());
                for (int k=1; k< 255; k++)
                {
                    if (fields.at(3).toInt() == k)
                    {
                        //logd("Ignoring own address " + address.toString());
                        continue;
                    }
                    dest = QString("%1.%2.%3.%4").arg(fields.at(0)).arg(fields.at(1)).arg(fields.at(2)).arg(k);
                    udpClient->writeDatagram(datagram, QHostAddress(dest), DISCOVERY_PORT);
                }
            }
            QThread::msleep(1);
        }
        QThread::msleep(1000);
        //qInfo() << "scan again";
    }
    qInfo() << "scan done";
}

void MainWindow::on_btNetworkRefresh_clicked()
{
   /* logd("Scanning start...");

    ui->btNetworkRefresh->setEnabled(false);
    ui->cbNetworkPort->clear();
    oneShotTimer->stop();
    oneShotTimerState = TIMER_SEARCH_NETWORK_DEVICES;
    oneShotTimer->start(2500);

    qInfo() << "click start";
    QFuture<void> future = QtConcurrent::run(this, &MainWindow::udpNetworkScan);
    qInfo() << "click done";*/
}

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


    if (index != 1)
    {
        ui->labDetected->setVisible(false);
        ui->lvDetectedDevices->setVisible(false);
    }
    else
    {
        ui->labDetected->setVisible(true);
        ui->lvDetectedDevices->setVisible(true);
    }

}

void MainWindow::on_btWriteAddressTag_clicked()
{
    bool bStatus = false;
    uint8_t address;
    uint8_t cmd[5];

    address = ui->leDeviceAddress->text().toUInt(&bStatus,16);

    if (bStatus == false)
    {
        logd("Please enter correct tag address defice address!");
        return;
    }

    memset(&tagData, 0, sizeof(tagData));

    tagData.header = HEADER_ADDRESS_TAG;

    tagData.RS485_Address = address;
    tagData.IsTerminated = (uint8_t)ui->cbTagTerminated->isChecked();
    tagData.PayloadCRC16 = GetCCITTCRC((uint8_t*)&tagData.header, (sizeof(tagData)-2));

    cmd[0] = CMD_GET_TAG_COUNT;
    binaryProtocol.send(cmd ,1);

    oneShotTimerState = TIMER_WAIT4WRITE_TAG;
    oneShotTimer->start(1000);
}

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

    cmd[0] = CMD_GET_TAG_COUNT;
    binaryProtocol.send(cmd ,1);

    oneShotTimerState = TIMER_WAIT4READ_TAG;
    oneShotTimer->start(1000);
}

void MainWindow::on_btLoopTest_clicked()
{
    uint8_t cmd[10];

    oneShotTimer->stop();

    if (oneShotTimerState == TIMER_STATE_RFID_LOOP)
    {
        logd("Loop test stoped");
        ui->btLoopTest->setText("Start loop test");
        oneShotTimerState = TIMER_STATE_IDLE;
        return;
    }

    logd("Loop test started");
    ui->btLoopTest->setText("Stop loop test");

    tagInFieldCount = 0;
    noTagCount = 0;
    retryCount = 0;
    errorCount = 0;
    updateStats();
    cmd[0] = CMD_GET_TAG_COUNT;
    testTime = QDateTime::currentMSecsSinceEpoch();
    binaryProtocol.send(cmd ,1);
    oneShotTimerState = TIMER_STATE_RFID_LOOP;
    oneShotTimer->start(3000);
}

void MainWindow::updateStats(void)
{
    QString msg;
    msg.sprintf("Not found: %d\tFound:%d\nRetry: %d   \tErrors:%d", noTagCount, tagInFieldCount, retryCount, errorCount);
    ui->labStatistic->setText(msg);

    if (ui->cbStopAfter3k->isChecked() && tagInFieldCount >= 3000)
    {
        logd("Loop test stoped after 3k tests");
        ui->btLoopTest->setText("Start loop test");
        oneShotTimerState = TIMER_STATE_IDLE;
        //play trumped
        //QSound::play(":/wav/trumped.wav");
    }
}

void MainWindow::on_btOpenWeb_clicked()
{
    QString address = ui->leDeviceAddr->text();
    if (address.isEmpty())
    {
        logd("Please enter device IP address or select from device list");
        return;
    }

    QString link = "http://" + address;
    QDesktopServices::openUrl(QUrl(link));
}

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

    if (fileName.isEmpty())
    {
        logd("No file selected");
        return;
    }

    QFile file(fileName);

    file.remove();

    Document xlsx(file.fileName());

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

    xlsx.write(1, 1, "Name");
    xlsx.write(1, 2, "IP");
    xlsx.write(1, 3, "Firmware");


    for (int k=0; k < ui->lvDetectedDevices->count(); k++)
    {
        QString addr = ui->lvDetectedDevices->item(k)->data(Qt::UserRole).toString();
        QString name = ui->lvDetectedDevices->item(k)->data(Qt::UserRole+1).toString(); //name
        QString version = ui->lvDetectedDevices->item(k)->data(Qt::UserRole+2).toString(); //version

        xlsx.write(k+2, 1, name);
        xlsx.write(k+2, 2, addr);
        xlsx.write(k+2, 3, version);
    }

    xlsx.save();
    logd("Device list exported to file " + fileName);
}

void MainWindow::on_lvDetectedDevices_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
    ui->leDeviceAddr->setText(current->data(Qt::UserRole).toString());
}

void MainWindow::on_btDownloadConfig_clicked()
{
    uint8_t cmd[255];
    int len = 0;


    dlFileName =  QFileDialog::getSaveFileName(this, "Save json", "", "Export file (*.json)");

    if (dlFileName.isEmpty())
    {
        logd("Please enter correct file name!");
        return;
    }

    logd("Saving to file " + dlFileName + "...");

    downloaderState = DL_STATE4BACKUP;
    oneShotTimerState = TIMER_FOPEN_READ;
    oneShotTimer->stop();

    cmd[len++] = CMD_FOPEN;
    cmd[len++] = 0;

    strcpy((char*)&cmd[2], "config.json");

    binaryProtocol.send(cmd ,13);

    oneShotTimer->start(3000);
}

void MainWindow::on_btUploadConfig_clicked()
{
    uint8_t cmd[255];
    int len = 0;


    dlFileName =  QFileDialog::getOpenFileName(this, "Select json file", "", "Import file (*.json)");

    if (dlFileName.isEmpty())
    {
        logd("Please enter correct file name!");
        return;
    }

    logd("Loading file " + dlFileName + "...");

    QFile file(dlFileName);
    if(!file.open(QIODevice::ReadOnly)) return;
    fileContent = file.readAll();
    file.close();
    logd("File size " + QString::number(fileContent.length()) + " bytes.");

    oneShotTimerState = TIMER_FOPEN_WRITE;
    oneShotTimer->stop();

    cmd[len++] = CMD_FOPEN;
    cmd[len++] = 1; //writing

    strcpy((char*)&cmd[2], "config.json");

    binaryProtocol.send(cmd, 13);

    oneShotTimer->start(3000);
}

void MainWindow::on_btPollingConfiguration_clicked()
{
    uint8_t cmd[255];
    int len = 0;

    logd("Please wait downloading current configuration...");

    downloaderState = DL_STATE4POLLING;
    oneShotTimerState = TIMER_FOPEN_READ;
    oneShotTimer->stop();

    cmd[len++] = CMD_FOPEN;
    cmd[len++] = 0;

    strcpy((char*)&cmd[2], "config.json");

    binaryProtocol.send(cmd ,13);

    oneShotTimer->start(3000);
}

void MainWindow::updateConfigSignal(QString newCfg)
{
    uint8_t cmd[255];
    int len = 0;

    logd("New configuration received. Updating...");

    fileContent = newCfg.toUtf8();

    oneShotTimerState = TIMER_FOPEN_WRITE;
    oneShotTimer->stop();

    cmd[len++] = CMD_FOPEN;
    cmd[len++] = 1; //writing

    strcpy((char*)&cmd[2], "config.json");

    binaryProtocol.send(cmd, 13);

    oneShotTimer->start(3000);

}

void MainWindow::on_btUartConfiguration_clicked()
{
    uint8_t cmd[255];
    int len = 0;

    logd("Please wait downloading current configuration...");

    downloaderState = DL_STATE4UART;
    oneShotTimerState = TIMER_FOPEN_READ;
    oneShotTimer->stop();

    cmd[len++] = CMD_FOPEN;
    cmd[len++] = 0;

    strcpy((char*)&cmd[2], "config.json");

    binaryProtocol.send(cmd ,13);

    oneShotTimer->start(3000);
}

void MainWindow::on_btPNUpdate_clicked()
{
    uint8_t cmd[25];

    if (ui->btPNUpdate->text() == "Abort...")
    {
        logd("Flash firmware aborted");
        oneShotTimerState = TIMER_STATE_IDLE;
        progressBar->setValue(0);
        ui->btPNUpdate->setText("RFID chip update");
        return;
    }

    firmwareFileName =  QFileDialog::getOpenFileName(NULL, tr("Select firmware file"), NULL, "PN51xx firmware file (*.esfwu)");

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

    logd("Firmware file " + firmwareFileName);

    QFile file(firmwareFileName);

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

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

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

    memcpy(cmd, firmwareBuff.data() + 4, 2);

    logd("Firmware file version detected " + byte2hexstring(cmd[1]) + "." + byte2hexstring(cmd[0]));

    oneShotTimerState = TIMER_PN_FIRMWARE_LOADING;
    cmd[0] = CMD_PN_OTA_BEGIN;
    binaryProtocol.send(cmd ,1);

    ui->btPNUpdate->setText("Abort...");
    oneShotTimer->start(3000);
}

void MainWindow::pnFirmwareFrame(void)
{
    uint8_t cmd[600];

    oneShotTimer->stop();

    if (oneShotTimerState == TIMER_STATE_IDLE) {
        return;
    }

    if (firmwarePos < firmwareBuff.size())
    {
        uint16_t wCurrBlockLen;

        memcpy(cmd, firmwareBuff.data() + firmwarePos, 2);
        wCurrBlockLen = (uint16_t)(((uint16_t)(cmd[0]) << 8) + cmd[1]);

        //logd(QString( "Firmware part %1 bytes" ).arg(wCurrBlockLen));

        memcpy(&cmd[1], firmwareBuff.data() + firmwarePos + 2, wCurrBlockLen);
        cmd[0] = CMD_PN_OTA_FRAME;
        firmwarePos += 2 + wCurrBlockLen;
        progressBar->setValue(firmwarePos);
        qApp->processEvents();
        binaryProtocol.send(cmd, wCurrBlockLen+1);
    }
    else
    {
        cmd[0] = CMD_PN_OTA_FINISH;
        binaryProtocol.send(cmd, 1);
        logd(QString( "Firmware size %1, data written %2" ).arg(firmwareBuff.size()).arg(firmwarePos));
        progressBar->setValue(0);
    }

    oneShotTimer->start(800);
}
