#ifdef IDS_NXT_PATCH
#ifndef MAIN_NXT_PATCH_H
#define MAIN_NXT_PATCH_H

#include <QDialog>
#include <QString>
#include <QStringList>
#include <QProcess>
#include <QThread>
#include <string>
#include <memory>
#include <sstream>

#ifdef CREATE_INSTALL_LOG
#include <QFile>
#include <QTextStream>
#include <QStandardPaths>
#endif


class QuaZip;

namespace Nxt {

unsigned int number_of_logical_cpu_cores();
unsigned long long total_system_memory_in_bytes();
const QString get_executable_path();

template<typename A, typename B>
const QString percent(const A i = 0, const B n = 1);

class NxtPatchWorker;
class NxtPatchDialog;
class NxtPatch;
class NxtZip;

namespace Ui {
    class NxtPatchDialog;
}

class CMake : public QObject
{
    Q_OBJECT

public:
    CMake(QObject* parent = nullptr);
    const std::string capabilities();
    const std::string version();
    const std::string help();
};

class QMake : public QObject
{
    Q_OBJECT

public:
    QMake(const QString& targetname, const QString& targetspath, QObject* parent = nullptr);

    const std::string query() const;
    const std::string version() const;
    const QString& targetName() const;
    const QString& targetsPath() const;
    const QString& basePath() const;

protected:
    QString targetname;
    QString targetspath;
    QString basepath;

    std::string& fixSeparator(std::string& str) const;
};

class NxtPatchDialog : public QDialog
{
    Q_OBJECT

public:
    explicit NxtPatchDialog(const QString& appname, QWidget *parent = nullptr);

    bool withSleep;
    unsigned long sleepMSec;
    const QString virtualBoxUrl;
    const QString styleEnabled;
    const QString styleDisabled;
    Ui::NxtPatchDialog *ui = nullptr;

    void sleep();
    void resetProgressError();

protected:
    void closeEvent(QCloseEvent* event) override;
    void keyPressEvent(QKeyEvent* event) override;

public slots:
    void setProgressStatus(const QString& text);
    void setProgressError(const QString& text);
    void progressChanged(int progress);
    void openVirtualBoxWebsite();

    void error(const QString& text);
    void complete();
};

class NxtPatchWorker : public QThread
{
    Q_OBJECT

public:
    NxtPatchWorker(QObject* parent = nullptr);
    ~NxtPatchWorker();

    const QString uninstallScript();

    #ifdef IDS_NXT_DEBUG
    void dumpDebug();
    #endif /* IDS_NXT_DEBUG */
    bool dumpFile(const std::string& outFile, const std::string& content);

    static const QString quote();
    static const QString enquote(const QString& value);

    static const QString applicationVendor();
    static const QString applicationName();

    static const QString appdataVendorDirName();
    static const QString appdataProductDirName();
    static const QString appdataAppnameDirName();

    static const QString appdataPath();
    static const QString appdataIdsPath();
    static const QString appdataIdsNxtPath();
    static const QString appdataIdsNxtCreatorPath();
    static const QString appdataIdsNxtCreatorUninstallPath();
    static const QString appdataIdsNxtCreatorSettingsPath();
    static const QString appdataIdsNxtCreatorSettingsSdkPath();
    static const QString appdataIdsNxtCreatorNxtsdkPath();
    static const QString appdataIdsNxtCreatorNxtsdkSshPath();
    static const QString appdataIdsNxtCreatorNxtsdkTargetsPath();
    static const QString appdataIdsNxtCreatorVmsharePath();
    static const QString appdataIdsNxtCreatorVmshareSshPath();

    QString applicationDirPath;
    std::unique_ptr<QMake> qmake;
    std::unique_ptr<CMake> cmake;

    const QString buildengineName() const;

    const QString qtcreatorPath() const;
    const QString qtcreatorBinPath() const;
    const QString qtcreatorShareNxtPath() const;
    const QString qtcreatorShareDebuggerPath() const;

