#ifndef WORKER_H_INCLUDED
#define WORKER_H_INCLUDED

#include <QObject>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QFile>
#include <QJsonObject>
#include <QHash>

#include <optional>

#include "worker_thread.h"
#include "update.h"
#include "git/git_client.h"
#include "ismas/ismas_client.h"

#ifdef PTU5
#define SERIAL_PORT "ttymxc2"
#else
#define SERIAL_PORT "ttyUSB0"
#endif


enum class UPDATE_STATUS : quint8 {
    NOT_DEFINED,
    ISMAS_WAIT_STATE_CHECK_PENDING,
    ISMAS_WAIT_STATE_CHECK_FAILURE,
    ISMAS_RESPONSE_RECEIVED,
    BACKEND_CHECK,
    BACKEND_CHECK_FAILURE,
    BACKEND_NOT_CONNECTED,
    ISMAS_UPDATE_TRIGGER_SET,
    ISMAS_UPDATE_TRIGGER_NOT_SET_OR_WRONG,
    GIT_CLONE_AND_CHECKOUT_SUCCESS,
    GIT_CLONE_AND_CHECKOUT_FAILURE,
    GIT_CHECKOUT_BRANCH,
    GIT_CHECKOUT_BRANCH_FAILURE,
    GIT_FETCH_UPDATES,
    GIT_FETCH_UPDATES_REQUEST_FAILURE,
    EXEC_OPKG_COMMAND,
    EXEC_OPKG_COMMANDS,
    RSYNC_UPDATES_FAILURE,
    RSYNC_UPDATES_SUCCESS,
    RSYNC_FILE_SUCCESS,
    JSON_PARSE_FAILURE,
    UPDATE_PROCESS_SUCCESS,
    UPDATE_PROCESS_FAILURE,
    ISMAS_SANITY_CHECK_OK,
    ISMAS_UPDATE_TRIGGER_SET_FAILURE,
    PSA_UPDATE_FILES_FAILED,
    GIT_CHECK_FILES_TO_UPDATE_SUCCESS,
    ISMAS_SEND_LAST_VERSION_FAILED,
    SAVE_LOG_FILES_FAILED
};

struct UpdateStatus {
    UPDATE_STATUS m_updateStatus;
    QString m_statusDescription;

    explicit UpdateStatus(UPDATE_STATUS s = UPDATE_STATUS::NOT_DEFINED,
                          QString const &d = QString(""))
        : m_updateStatus(s), m_statusDescription(d) {}
};

QDebug operator<<(QDebug debug, UpdateStatus status);
QString& operator<<(QString &str, UpdateStatus status);

#define ISMAS_UPDATE_REQUESTS   (10)

class MainWindow;
class hwinf;
class Worker : public QObject {
    Q_OBJECT

    WorkerThread m_workerThread;
    int const m_customerNr;
    QString const m_customerNrStr;
    int const m_machineNr;
    int const m_zoneNr;
    QString const m_pluginName;
    QString const m_workingDirectory;
    QString const m_branchName;
    QString const m_customerRepositoryPath;
    QString const m_customerRepository;
    bool const m_dryRun;
    QObject *m_parent;
    QString const m_serialInterface;
    QString const m_baudrate;
    IsmasClient m_ismasClient;
    GitClient m_gc;
    QString const m_osVersion;
    QString const m_atbqtVersion;
    QString const m_cpuSerial;
    QString const m_pluginVersionATBDeciceController;
    QString const m_pluginVersionIngenicoISelf;
    QString const m_pluginVersionMobilisisCalc;
    QString const m_pluginVersionMobilisisCalcConfig;
    QString const m_pluginVersionPrmCalc;
    QString const m_pluginVersionPrmCalcConfig;
    QString const m_pluginVersionTcpZvt;

    int m_ismasUpdateRequests;
    QTimer m_waitForNewUpdates;

    UpdateStatus m_updateStatus;

    QStringList m_filesToUpdate;
    bool m_updateProcessRunning;
    int m_displayIndex;
    int m_returnCode;

    MainWindow *m_mainWindow;
    int m_progressValue;
    bool m_withoutIsmasDirectPort;
    QString m_apismVersion;

    bool executeOpkgCommand(QString opkgCommand);
    QString getOsVersion() const;
    QString getATBQTVersion() const;
    QString getCPUSerial() const;
    QString getRaucVersion() const;
    QString getOpkgVersion() const;
    QString getPluginVersion(QString const &pluginFileName) const;
    QStringList getDCVersion() const;

    qint64 getFileSize(QString const &fileName) const;

public:
    static const QString UPDATE_STEP_OK;
    static const QString UPDATE_STEP_DONE;
    static const QString UPDATE_STEP_FAIL;
    static const QString UPDATE_STEP_SUCCESS;

    explicit Worker(int customerNr, // 281
                    int machineNr,
                    int zoneNr,
                    QString branchName,
                    QString pluginName,
                    QString workingDir = ".",
                    bool dryRun = false,
                    QObject *parent = nullptr,
                    char const *serialInterface = SERIAL_PORT,
                    char const *baudrate = "115200");
    ~Worker();

    void setMainWindow(MainWindow *mainWindow) { m_mainWindow = mainWindow; }
    hwinf *getPlugin();
    hwinf const *getPlugin() const;
    void setProgress(int progress);
    void startProgressLoop();
    void stopProgressLoop();

    IsmasClient &getIsmasClient() { return m_ismasClient; }
    IsmasClient const &getIsmasClient() const { return m_ismasClient; }

    bool updateProcessRunning() const { return m_updateProcessRunning; }
    int returnCode() const { return m_returnCode; }

    int machineNr() const { return m_machineNr; }
    int customerNr() const { return m_customerNr; }
    int zoneNr() const { return m_zoneNr; }
    QString apismVersion() const { return m_apismVersion; }

    MainWindow *mainWindow() { return m_mainWindow; }
    MainWindow const *mainWindow() const { return m_mainWindow; }

    //friend QDebug operator<<(QDebug debug, Worker const &w) {
    //    Q_UNUSED(w);
    //    return debug;
    //}
    //friend QString& operator<<(QString &str, Worker const &w) {
    //    Q_UNUSED(w);
    //    return str;
    //}

signals:
    void appendText(QString, QString suffix = "");
    void replaceLast(QString, QString);
    void replaceLast(QStringList, QString);
    void showErrorMessage(QString title, QString description);
    void showStatusMessage(QString title, QString description);
    void stopStartTimer();
    void restartExitTimer();
    void enableExit();
    void disableExit();

public slots:
    void update();

private slots:
    bool updateTriggerSet();
    bool customerEnvironment();
    bool filesToUpdate();
    bool updateFiles(quint8 percent);
    bool syncCustomerRepositoryAndFS();
    bool sendIsmasLastVersionNotification();
    bool saveLogFile();

private:
    PSAInstalled getPSAInstalled();
    void privateUpdate();
    std::optional<QString> getApismVersion();
};

#endif // WORKER_H_INCLUDED
