#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtSerialPort/QSerialPortInfo>
#include <QtSerialPort/QSerialPort>
#include <binary_protocol.h>
#include <QLabel>
#include <QTimer>
#include <QProgressBar>
#include <QFile>
#include <QByteArray>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QLineEdit>
#include <QListWidgetItem>
#include <QtNetwork>
#include "config.h"
#include <pollingwindow.h>
#include <QTcpServer>
#include <QTcpSocket>

#define MAX_ARGS 10
#define FIRMWARE_PART_SIZE  256
#define SEARCH_TIMEOUT  100

#define C1_HARDWARE             1
#define C1_MUX_HARDWARE         2
#define C1_MODULE_HARDWARE      3
#define C1_V2_HARDWARE          4
#define C1_10X_HARDWARE         5
#define C1_BALLERS_HARDWARE     6 //not used
#define C1_MODULEv2_HARDWARE    7
#define C1_MUXv2_HARDWARE       8
#define C1_V2_BALLERS_HARDWARE  9 //not used
#define C1_PODPOINT_HARDWARE    10
#define C1_ADHOC_HARDWARE       11
#define C2_V1_HARDWARE          12

#define HEADER_ADDRESS_TAG 0x31307847
#define ADDRESS_INFO_PAGE   4
#pragma pack(push, 1)
typedef struct
{
    uint32_t header; // 0x31307847 - magic header comes from string 10xG
    uint8_t RS485_Address; // Module address on bus
    uint8_t IsTerminated; // Should module terminate bus
    //do not move this field
    uint16_t PayloadCRC16; // 16 bit CRC of payload, ShortName, RS485_Address, IsTerminated
}AddressTagData;
#pragma pack(pop)

typedef enum {
    WIFI_MODE = 0x00,
    WIFI_AUTH,
    WIFI_CHANNEL,
    WIFI_SID,
    WIFI_PASSWORD,
    DHCP_CLIENT,
    NET_ADDR,
    NET_NETMASK,
    NET_GATEWAY,
    NET_DNS,
    WEB_ADMIN,
    WEB_PASSWORD
} cmd_set_network_cfg;