    const QString userHomePath() const;
    const QString userAltSrcPath() const;
    static const QString vboxmanagePath();
    const QString nxtsdktoolPath() const;

    const QStringList ovaFiles() const;
    const QStringList zipFiles() const;

    const QStringList requiredDirectories() const;
    bool createRequiredDirectories() const;
    bool testRequiredDirectories() const;

    bool isValidVboxmanage() const;
    bool isValidNxtsdktool() const;
    bool isValidOva() const;
    bool isValidZip() const;

    const std::string ssh_nxt_key_private() const;
    const std::string ssh_root_key_private() const;
    const std::string ssh_nxt_key_public() const;
    const std::string ssh_root_key_public() const;
    const std::string ssh_nxt_key_authorized() const;
    const std::string ssh_root_key_authorized() const;

    bool nxtsdktool(const QStringList& arguments, QByteArray& output, QByteArray& error, int& exitCode, QProcess::ExitStatus& exitStatus) const;

    const QList<QStringList> qtcSettings(const QString& name) const;
    const QList<QStringList> vmSettings(const QString& name) const;

    QList<QPair<std::string, std::string>> qtcDumpFiles();

    bool isSetupValid();

    void run();
    void sleep(const unsigned long timeInMS = 500) { QThread::msleep(timeInMS); }

    int numberOfFiles = 0;
    double lastPercent = 0;

public slots:
    void updateProgressForVmImport(double percent);
    void updateProgressForZipExtract(int i, const QString& file);


signals:
    void setProgressStatus(const QString& text);
    void setProgressError(const QString& text);
    void progressChanged(int progress);
    void error(const QString& text);
    void complete();
    bool vmImported(const QString& file) const;


#ifdef CREATE_INSTALL_LOG

private slots:
    void installLog(const QString &message);
private:
    std::unique_ptr<QFile> fdx;

#endif
private:
    bool running = false;
    int progress = 0;
};

class NxtPatch : public QObject
{
    Q_OBJECT

public:
    NxtPatch(QObject* parent = nullptr);
    ~NxtPatch();
    static const QString appdataIdsNxtCreatorSettingsPath() { return NxtPatchWorker::appdataIdsNxtCreatorSettingsPath(); }

private:
    NxtPatchWorker *nxtPatchWorker = nullptr;
    NxtPatchDialog *nxtPatchDialog = nullptr;
};

class NxtVBoxManage : public QObject
{
    Q_OBJECT

public:
    explicit NxtVBoxManage(QObject *parent = nullptr);

public slots:
    QString vboxDefaultVmFolder();
    QString vboxVersion();
    bool vboxVersionIsValid();
    bool extPackExists();
    bool vmExists(const QString& name);
    bool vmVersionIscorrect(const QString& name);
    bool vmModify(const QStringList& settings);
    bool vmImport(const QString& file);
    bool vmDelete(const QString& name);

    QString getLastError();

    qint64 freeDiskspace();
    bool hostVTxEnabled();
private:
    QString vboxmanagePath;
    bool vboxmanage(const QStringList& arguments, QByteArray& output, QByteArray& error, int& exitCode, QProcess::ExitStatus& exitStatus, bool verbose = true, int msecsFinished = 30000) const;
    QByteArray _lastError;

signals:
    void importProgressChanged(double percent) const;
};

class NxtZip : public QObject
{
    Q_OBJECT

public:
    explicit NxtZip(QObject *parent = nullptr);
    QStringList extractDir(QString fileCompressed, QString dir);
    int numberOfFiles(QString fileCompressed);
private:
    QStringList extractDir(QuaZip &zip, const QString &dir);
    bool extractFile(QuaZip* zip, QString fileName, QString fileDest);
    bool removeFile(QStringList listFile);

signals:
    void fileExtracted(int i, const QString& file);
};

} // namespace Nxt

#endif /* MAIN_NXT_PATCH_H */
#endif /* IDS_NXT_PATCH */