typedef enum {
    CMD_ACK = 0x00,
    CMD_DUMMY_COMMAND,
    CMD_GET_TAG_COUNT,
    CMD_GET_UID,
    CMD_ACTIVATE_TAG,
    CMD_HALT,
    CMD_SET_POLLING,
    CMD_SET_KEY,
    CMD_SAVE_KEYS,
    CMD_SET_NET_CFG,
    CMD_REBOOT,
    CMD_GET_VERSION,
    CMD_UART_PASSTHRU,
    CMD_SLEEP,
    CMD_GPIO,
    CMD_ANTENNA,
    CMD_BT_PIN,
    CMD_FACTORY_RESET,
    CMD_PROTO_AUTH,
    CMD_PROTO_CONFIG,
    CMD_LED,
    CMD_BT_DATA,
    CMD_POLLING_SETUP,
    CMD_SLEEP_SETUP,
    CMD_KNOWN_LIST,

    //mifare clasics commands
    CMD_MF_READ_BLOCK = 0x20,
    CMD_MF_WRITE_BLOCK,
    CMD_MF_READ_VALUE,
    CMD_MF_WRITE_VALUE,
    CMD_MF_INCREMENT,
    CMD_MF_TRANSFER,
    CMD_MF_RESTORE,
    CMD_MF_TRANSFER_RESTORE,
    CMD_MF_SET_UID,

    //mifare ultralight
    CMD_MFU_READ_PAGE = 0x40,
    CMD_MFU_WRITE_PAGE,
    CMD_MFU_GET_VERSION,
    CMD_MFU_READ_SIG,
    CMD_MFU_WRITE_SIG,
    CMD_MFU_LOCK_SIG,
    CMD_MFU_READ_COUNTER,
    CMD_MFU_INCREMENT_COUNTER,
    CMD_MFU_PASSWD_AUTH,
    CMD_MFUC_AUTHENTICATE,
    CMD_MFU_CHECKEVENT,

    CMD_MFDF_GET_VERSION = 0x60,
    CMD_MFDF_SELECT_APP,
    CMD_MFDF_APP_IDS,
    CMD_MFDF_FILE_IDS,
    CMD_MFDF_AUTH,
    CMD_MFDF_AUTH_ISO,
    CMD_MFDF_AUTH_AES,
    CMD_MFDF_CREATE_APP,
    CMD_MFDF_DELETE_APP,
    CMD_MFDF_CHANGE_KEY,
    CMD_MFDF_GET_KEY_SETTINGS,
    CMD_MFDF_CHANGE_KEY_SETTINGS,
    CMD_MFDF_CREATE_DATA_FILE,
    CMD_MFDF_WRITE_DATA,
    CMD_MFDF_READ_DATA,
    CMD_MFDF_CREATE_VALUE_FILE,
    CMD_MFDF_GET_VALUE,
    CMD_MFDF_CREDIT,
    CMD_MFDF_LIMITED_CREDIT,
    CMD_MFDF_DEBIT,
    CMD_MFDF_CREATE_RECORD_FILE,
    CMD_MFDF_WRITE_RECORD,
    CMD_MFDF_READ_RECORD,
    CMD_MFDF_CLEAR_RECORDS,
    CMD_MFDF_DELETE_FILE,
    CMD_MFDF_GET_FREEMEM,
    CMD_MFDF_FORMAT,
    CMD_MFDF_COMMIT_TRANSACTION,
    CMD_MFDF_ABORT_TRANSACTION,

    CMD_ICODE_INVENTORY_START = 0x90,
    CMD_ICODE_INVENTORY_NEXT,
    CMD_ICODE_STAY_QUIET,
    CMD_ICODE_READ_BLOCK,
    CMD_ICODE_WRITE_BLOCK,
    CMD_ICODE_LOCK_BLOCK,
    CMD_ICODE_WRITE_AFI,
    CMD_ICODE_LOCK_AFI,
    CMD_ICODE_WRITE_DSFID,
    CMD_ICODE_LOCK_DSFID,
    CMD_ICODE_GET_SYSTEM_INFORMATION,
    CMD_ICODE_GET_MULTIPLE_BSS,
    CMD_ICODE_PASSWORD_PROTECT_AFI,
    CMD_ICODE_READ_EPC,
    CMD_ICODE_GET_NXP_SYSTEM_INFORMATION,
    CMD_ICODE_GET_RANDOM_NUMBER,
    CMD_ICODE_SET_PASSWORD,
    CMD_ICODE_WRITE_PASSWORD,
    CMD_ICODE_LOCK_PASSWORD,
    CMD_ICODE_PROTECT_PAGE,
    CMD_ICODE_LOCK_PAGE_PROTECTION,
    CMD_ICODE_GET_MULTIPLE_BPS,
    CMD_ICODE_DESTROY,
    CMD_ICODE_ENABLE_PRIVACY,
    CMD_ICODE_ENABLE_64BIT_PASSWORD,
    CMD_ICODE_READ_SIGNATURE,
    CMD_ICODE_READ_CONFIG,
    CMD_ICODE_WRITE_CONFIG,
    CMD_ICODE_PICK_RANDOM_ID,
    CMD_ICODE_EXT_READ_BLOCK=179,
    CMD_ICODE_EXT_WRITE_BLOCK,

    CMD_FOPEN = 0xE0,
    CMD_FREAD,
    CMD_FWRITE,
    CMD_FCLOSE,
    CMD_LOG,

    CMD_OTA_BEGIN = 0xF0,
    CMD_OTA_FRAME,
    CMD_OTA_FINISH,
    CMD_PN_EEPROM,
    CMD_TEST_MODE,
    CMD_PN_REG,
    CMD_PN_OTA_BEGIN,
    CMD_PN_OTA_FRAME,
    CMD_PN_OTA_FINISH,

    CMD_ASYNC 	= 0xFE,
    CMD_ERROR	= 0xFF,
} c1_coommands_t;

typedef enum {
    TIMER_STATE_IDLE,
    TIMER_STATE_RFID_LOOP,
    TIMER_WAIT4VERSION,
    TIMER_WAIT4SEARCH_DEVICES,
    TIMER_SEARCH_NETWORK_DEVICES,
    TIMER_FIRMWARE_LOADING,
    TIMER_PN_FIRMWARE_LOADING,
    TIMER_READING_NETWORK,
    TIMER_WRITING_NETWORK,
    TIMER_WAIT4WRITE_TAG,
    TIMER_WAIT4READ_TAG,
    TIMER_FOPEN_READ,
    TIMER_FOPEN_WRITE,
    TIMER_WRITE,
    TIMER_READ,
    TIMER_CLOSE,
    TIMER_CLOSE_AFTER_WRITE,
} timerState_t;

typedef enum {
    DL_STATE4BACKUP,
    DL_STATE4POLLING,
    DL_STATE4UART,
} downloaderState_t;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    Config *config;
    void reloadFavs();
    void loadCommands(int state);
    void udpNetworkScanTask();
    void udpNetworkServerTask(QObject *parent);
    void udpNetworkServerTerminate(void);
private slots:
    void setControlsEnabled(bool);
    void udpMessageReceive(QString msg, QString ip);
    void logd(QString msg);
    void protoLog(QByteArray msg, QString prefix);
    void protoLogRaw(QByteArray msg, QString prefix);
    void handleSerialDataReady();
    void handleSerialDataError(QSerialPort::SerialPortError serialPortError);

    void binaryProtocolFrameComplete(uint8_t *header, size_t hlen, uint8_t *buff, size_t len);
    void binaryProtocolWrite(uint8_t *buff, size_t len);
    void binaryProtocolFrameTimeout();
    void binaryProtocolFrameError();

    void oneShotTimeout(void);
    void pollingTimeout(void);
    void updateListTimer(void);

    void on_btSerialConnect_clicked();
    void on_btFlashFirmware_clicked();
    void on_btRefresComPorts_clicked();
    void on_btClearLogs_clicked();
    void on_btScanRs485_clicked();
    void on_cbDetectedDevices_currentIndexChanged(const QString &arg1);
    void on_btCommnadExecute_clicked();
    void on_lvCommands_currentRowChanged(int currentRow);
    void on_lvCommands_itemDoubleClicked(QListWidgetItem *item);
    void on_btAddFavCommand_clicked();
    void on_lvFavCommands_itemDoubleClicked(QListWidgetItem *item);
    void on_btDeleteFavCommand_clicked();
    void addNewFavCommand(int idx, QString name, QString hex);
    void on_btFavDown_clicked();
    void on_btFavUp_clicked();
    void on_btEditFavCommand_clicked();
    void on_cbDetectedDevices_activated(const QString &arg1);
    void on_btShowLegend_clicked();
    void on_btReadNetwork_clicked();
    void on_btWriteNetwork_clicked();
    void on_cbAddressType_currentIndexChanged(int index);

    void on_cbWifiMode_currentIndexChanged(int index);

    void on_tabs_currentChanged(int index);

    void on_btRestartDevice_clicked();
    //network
    void udpReceive();
    void udpAboutToClose();

    void onSocketStateChanged(QAbstractSocket::SocketState socketState);
    void onReadyRead();
    void tcpClientError(QAbstractSocket::SocketError socketError);
    void connectToHost(QString address, int port);
    void disconnectHost();
    void on_cbSaveToFile_stateChanged(int arg1);

    void on_btSelectOutputXlsFile_clicked();
    void on_btStartPolling_clicked();

    void on_btStopPolling_clicked();

    void on_btNetworkConnect_clicked();

    void on_btNetworkRefresh_clicked();

    void on_tabWidget_currentChanged(int index);

    void on_btWriteAddressTag_clicked();

    void on_btReadAddressTag_clicked();

    void on_btLoopTest_clicked();

    void on_btOpenWeb_clicked();

    void on_btSaveList_clicked();
    void on_lvDetectedDevices_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);

    void on_btDownloadConfig_clicked();

    void on_btUploadConfig_clicked();

    void on_btPollingConfiguration_clicked();
    void updateConfigSignal(QString newCfg);
    void binaryProtocolSendSignal(uint8_t *buff, int len);

    void on_btUartConfiguration_clicked();

    void on_btPNUpdate_clicked();

private:
    Ui::MainWindow *ui;
    QSerialPort serialPort;
    BinaryProtocol binaryProtocol;

    uint8_t searchAddress;

    QLabel *hardwareLabel;
    QLabel *statusLabel;
    QProgressBar *progressBar;

    QTimer *oneShotTimer;
    timerState_t oneShotTimerState;
    downloaderState_t downloaderState;

    QTimer *pollingTimer;
    QByteArray fileContent;
    QString dlFileName;
    //QUdpSocket *udpSocket;

    QJsonArray jsonCommandsArray;
    QLabel *argsLabel[MAX_ARGS];
    QLineEdit *argsEdit[MAX_ARGS];
    QString lastValue[MAX_ARGS];

    QString getCommandName(int cmdValue);

    QFuture<void> udpServerTask;
    QFuture<void> udpClientTask;

    QTcpSocket *clientSocket;
    bool tcpClientConnected;


    void serialPortClose(void);
    void serialPortOpen(void);
    void serialPortRefreshList(QString com);
    void requestVersion(void);
    void requestHardwareVersion(void);
    void scanNext(void);
    void firmwareFlashStart(void);
    void firmwareFlashFrame(void);
    void pnFirmwareFrame(void);
    void firmwareFlashEnd(void);
    QString byte2hexstring(uint8_t value);
    void storeTag2File(QString uid);
    void updateStats(void);
    QByteArray firmwareBuff;
    int firmwarePos;
    QString firmwareFileName;
    int retry_count;
    AddressTagData tagData;

    int noTagCount;
    int tagInFieldCount;
    int retryCount;
    int errorCount;
    uint32_t testWriteValue;
    qint64 testTime;
};
#endif // MAINWINDOW_H
