/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2018-2019, Antony jr
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/
 
#include <QCoreApplication>
 
#include "qappimageupdate_p.hpp"
#include "helpers_p.hpp"
 
#ifndef NO_GUI
#include <QApplication>
#include <QMessageBox>
#include <QProcess>
#include <QScreen>
#include <QPixmap>
 
#include "softwareupdatedialog_p.hpp"
#include <ui_AppImageUpdaterDialog.h>
#endif // NOT NO_GUI
 
 
QAppImageUpdatePrivate::QAppImageUpdatePrivate(bool singleThreaded, QObject *parent)
    : QObject(parent) {
    setObjectName("QAppImageUpdatePrivate");
    if(!singleThreaded) {
        m_SharedThread.reset(new QThread);
        m_SharedThread->start();
    }
 
 
    m_SharedNetworkAccessManager.reset(new QNetworkAccessManager);
    m_UpdateInformation.reset(new AppImageUpdateInformationPrivate);
    m_DeltaWriter.reset(new ZsyncWriterPrivate(m_SharedNetworkAccessManager.data()));
#ifdef DECENTRALIZED_UPDATE_ENABLED
    m_Seeder.reset(new Seeder(m_SharedNetworkAccessManager.data()));
#endif
 
    if(!singleThreaded) {
        m_SharedNetworkAccessManager->moveToThread(m_SharedThread.data());
        m_UpdateInformation->moveToThread(m_SharedThread.data());
        m_DeltaWriter->moveToThread(m_SharedThread.data());
#ifdef DECENTRALIZED_UPDATE_ENABLED
	m_Seeder->moveToThread(m_SharedThread.data());
#endif       
    }
    m_ControlFileParser.reset(new ZsyncRemoteControlFileParserPrivate(m_SharedNetworkAccessManager.data()));
    if(!singleThreaded) {
        m_ControlFileParser->moveToThread(m_SharedThread.data());
    }
    m_ControlFileParser->setObjectName("ZsyncRemoteControlFileParserPrivate");
    m_UpdateInformation->setObjectName("AppImageUpdateInformationPrivate");
    m_DeltaWriter->setObjectName("ZsyncWriterPrivate");
 
    // Set logger name
    m_UpdateInformation->setLoggerName("UpdateInformation");
    m_ControlFileParser->setLoggerName("ControlFileParser");
    m_DeltaWriter->setLoggerName("DeltaWriter");
 
 
    // Update information
    connect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::logger,
            this, &QAppImageUpdatePrivate::logger,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
    connect(m_UpdateInformation.data(), SIGNAL(operatingAppImagePath(QString)),
            this, SLOT(setCurrentAppImagePath(QString)),
            Qt::QueuedConnection);
 
 
    // Control file parsing
    connect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::logger,
            this, &QAppImageUpdatePrivate::logger,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
 
    // Delta Writer and Downloader
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::logger,
            this, &QAppImageUpdatePrivate::logger,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::started,
            this, &QAppImageUpdatePrivate::handleUpdateStart,
   	    (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    // Torrent Seeder
#ifdef DECENTRALIZED_UPDATE_ENABLED 
    connect(m_Seeder.data(), &Seeder::started,
            this, &QAppImageUpdatePrivate::torrentClientStarted ,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
    connect(m_Seeder.data(), &Seeder::torrentStatus,
            this, &QAppImageUpdatePrivate::torrentStatus,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
#endif
 
    // Torrent Downloader Specific
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::torrentClientStarted,
            this, &QAppImageUpdatePrivate::torrentClientStarted ,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::torrentStatus,
            this, &QAppImageUpdatePrivate::torrentStatus,
            (Qt::ConnectionType)(Qt::DirectConnection | Qt::UniqueConnection));
}
 
QAppImageUpdatePrivate::QAppImageUpdatePrivate(const QString &AppImagePath, bool singleThreaded, QObject *parent)
    : QAppImageUpdatePrivate(singleThreaded, parent) {
    setAppImage(AppImagePath);
    return;
}
 
QAppImageUpdatePrivate::QAppImageUpdatePrivate(QFile *AppImage, bool singleThreaded, QObject *parent)
    : QAppImageUpdatePrivate(singleThreaded, parent) {
    setAppImage(AppImage);
    return;
}
 
QAppImageUpdatePrivate::~QAppImageUpdatePrivate() {
    if(b_Started || b_Running) {
        cancel();
    }
 
    if(!m_SharedThread.isNull()) {
        m_SharedThread->quit();
        m_SharedThread->wait();
    }
    return;
}
 
void QAppImageUpdatePrivate::setApplicationName(const QString &applicationName) {
    if(b_Started || b_Running) {
        return;
    }
    m_ApplicationName = applicationName;
}
 
void QAppImageUpdatePrivate::setIcon(QByteArray icon) {
    if(b_Started || b_Running) {
        return;
    }
 
    m_Icon = icon;
}
 
void QAppImageUpdatePrivate::setGuiFlag(int flag) {
    if(b_Started || b_Running) {
        return;
    }
 
    n_GuiFlag = flag;
}
 
void QAppImageUpdatePrivate::setAppImage(const QString &AppImagePath) {
    if(b_Started || b_Running) {
        return;
    }
 
    clear();
    getMethod(m_UpdateInformation.data(), "setAppImage(const QString&)")
    .invoke(m_UpdateInformation.data(),
            Qt::QueuedConnection,
            Q_ARG(QString,AppImagePath));
    return;
}
 
void QAppImageUpdatePrivate::setAppImage(QFile *AppImage) {
    if(b_Started || b_Running) {
        return;
    }
 
    clear();
    getMethod(m_UpdateInformation.data(), "setAppImage(QFile *)")
    .invoke(m_UpdateInformation.data(),
            Qt::QueuedConnection,
            Q_ARG(QFile*,AppImage));
}
 
void QAppImageUpdatePrivate::setShowLog(bool choice) {
    if(b_Started || b_Running) {
        return;
    }
 
    getMethod(m_UpdateInformation.data(), "setShowLog(bool)")
    .invoke(m_UpdateInformation.data(),
            Qt::QueuedConnection,
            Q_ARG(bool, choice));
 
    getMethod(m_ControlFileParser.data(), "setShowLog(bool)")
    .invoke(m_ControlFileParser.data(),
            Qt::QueuedConnection,
            Q_ARG(bool, choice));
 
    getMethod(m_DeltaWriter.data(), "setShowLog(bool)")
    .invoke(m_DeltaWriter.data(),
            Qt::QueuedConnection,
            Q_ARG(bool, choice));
}
 
void QAppImageUpdatePrivate::setOutputDirectory(const QString &dir) {
    if(b_Started || b_Running) {
        return;
    }
 
    getMethod(m_DeltaWriter.data(), "setOutputDirectory(const QString&)")
    .invoke(m_DeltaWriter.data(),
            Qt::QueuedConnection,
            Q_ARG(QString, dir));
    return;
}
 
void QAppImageUpdatePrivate::setProxy(const QNetworkProxy &proxy) {
    if(b_Started || b_Running) {
        return;
    }
    m_SharedNetworkAccessManager->setProxy(proxy);
    return;
}
 
void QAppImageUpdatePrivate::clear(void) {
    if(b_Started || b_Running) {
        return;
    }
 
    b_Started = b_Running = b_Finished = b_Canceled = b_CancelRequested = false;
    getMethod(m_UpdateInformation.data(), "clear(void)")
    .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
    getMethod(m_ControlFileParser.data(), "clear(void)")
    .invoke(m_ControlFileParser.data(), Qt::QueuedConnection);
    return;
}
 
 
void QAppImageUpdatePrivate::start(short action, int flags, QByteArray icon) {
    if(b_Started || b_Running) {
        return;
    }
 
    b_Started = b_Running = true;
    b_Canceled = false;
    b_Finished = false;
 
    if(b_CancelRequested) {
        b_Started = b_Running = false;
        b_Canceled = true;
        b_CancelRequested = false;
        emit canceled(action);
        return;
    }
 
    if(flags != (int)GuiFlag::None) {
	    n_GuiFlag = flags;
    }else if(n_GuiFlag == GuiFlag::None) {
	    n_GuiFlag = GuiFlag::Default;
    }
 
    if(icon.isEmpty() && !m_Icon.isEmpty()) {
        icon = m_Icon;
    }
 
    if(action == Action::GetEmbeddedInfo) {
        n_CurrentAction = action;
        connect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::info,
                this, &QAppImageUpdatePrivate::redirectEmbeddedInformation,
                Qt::QueuedConnection);
        connect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::progress,
                this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoProgress);
        connect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::error,
                this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoError,
                Qt::QueuedConnection);
 
        emit started(Action::GetEmbeddedInfo);
        getMethod(m_UpdateInformation.data(), "getInfo(void)")
        .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
 
    } else if(action == Action::CheckForUpdate) {
        n_CurrentAction = action;
 
        //// Needed to check if Bittorrent file is
        //// supported.
#ifdef DECENTRALIZED_UPDATE_ENABLED
        m_ControlFileParser->setUseBittorrent(true);
#else
        m_ControlFileParser->setUseBittorrent(false);
#endif
        connect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
                m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
                m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
                this, SLOT(redirectUpdateCheck(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(progress(int)),
                this, SLOT(handleCheckForUpdateProgress(int)));
 
        connect(m_ControlFileParser.data(), SIGNAL(error(short)),
                this, SLOT(handleCheckForUpdateError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_UpdateInformation.data(), SIGNAL(error(short)),
                this, SLOT(handleCheckForUpdateError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
 
        emit started(Action::CheckForUpdate);
        getMethod(m_UpdateInformation.data(), "getInfo(void)")
        .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
    } else if(action == Action::Update || action == Action::UpdateWithTorrent) {
        n_CurrentAction = action;
 
        //// With respect to GDPR, It is strongly adviced
        //// to use Torrent only if the user explicitly
        //// asks for it.
#ifdef DECENTRALIZED_UPDATE_ENABLED
        m_ControlFileParser->setUseBittorrent((action == Action::UpdateWithTorrent));
#else
        m_ControlFileParser->setUseBittorrent(false);
#endif
        connect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
                m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
                m_ControlFileParser.data(), SLOT(getZsyncInformation(void)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
                m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
                m_DeltaWriter.data(), &ZsyncWriterPrivate::start,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
                this, &QAppImageUpdatePrivate::handleUpdateFinished,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
                this, &QAppImageUpdatePrivate::handleUpdateError,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
                this, &QAppImageUpdatePrivate::handleUpdateError,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
                this, &QAppImageUpdatePrivate::handleUpdateError,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
                this, &QAppImageUpdatePrivate::handleUpdateProgress,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
                this, &QAppImageUpdatePrivate::handleUpdateCancel,
                (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
        //// Started signal will be emitted by handleUpdateStart
        //// which is connected at the construction.
        getMethod(m_UpdateInformation.data(), "getInfo(void)")
        .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
 
    } else if(action == Action::UpdateWithGUI || action == Action::UpdateWithGUIAndTorrent) {
#ifdef NO_GUI
        b_Started = b_CancelRequested = b_Finished = b_Canceled = false;
        emit error(QAppImageUpdateEnums::Error::UnsupportedActionForBuild, action);
        return;
#else
        n_CurrentAction = action;
 
        if(m_ApplicationName.isEmpty()) {
            m_ApplicationName = QCoreApplication::applicationName();
        }
 
        // Setup GUI if not constructed before
        if(!b_GuiClassesConstructed) {
            m_UpdaterDialog.reset(new QDialog);
            m_UpdaterDialog->setObjectName(QString::fromUtf8("AppImageUpdaterDialog"));
 
            m_Ui = QSharedPointer<Ui::AppImageUpdaterDialog>(new Ui::AppImageUpdaterDialog);
            m_Ui->setupUi(m_UpdaterDialog.data());
 
            /*
             * Default program logic.
             */
 
            connect((m_Ui->updateCancelBtn), &QPushButton::clicked, this, &QAppImageUpdatePrivate::cancel, Qt::QueuedConnection);
            connect(m_UpdaterDialog.data(), &QDialog::rejected, this, &QAppImageUpdatePrivate::cancel, Qt::QueuedConnection);
 
            b_GuiClassesConstructed = true;
        }
 
        QPixmap icon;
 
        if(!m_Icon.isEmpty()) {
            icon.loadFromData(m_Icon);
        }
 
        /* Set AppImage icon if given. */
        if(!icon.isNull()) {
            (m_Ui->softwareIcon)->setPixmap(icon);
            (m_Ui->softwareIconOnUpdating)->setPixmap(icon);
            m_UpdaterDialog->setWindowIcon(icon);
        } else {
            bool found = false;
            if(QApplication::windowIcon().isNull()) {
                foreach (QWidget *widget, QApplication::allWidgets()) {
                    if(!((widget->windowIcon()).isNull())) {
                        icon = widget->windowIcon().pixmap(100, 100);
                        (m_Ui->softwareIcon)->setPixmap(icon);
                        (m_Ui->softwareIconOnUpdating)->setPixmap(icon);
                        m_UpdaterDialog->setWindowIcon(icon);
                        found = true;
                        break;
                    }
                    QCoreApplication::processEvents();
                }
            } else {
                icon = QApplication::windowIcon().pixmap(100, 100);
                found = true;
            }
 
            if(!found) {
                (m_Ui->softwareIcon)->setVisible(false);
                (m_Ui->softwareIconOnUpdating)->setVisible(false);
            }
        }
 
	// Reset Check For Update Progress Bar.
	(m_Ui->updateCheckProgressBar)->setValue(0);    
 
 
        m_ConfirmationDialog = QSharedPointer<SoftwareUpdateDialog>(new SoftwareUpdateDialog(nullptr, icon, n_GuiFlag));
 
#ifdef DECENTRALIZED_UPDATE_ENABLED
        m_ControlFileParser->setUseBittorrent((action == Action::UpdateWithGUIAndTorrent));
#else
        m_ControlFileParser->setUseBittorrent(false);
#endif
 
        connect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
                m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
                m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
                this, SLOT(handleGUIUpdateCheck(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(progress(int)),
                this, SLOT(handleGUIUpdateCheckProgress(int)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(error(short)),
                this, SLOT(handleGUIUpdateCheckError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_UpdateInformation.data(), SIGNAL(error(short)),
                this, SLOT(handleGUIUpdateCheckError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        /// First Check for Update
        getMethod(m_UpdateInformation.data(), "getInfo(void)")
        .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
 
        if(n_GuiFlag & GuiFlag::ShowBeforeProgress) {
            (m_Ui->mainStack)->setCurrentIndex(0);
            showWidget();
        }
#endif // NO_GUI
    } else if(action == Action::Seed) {
#ifndef DECENTRALIZED_UPDATE_ENABLED
	b_Started = b_CancelRequested = b_Finished = b_Canceled = false;
        emit error(QAppImageUpdateEnums::Error::UnsupportedActionForBuild, action);
        return;
#else
	n_CurrentAction = action;
 
        m_ControlFileParser->setUseBittorrent(true);
 
	connect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
                m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
                m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
                m_Seeder.data(), SLOT(start(QJsonObject)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_Seeder.data(), SIGNAL(canceled()),
                this, SLOT(handleSeedCancel()),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_Seeder.data(), SIGNAL(error(short)),
                this, SLOT(handleSeedError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_ControlFileParser.data(), SIGNAL(error(short)),
                this, SLOT(handleSeedError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        connect(m_UpdateInformation.data(), SIGNAL(error(short)),
                this, SLOT(handleSeedError(short)),
                (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
        emit started(Action::Seed);
        getMethod(m_UpdateInformation.data(), "getInfo(void)")
        .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
 
#endif // DECENTRALIZED_UPDATE_ENABLED
    } else {
        n_CurrentAction = Action::None;
        b_Started = b_Running = b_Canceled = false;
        emit error(QAppImageUpdateEnums::Error::InvalidAction, n_CurrentAction);
    }
    return;
}
 
void QAppImageUpdatePrivate::cancel(void) {
    if(!b_Started && !b_Running) {
        return;
    }
 
    b_CancelRequested = true;
    getMethod(m_DeltaWriter.data(),"cancel()")
    .invoke(m_DeltaWriter.data(), Qt::QueuedConnection);
    
    return;
}
 
 
/// * * *
/// Private Slots
void QAppImageUpdatePrivate::setCurrentAppImagePath(QString path) {
    m_CurrentAppImagePath = path;
}
 
void QAppImageUpdatePrivate::handleUpdateProgress(int percentage,
        qint64 bytesReceived,
        qint64 bytesTotal,
        double speed,
        QString units) {
    emit progress(percentage, bytesReceived, bytesTotal, speed, units, n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleGetEmbeddedInfoProgress(int percentage) {
    emit progress(percentage, 1, 1, 0, QString(), n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleCheckForUpdateProgress(int percentage) {
    emit progress(percentage, 1, 1, 0, QString(), n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleGetEmbeddedInfoError(short code) {
    b_Canceled = b_Started = b_Running = false;
    b_Finished = false;
    b_CancelRequested = false;
    disconnect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoError);
    disconnect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::info,
               this, &QAppImageUpdatePrivate::redirectEmbeddedInformation);
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::progress,
               this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoProgress);
    emit error(code, n_CurrentAction);
}
 
void QAppImageUpdatePrivate::redirectEmbeddedInformation(QJsonObject info) {
    b_Canceled = b_Started = b_Running = false;
    b_Finished = true;
    disconnect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoError);
    disconnect(m_UpdateInformation.data(),  &AppImageUpdateInformationPrivate::info,
               this, &QAppImageUpdatePrivate::redirectEmbeddedInformation);
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::progress,
               this, &QAppImageUpdatePrivate::handleGetEmbeddedInfoProgress);
 
    if(b_CancelRequested) {
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
    emit finished(info, n_CurrentAction);
}
 
 
void QAppImageUpdatePrivate::handleCheckForUpdateError(short code) {
    b_Canceled = b_Started = b_Running = false;
    b_Finished = false;
    b_CancelRequested = false;
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
               this, SLOT(redirectUpdateCheck(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleCheckForUpdateError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleCheckForUpdateError(short)));
    disconnect(m_ControlFileParser.data(), SIGNAL(progress(int)),
               this, SLOT(handleCheckForUpdateProgress(int)));
 
    emit error(code, n_CurrentAction);
}
 
#ifdef DECENTRALIZED_UPDATE_ENABLED
void QAppImageUpdatePrivate::handleSeedError(short code) {
    b_Canceled = b_Started = b_Running = false;
    b_Finished = false;
    b_CancelRequested = false;
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
                m_Seeder.data(), SLOT(start(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleSeedError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleSeedError(short)));
    disconnect(m_Seeder.data(), SIGNAL(canceled()),
                this, SLOT(handleSeedCancel()));
    disconnect(m_Seeder.data(), SIGNAL(error(short)),
                this, SLOT(handleSeedError(short)));
 
    emit error(code, n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleSeedCancel() {
    b_Canceled = b_Started = b_Running = false;
    b_Finished = false;
    b_CancelRequested = false;
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
                m_Seeder.data(), SLOT(start(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleSeedError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleSeedError(short)));
    disconnect(m_Seeder.data(), SIGNAL(canceled()),
                this, SLOT(handleSeedCancel()));
    disconnect(m_Seeder.data(), SIGNAL(error(short)),
                this, SLOT(handleSeedError(short)));
 
    b_Finished = true;
 
    QJsonObject r { };
    emit finished(r, n_CurrentAction);
}
#endif // DECENTRALIZED_UPDATE_ENABLED
 
void QAppImageUpdatePrivate::redirectUpdateCheck(QJsonObject info) {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
               this, SLOT(redirectUpdateCheck(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleCheckForUpdateError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleCheckForUpdateError(short)));
    disconnect(m_ControlFileParser.data(), SIGNAL(progress(int)),
               this, SLOT(handleCheckForUpdateProgress(int)));
 
 
    // Can this happen without an error?
    if(info.isEmpty()) {
        return;
    }
 
    auto releaseNotes = info["ReleaseNotes"].toString();
    auto embeddedUpdateInformation = info["EmbededUpdateInformation"].toObject();
    auto oldVersionInformation = embeddedUpdateInformation["FileInformation"].toObject();
 
    QString remoteTargetFileSHA1Hash = info["RemoteTargetFileSHA1Hash"].toString(),
            localAppImageSHA1Hash = oldVersionInformation["AppImageSHA1Hash"].toString(),
            localAppImagePath = oldVersionInformation["AppImageFilePath"].toString();
 
    bool torrentSupported = info["TorrentSupported"].toBool();
    auto torrentFileUrl = info["TorrentFileUrl"].toString();
    auto targetFileName = info["RemoteTargetFileName"].toString();
 
    QJsonObject updateinfo {
        { "UpdateAvailable", localAppImageSHA1Hash != remoteTargetFileSHA1Hash},
        { "AbsolutePath", localAppImagePath},
	{ "RemoteTargetFileName", targetFileName}, 
	{ "LocalSha1Hash",  localAppImageSHA1Hash },
        { "RemoteSha1Hash", remoteTargetFileSHA1Hash},
        { "ReleaseNotes", releaseNotes},
        { "TorrentSupported", torrentSupported},
	{ "TorrentFileUrl", torrentFileUrl }
    };
 
    b_Started = b_Running = false;
    b_Finished = true;
    b_Canceled = false;
 
 
    if(b_CancelRequested) {
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
    emit finished(updateinfo, n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleUpdateStart() {
    emit started(n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleUpdateCancel() {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleUpdateFinished);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleUpdateProgress);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleUpdateCancel);
 
    b_Started = b_Running = b_Finished = b_CancelRequested = false;
    b_Canceled = true;
 
    emit canceled(n_CurrentAction);
 
}
 
void QAppImageUpdatePrivate::handleUpdateError(short ecode) {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleUpdateFinished);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleUpdateProgress);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleUpdateCancel);
 
 
    b_Started = b_Running = b_Finished = false;
    b_Canceled = false;
 
    if(b_CancelRequested) {
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
    emit error(ecode, n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleUpdateFinished(QJsonObject info, QString oldVersionPath) {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleUpdateFinished);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleUpdateError);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleUpdateProgress);
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleUpdateCancel);
 
 
    QJsonObject result {
        {"OldVersionPath", oldVersionPath},
        {"NewVersionPath", info["AbsolutePath"].toString()},
        {"NewVersionSha1Hash", info["Sha1Hash"].toString()},
        {"UsedTorrent", info["UsedTorrent"].toBool()},
	{"TorrentFileUrl", info["TorrentFileUrl"].toString()}
    };
    b_Started = b_Running = false;
    b_Finished = true;
    b_Canceled = false;
 
    if(b_CancelRequested) {
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
    emit finished(result, n_CurrentAction);
}
 
 
/// GUI Update routines.
 
#ifndef NO_GUI
void QAppImageUpdatePrivate::showWidget() {
    if(m_Ui.isNull() || m_UpdaterDialog.isNull()) {
        return;
    }
    if(!(n_GuiFlag & GuiFlag::ShowProgressDialog) &&
            (m_Ui->mainStack)->currentIndex() != 0) {
        return;
    }
    m_UpdaterDialog->show();
    return;
}
 
void QAppImageUpdatePrivate::handleGUIConfirmationRejected() {
    handleGUIUpdateCancel();
}
 
void QAppImageUpdatePrivate::handleGUIConfirmationAccepted() {
    doGUIUpdate();
}
 
void QAppImageUpdatePrivate::doGUIUpdate() {
    connect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
            m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)),
            (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
    connect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
            m_ControlFileParser.data(), SLOT(getZsyncInformation(void)),
            (Qt::ConnectionType)(Qt::UniqueConnection | Qt::QueuedConnection));
 
    connect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
            m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
            m_DeltaWriter.data(), &ZsyncWriterPrivate::start,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
            this, &QAppImageUpdatePrivate::handleGUIUpdateFinished,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
            this, &QAppImageUpdatePrivate::handleGUIUpdateError,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
            this, &QAppImageUpdatePrivate::handleGUIUpdateError,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
            this, &QAppImageUpdatePrivate::handleGUIUpdateError,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
            this, &QAppImageUpdatePrivate::handleGUIUpdateProgress,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    connect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
            this, &QAppImageUpdatePrivate::handleGUIUpdateCancel,
            (Qt::ConnectionType)(Qt::QueuedConnection | Qt::UniqueConnection));
 
    getMethod(m_UpdateInformation.data(), "getInfo(void)")
    .invoke(m_UpdateInformation.data(), Qt::QueuedConnection);
 
 
    showWidget();
}
 
void QAppImageUpdatePrivate::handleGUIUpdateCheck(QJsonObject info) {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
 
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
               this, SLOT(handleGUIUpdateCheck(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(progress(int)),
               this, SLOT(handleGUIUpdateCheckProgress(int)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleGUIUpdateCheckError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleGUIUpdateCheckError(short)));
 
 
    /// Connect confirmation buttons
    connect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::rejected,
            this, &QAppImageUpdatePrivate::handleGUIConfirmationRejected, Qt::QueuedConnection);
    connect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::accepted,
            this, &QAppImageUpdatePrivate::handleGUIConfirmationAccepted, Qt::QueuedConnection);
 
 
 
    if(b_CancelRequested) {
        b_Started = b_Running = b_Finished = false;
        b_Canceled = false;
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
 
 
    m_UpdaterDialog->hide();
    m_UpdaterDialog->move(QGuiApplication::primaryScreen()->geometry().center() - m_UpdaterDialog->rect().center());
    (m_Ui->mainStack)->setCurrentIndex(1);
 
    auto notes = info["ReleaseNotes"].toString();
    auto embeddedUpdateInformation = info["EmbededUpdateInformation"].toObject();
    auto oldVersionInformation = embeddedUpdateInformation["FileInformation"].toObject();
 
    QString remoteTargetFileSHA1Hash = info["RemoteTargetFileSHA1Hash"].toString(),
            localAppImageSHA1Hash = oldVersionInformation["AppImageSHA1Hash"].toString(),
            localAppImagePath = oldVersionInformation["AppImageFilePath"].toString();
 
    bool torrentSupported = info["TorrentSupported"].toBool();
 
    bool showUpdateDialog = n_GuiFlag & GuiFlag::ShowUpdateConfirmationDialog;
    bool showNoUpdateDialog = n_GuiFlag & GuiFlag::NotifyWhenNoUpdateIsAvailable;
    bool noConfirmTorrentUsage = n_GuiFlag & GuiFlag::NoConfirmTorrentUsage;
    m_UpdaterDialog->setWindowTitle(QString::fromUtf8("Updating ") +
		    		   (m_ApplicationName.isEmpty() ? 
				    QFileInfo(localAppImagePath).baseName() : 
				    m_ApplicationName));
 
    bool isUpdateAvailable = (localAppImageSHA1Hash != remoteTargetFileSHA1Hash);
 
 
    if(isUpdateAvailable) {
        if(torrentSupported) {
            bool permission = true;
            if(!noConfirmTorrentUsage) {
                QMessageBox box(m_UpdaterDialog.data());
                box.setWindowTitle(QString::fromUtf8("Torrent Usage Permission"));
                box.setIcon(QMessageBox::Information);
                box.setText(
                    QString::fromUtf8(
                        "It seems that the author of ") +
                    (m_ApplicationName.isEmpty() ? QFileInfo(localAppImagePath).baseName() : 
		     m_ApplicationName) +
                    QString::fromUtf8(" supports decentralized update via Bittorrent.") +
                    QString::fromUtf8(
                        " Do you agree to <b>use Bittorrent for decentralized update?</b> This is completely optional.") +
                    QString::fromUtf8(
                        " <b>Please click 'No' if you don't understand the question</b>.")
                );
                box.addButton(QMessageBox::Yes);
                box.addButton(QMessageBox::No);
                permission = (box.exec() == QMessageBox::Yes);
 
            }
 
            if(!permission) {
                m_ControlFileParser->setUseBittorrent(false);
            }
        }
        if(showUpdateDialog) {
            QString oldSha = localAppImageSHA1Hash,
                    newSha = remoteTargetFileSHA1Hash;
            /* we don't need the full sha to show the diff. */
            oldSha.resize(7);
            newSha.resize(7);
 
            oldSha = oldSha.toLower();
            newSha = newSha.toLower();
 
            m_ConfirmationDialog->init( m_ApplicationName, oldSha, newSha, notes);
        } else {
            // Auto confirm if we are not showing the confirm dialog.
            doGUIUpdate();
        }
    } else {
        if(showNoUpdateDialog) {
            QMessageBox box(m_UpdaterDialog.data());
            box.setWindowTitle(QString::fromUtf8("No Updates Available!"));
            box.setText(
                QString::fromUtf8("You are currently using the lastest version of ") +\
		(m_ApplicationName.isEmpty() ? QFileInfo(localAppImagePath).fileName()
		 : m_ApplicationName ) +
                QString::fromUtf8("."));
            box.exec();
        }
 
        QJsonObject result {
            {"OldVersionPath", localAppImagePath},
            {"NewVersionPath", localAppImagePath},
            {"NewVersionSha1Hash", localAppImageSHA1Hash},
            {"UsedTorrent", false}
        };
        b_Started = b_Running = false;
        b_Finished = true;
        b_Canceled = false;
 
        emit finished(result, n_CurrentAction);
    }
 
}
 
 
 
void QAppImageUpdatePrivate::handleGUIUpdateCheckError(short ecode) {
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
 
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getUpdateCheckInformation(void)));
    disconnect(m_ControlFileParser.data(), SIGNAL(updateCheckInformation(QJsonObject)),
               this, SLOT(handleGUIUpdateCheck(QJsonObject)));
    disconnect(m_ControlFileParser.data(), SIGNAL(progress(int)),
               this, SLOT(handleGUIUpdateCheckProgress(int)));
    disconnect(m_ControlFileParser.data(), SIGNAL(error(short)),
               this, SLOT(handleGUIUpdateCheckError(short)));
    disconnect(m_UpdateInformation.data(), SIGNAL(error(short)),
               this, SLOT(handleGUIUpdateCheckError(short)));
 
    if(b_CancelRequested) {
        b_Started = b_Running = b_Finished = false;
        b_Canceled = false;
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
    bool show = n_GuiFlag & GuiFlag::ShowErrorDialog;
 
 
    QString errorString = QAppImageUpdatePrivate::errorCodeToDescriptionString(ecode);
    if(ecode == QAppImageUpdateEnums::Error::NoReadPermission ||
            ecode == QAppImageUpdateEnums::Error::NoPermissionToReadSourceFile ||
            ecode == QAppImageUpdateEnums::Error::NoPermissionToReadWriteTargetFile) {
        if(n_GuiFlag & GuiFlag::NoShowErrorDialogOnPermissionErrors) {
            show = false;
        }
    }
 
    if(show) {
        QMessageBox box(m_UpdaterDialog.data());
        box.setWindowTitle(QString::fromUtf8("Update Failed"));
        box.setIcon(QMessageBox::Critical);
        box.setText(QString::fromUtf8("Update failed for '") +
                    QFileInfo(m_CurrentAppImagePath).fileName() +
                    QString::fromUtf8("': ") + errorString);
        box.exec();
    }
    
    m_UpdaterDialog->hide(); 
    emit error(ecode, n_CurrentAction);
    return;
}
 
void QAppImageUpdatePrivate::handleGUIUpdateCheckProgress(int percentage) {
	(m_Ui->updateCheckProgressBar)->setValue(percentage);
	return;
}
 
void QAppImageUpdatePrivate::handleGUIUpdateProgress(int percentage,
        qint64 bytesReceived,
        qint64 bytesTotal,
        double speed,
        QString units) {
    //// Show that we are canceling if cancel was requested.
    if(b_CancelRequested) {
        (m_Ui->updateSpeedLbl)->setText(QString::fromUtf8("Rolling back changes, Please wait... "));
        return;
    }
 
    (m_Ui->progressBar)->setValue(percentage);
    double MegaBytesReceived = bytesReceived / 1048576;
    double MegaBytesTotal = bytesTotal / 1048576;
    const QString progressTemplate = QString::fromUtf8("Updating %1 MiB of %2 MiB at %3 %4...");
    QString statusText = progressTemplate.arg(MegaBytesReceived).arg(MegaBytesTotal).arg(speed).arg(units);
    (m_Ui->updateSpeedLbl)->setText(statusText);
}
 
 
void QAppImageUpdatePrivate::handleGUIUpdateStart() {
    emit started(n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleGUIUpdateCancel() {
    m_UpdaterDialog->hide();
 
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::rejected,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationRejected);
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::accepted,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationAccepted);
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
 
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleGUIUpdateFinished);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleGUIUpdateProgress);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleGUIUpdateCancel);
 
    b_Started = b_Running = b_Finished = b_CancelRequested = false;
    b_Canceled = true;
 
    emit canceled(n_CurrentAction);
}
 
void QAppImageUpdatePrivate::handleGUIUpdateError(short ecode) {
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::rejected,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationRejected);
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::accepted,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationAccepted);
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
 
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleGUIUpdateFinished);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleGUIUpdateProgress);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleGUIUpdateCancel);
 
 
    b_Started = b_Running = b_Finished = false;
    b_Canceled = false;
 
    if(b_CancelRequested) {
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
    bool show = n_GuiFlag & GuiFlag::ShowErrorDialog;
 
 
    QString errorString = QAppImageUpdatePrivate::errorCodeToDescriptionString(ecode);
    if(ecode == QAppImageUpdateEnums::Error::NoReadPermission ||
            ecode == QAppImageUpdateEnums::Error::NoPermissionToReadSourceFile ||
            ecode == QAppImageUpdateEnums::Error::NoPermissionToReadWriteTargetFile) {
        if(n_GuiFlag & GuiFlag::NoShowErrorDialogOnPermissionErrors) {
            show = false;
        }
    }
 
    if(show) {
        QMessageBox box(m_UpdaterDialog.data());
        box.setWindowTitle(QString::fromUtf8("Update Failed"));
        box.setIcon(QMessageBox::Critical);
        box.setText(QString::fromUtf8("Update failed for '") +
                    (m_ApplicationName.isEmpty() ? QFileInfo(m_CurrentAppImagePath).fileName() : m_ApplicationName) +
                    QString::fromUtf8("': ") + errorString);
        box.exec();
    }
 
 
    emit error(ecode, n_CurrentAction);
    m_UpdaterDialog->hide();
    return;
}
 
void QAppImageUpdatePrivate::handleGUIUpdateFinished(QJsonObject info, QString oldVersionPath) {
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::rejected,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationRejected);
    disconnect(m_ConfirmationDialog.data(), &SoftwareUpdateDialog::accepted,
               this, &QAppImageUpdatePrivate::handleGUIConfirmationAccepted);
 
    disconnect(m_UpdateInformation.data(), SIGNAL(info(QJsonObject)),
               m_ControlFileParser.data(), SLOT(setControlFileUrl(QJsonObject)));
 
    disconnect(m_ControlFileParser.data(), SIGNAL(receiveControlFile(void)),
               m_ControlFileParser.data(), SLOT(getZsyncInformation(void)));
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::zsyncInformation,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::setConfiguration);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finishedConfiguring,
               m_DeltaWriter.data(), &ZsyncWriterPrivate::start);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::finished,
               this, &QAppImageUpdatePrivate::handleGUIUpdateFinished);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_UpdateInformation.data(), &AppImageUpdateInformationPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_ControlFileParser.data(), &ZsyncRemoteControlFileParserPrivate::error,
               this, &QAppImageUpdatePrivate::handleGUIUpdateError);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::progress,
               this, &QAppImageUpdatePrivate::handleGUIUpdateProgress);
 
    disconnect(m_DeltaWriter.data(), &ZsyncWriterPrivate::canceled,
               this, &QAppImageUpdatePrivate::handleGUIUpdateCancel);
 
 
 
    QJsonObject result {
        {"OldVersionPath", oldVersionPath},
        {"NewVersionPath", info["AbsolutePath"].toString()},
        {"NewVersionSha1Hash", info["Sha1Hash"].toString()},
        {"UsedTorrent", info["UsedTorrent"].toBool()}
    };
    b_Started = b_Running = false;
    b_Finished = true;
    b_Canceled = false;
 
    if(b_CancelRequested) {
        QFile::remove(info["AbsolutePath"].toString());
 
        b_CancelRequested = false;
        b_Canceled = true;
        emit canceled(n_CurrentAction);
        return;
    }
 
    (m_Ui->updateSpeedLbl)->setText(QString::fromUtf8("Finalizing Update... "));
 
    bool execute = false;
    bool show = n_GuiFlag & GuiFlag::ShowFinishedDialog;
 
    if(show) {
        auto curinfo = QFileInfo(oldVersionPath);
        QString currentAppImageName = curinfo.fileName();
        QString currentAppImageAbsPath = curinfo.absolutePath();
 
        QMessageBox box(m_UpdaterDialog.data());
        box.setWindowTitle(QString::fromUtf8("Update Completed"));
        box.setIcon(QMessageBox::Information);
        box.setDetailedText(
            QString::fromUtf8("Old version is at: ") +
            currentAppImageAbsPath + QString::fromUtf8("/") + currentAppImageName +
            QString::fromUtf8("\n\n") +
            QString::fromUtf8("New version is saved at: ") +
            result["NewVersionPath"].toString());
        box.setText(QString::fromUtf8("Update completed successfully for <b>") +
                    (m_ApplicationName.isEmpty() ? currentAppImageName : m_ApplicationName) +
                    QString::fromUtf8("</b>, do you want to launch the new version? View details for more information."));
        box.addButton(QMessageBox::Yes);
        box.addButton(QMessageBox::No);
        execute = (box.exec() == QMessageBox::Yes);
    }
 
    if(execute) {
        QFileInfo info(result["NewVersionPath"].toString());
        if(!info.isExecutable()) {
            {
                QFile file(result["NewVersionPath"].toString());
                file.setPermissions(QFileDevice::ExeUser |
                                    QFileDevice::ExeOther|
                                    QFileDevice::ExeGroup|
                                    info.permissions());
            }
        }
	
	m_UpdaterDialog->hide();
        QProcess::startDetached(result["NewVersionPath"].toString(), QStringList());
        
	emit quit();
    }
    m_UpdaterDialog->hide();
    emit finished(result, n_CurrentAction);
    return;
}
#endif // NOT NO_GUI
 
//// Static Methods
QString QAppImageUpdatePrivate::errorCodeToString(short errorCode) {
    QString ret = "QAppImageUpdate::Error::";
    switch(errorCode) {
    case QAppImageUpdateEnums::Error::NoAppimagePathGiven:
        ret += "NoAppImagePathGiven";
        break;
    case QAppImageUpdateEnums::Error::AppimageNotReadable:
        ret += "AppImageNotReadable";
        break;
    case QAppImageUpdateEnums::Error::NoReadPermission:
        ret += "NoReadPermission";
        break;
    case QAppImageUpdateEnums::Error::AppimageNotFound:
        ret += "AppimageNotFound";
        break;
    case QAppImageUpdateEnums::Error::CannotOpenAppimage:
        ret += "CannnotOpenAppimage";
        break;
    case QAppImageUpdateEnums::Error::EmptyUpdateInformation:
        ret += "EmptyUpdateInformation";
        break;
    case QAppImageUpdateEnums::Error::InvalidAppimageType:
        ret += "InvalidAppimageType";
        break;
    case QAppImageUpdateEnums::Error::InvalidMagicBytes:
        ret += "InvalidMagicBytes";
        break;
    case QAppImageUpdateEnums::Error::InvalidUpdateInformation:
        ret += "InvalidUpdateInformation";
        break;
    case QAppImageUpdateEnums::Error::NotEnoughMemory:
        ret += "NotEnoughMemory";
        break;
    case QAppImageUpdateEnums::Error::SectionHeaderNotFound:
        ret += "SectionHeaderNotFound";
        break;
    case QAppImageUpdateEnums::Error::UnsupportedElfFormat:
        ret += "UnsupportedElfFormat";
        break;
    case QAppImageUpdateEnums::Error::UnsupportedTransport:
        ret += "UnsupportedTransport";
        break;
    case QAppImageUpdateEnums::Error::UnknownNetworkError:
        ret += "UnknownNetworkError";
        break;
    case QAppImageUpdateEnums::Error::IoReadError:
        ret += "IoReadError";
        break;
    case QAppImageUpdateEnums::Error::ErrorResponseCode:
        ret += "ErrorResponseCode";
        break;
    case QAppImageUpdateEnums::Error::GithubApiRateLimitReached:
        ret += "GithubApiRateLimitReached";
        break;
    case QAppImageUpdateEnums::Error::NoMarkerFoundInControlFile:
        ret += "NoMarkerFoundInControlFile";
        break;
    case QAppImageUpdateEnums::Error::InvalidZsyncHeadersNumber:
        ret += "InvalidZsyncHeadersNumber";
        break;
    case QAppImageUpdateEnums::Error::InvalidZsyncMakeVersion:
        ret += "InvalidZsyncMakeVersion";
        break;
    case QAppImageUpdateEnums::Error::InvalidZsyncTargetFilename:
        ret += "InvalidZsyncTargetFilename";
        break;
    case QAppImageUpdateEnums::Error::InvalidZsyncMtime:
        ret += "InvalidZsyncMtime";
        break;
    case QAppImageUpdateEnums::Error::InvalidZsyncBlocksize:
        ret += "InvalidZsyncBlocksize";
        break;
    case QAppImageUpdateEnums::Error::InvalidTargetFileLength:
        ret += "InvalidTargetFileLength";
        break;
    case QAppImageUpdateEnums::Error::InvalidHashLengthLine:
        ret += "InvalidHashLengthLine";
        break;
    case QAppImageUpdateEnums::Error::InvalidHashLengths:
        ret += "InvalidHashLengths";
        break;
    case QAppImageUpdateEnums::Error::InvalidTargetFileUrl:
        ret += "InvalidTargetFileUrl";
        break;
    case QAppImageUpdateEnums::Error::InvalidTargetFileSha1:
        ret += "InvalidTargetFileSha1";
        break;
    case QAppImageUpdateEnums::Error::ConnectionRefusedError:
        ret += "ConnectionRefusedError";
        break;
    case QAppImageUpdateEnums::Error::RemoteHostClosedError:
        ret += "RemoteHostClosedError";
        break;
    case QAppImageUpdateEnums::Error::HostNotFoundError:
        ret += "HostNotFoundError";
        break;
    case QAppImageUpdateEnums::Error::TimeoutError:
        ret += "TimeoutError";
        break;
    case QAppImageUpdateEnums::Error::OperationCanceledError:
        ret += "OperationCanceledError";
        break;
    case QAppImageUpdateEnums::Error::SslHandshakeFailedError:
        ret += "SslHandshakeFailedError";
        break;
    case QAppImageUpdateEnums::Error::TemporaryNetworkFailureError:
        ret += "TemporaryNetworkFailureError";
        break;
    case QAppImageUpdateEnums::Error::NetworkSessionFailedError:
        ret += "NetworkSessionFailedError";
        break;
    case QAppImageUpdateEnums::Error::BackgroundRequestNotAllowedError:
        ret += "BackgroundRequestNotAllowedError";
        break;
    case QAppImageUpdateEnums::Error::TooManyRedirectsError:
        ret += "TooManyRedirectsError";
        break;
    case QAppImageUpdateEnums::Error::InsecureRedirectError:
        ret += "InsecureRedirectError";
        break;
    case QAppImageUpdateEnums::Error::ProxyConnectionRefusedError:
        ret += "ProxyConnectionRefusedError";
        break;
    case QAppImageUpdateEnums::Error::ProxyConnectionClosedError:
        ret += "ProxyConnectionClosedError";
        break;
    case QAppImageUpdateEnums::Error::ProxyNotFoundError:
        ret += "ProxyNotFoundError";
        break;
    case QAppImageUpdateEnums::Error::ProxyTimeoutError:
        ret += "ProxyTimeoutError";
        break;
    case QAppImageUpdateEnums::Error::ProxyAuthenticationRequiredError:
        ret += "ProxyAuthenticationRequiredError";
        break;
    case QAppImageUpdateEnums::Error::ContentAccessDenied:
        ret += "ContentAccessDenied";
        break;
    case QAppImageUpdateEnums::Error::ContentOperationNotPermittedError:
        ret += "ContentOperationNotPermittedError";
        break;
    case QAppImageUpdateEnums::Error::ContentNotFoundError:
        ret += "ContentNotFoundError";
        break;
    case QAppImageUpdateEnums::Error::AuthenticationRequiredError:
        ret += "AuthenticationRequiredError";
        break;
    case QAppImageUpdateEnums::Error::ContentReSendError:
        ret += "ContentReSendError";
        break;
    case QAppImageUpdateEnums::Error::ContentConflictError:
        ret += "ContentConflictError";
        break;
    case QAppImageUpdateEnums::Error::ContentGoneError:
        ret += "ContentGoneError";
        break;
    case QAppImageUpdateEnums::Error::InternalServerError:
        ret += "InternalServerError";
        break;
    case QAppImageUpdateEnums::Error::OperationNotImplementedError:
        ret += "OperationNotImplementedError";
        break;
    case QAppImageUpdateEnums::Error::ServiceUnavailableError:
        ret += "ServiceUnavailableError";
        break;
    case QAppImageUpdateEnums::Error::ProtocolUnknownError:
        ret += "ProtocolUnknownError";
        break;
    case QAppImageUpdateEnums::Error::ProtocolInvalidOperationError:
        ret += "ProtocolInvalidOperationError";
        break;
    case QAppImageUpdateEnums::Error::UnknownProxyError:
        ret += "UnknownProxyError";
        break;
    case QAppImageUpdateEnums::Error::UnknownContentError:
        ret += "UnknownContentError";
        break;
    case QAppImageUpdateEnums::Error::ProtocolFailure:
        ret += "ProtocolFailure";
        break;
    case QAppImageUpdateEnums::Error::UnknownServerError:
        ret += "UnknownServerError";
        break;
    case QAppImageUpdateEnums::Error::ZsyncControlFileNotFound:
        ret += "ZsyncControlFileNotFound";
        break;
    case QAppImageUpdateEnums::Error::HashTableNotAllocated:
        ret += "HashTableNotAllocated";
        break;
    case QAppImageUpdateEnums::Error::InvalidTargetFileChecksumBlocks:
        ret += "InvalidTargetFileChecksumBlocks";
        break;
    case QAppImageUpdateEnums::Error::CannotConstructHashTable:
        ret += "CannotConstructHashTable";
        break;
    case QAppImageUpdateEnums::Error::CannotOpenTargetFileChecksumBlocks:
        ret += "CannotOpenTargetFileChecksumBlocks";
        break;
    case QAppImageUpdateEnums::Error::QbufferIoReadError:
        ret += "QbufferIoReadError";
        break;
    case QAppImageUpdateEnums::Error::SourceFileNotFound:
        ret += "SourceFileNotFound";
        break;
    case QAppImageUpdateEnums::Error::NoPermissionToReadSourceFile:
        ret += "NoPermissionToReadSourceFile";
        break;
    case QAppImageUpdateEnums::Error::CannotOpenSourceFile:
        ret += "CannotOpenSourceFile";
        break;
    case QAppImageUpdateEnums::Error::NoPermissionToReadWriteTargetFile:
        ret += "NoPermissionToReadWriteTargetFile";
        break;
    case QAppImageUpdateEnums::Error::CannotOpenTargetFile:
        ret += "CannotOpenTargetFile";
        break;
    case QAppImageUpdateEnums::Error::TargetFileSha1HashMismatch:
        ret += "TargetFileSha1HashMismatch";
        break;
    case QAppImageUpdateEnums::Error::TorrentNotSupported:
	ret += "TorrentNotSupported";
	break;
    case QAppImageUpdateEnums::Error::TorrentSeedFailed:
	ret += "TorrentSeedFailed";
	break;
    case QAppImageUpdateEnums::Error::OutdatedAppImageForSeed:
	ret += "OutdatedAppImageForSeed";
	break;
    case QAppImageUpdateEnums::Error::IncompleteAppImageForSeed:
	ret += "IncompleteAppImageForSeed";
	break;
    case QAppImageUpdateEnums::Error::UnsupportedActionForBuild:
        ret += "UnsupportedActionForBuild";
        break;
    case QAppImageUpdateEnums::Error::InvalidAction:
        ret += "InvalidAction";
        break;
    default:
        ret += "Unknown";
        break;
    }
    return ret;
}
 
QString QAppImageUpdatePrivate::errorCodeToDescriptionString(short errorCode) {
    QString errorString;
    switch(errorCode) {
    case QAppImageUpdateEnums::Error::ConnectionRefusedError:
        errorString = QString::fromUtf8("The update server is not accepting requests.");
        break;
    case QAppImageUpdateEnums::Error::RemoteHostClosedError:
        errorString = QString::fromUtf8("The remote server closed the connection prematurely, ");
        errorString += QString::fromUtf8("before the entire reply was received and processed.");
        break;
    case QAppImageUpdateEnums::Error::HostNotFoundError:
        errorString = QString::fromUtf8("The remote host name was not found (invalid hostname).");
        break;
    case QAppImageUpdateEnums::Error::TimeoutError:
        errorString = QString::fromUtf8("The connection to the remote server timed out.");
        break;
    case QAppImageUpdateEnums::Error::SslHandshakeFailedError:
        errorString = QString::fromUtf8("The SSL/TLS handshake failed and the encrypted channel ");
        errorString += QString::fromUtf8("could not be established.");
        break;
    case QAppImageUpdateEnums::Error::TemporaryNetworkFailureError:
        errorString = QString::fromUtf8("The connection to the network was broken.");
        break;
    case QAppImageUpdateEnums::Error::NetworkSessionFailedError:
        errorString = QString::fromUtf8("The connection to the network was broken ");
        errorString += QString::fromUtf8("or could not be initiated.");
        break;
    case QAppImageUpdateEnums::Error::BackgroundRequestNotAllowedError:
        errorString = QString::fromUtf8("The background request is not currently allowed due to platform policy.");
        break;
    case QAppImageUpdateEnums::Error::TooManyRedirectsError:
        errorString = QString::fromUtf8("While following redirects, the maximum limit was reached.");
        break;
    case QAppImageUpdateEnums::Error::InsecureRedirectError:
        errorString = QString::fromUtf8("While following redirects, there was a redirect ");
        errorString += QString::fromUtf8("from a encrypted protocol (https) to an unencrypted one (http).");
        break;
    case QAppImageUpdateEnums::Error::ContentAccessDenied:
        errorString = QString::fromUtf8("The access to the remote content was denied (HTTP error 403).");
        break;
    case QAppImageUpdateEnums::Error::ContentOperationNotPermittedError:
        errorString = QString::fromUtf8("The operation requested on the remote content is not permitted.");
        break;
    case QAppImageUpdateEnums::Error::ContentNotFoundError:
        errorString = QString::fromUtf8("The remote content was not found at the server (HTTP error 404)");
        break;
    case QAppImageUpdateEnums::Error::AuthenticationRequiredError:
        errorString = QString::fromUtf8("The remote server requires authentication to serve the content, ");
        errorString += QString::fromUtf8("but the credentials provided were not accepted or given.");
        break;
    case QAppImageUpdateEnums::Error::ContentConflictError:
        errorString = QString::fromUtf8("The request could not be completed due to a conflict with the ");
        errorString += QString::fromUtf8("current state of the resource.");
        break;
    case QAppImageUpdateEnums::Error::ContentGoneError:
        errorString = QString::fromUtf8("The requested resource is no longer available at the server.");
        break;
    case QAppImageUpdateEnums::Error::InternalServerError:
        errorString = QString::fromUtf8("The server encountered an unexpected condition which prevented ");
        errorString += QString::fromUtf8("it from fulfilling the request.");
        break;
    case QAppImageUpdateEnums::Error::OperationNotImplementedError:
        errorString = QString::fromUtf8("The server does not support the functionality required to fulfill the request.");
        break;
    case QAppImageUpdateEnums::Error::ServiceUnavailableError:
        errorString = QString::fromUtf8("The server is unable to handle the request at this time.");
        break;
    case QAppImageUpdateEnums::Error::ProtocolUnknownError:
        errorString = QString::fromUtf8("The Network Access API cannot honor the request because the protocol");
        errorString += QString::fromUtf8(" is not known.");
        break;
    case QAppImageUpdateEnums::Error::ProtocolInvalidOperationError:
        errorString = QString::fromUtf8("The requested operation is invalid for this protocol.");
        break;
    case QAppImageUpdateEnums::Error::UnknownNetworkError:
        errorString = QString::fromUtf8("An unknown network-related error was detected.");
        break;
    case QAppImageUpdateEnums::Error::UnknownContentError:
        errorString = QString::fromUtf8("An unknown error related to the remote content was detected.");
        break;
    case QAppImageUpdateEnums::Error::ProtocolFailure:
        errorString = QString::fromUtf8("A breakdown in protocol was detected ");
        errorString += QString::fromUtf8("(parsing error, invalid or unexpected responses, etc.)");
        break;
    case QAppImageUpdateEnums::Error::UnknownServerError:
        errorString = QString::fromUtf8("An unknown error related to the server response was detected.");
        break;
    case QAppImageUpdateEnums::Error::NoAppimagePathGiven:
        errorString = QString::fromUtf8("No AppImage given.");
        break;
    case QAppImageUpdateEnums::Error::AppimageNotReadable:
        errorString = QString::fromUtf8("The AppImage is not readable.");
        break;
    case QAppImageUpdateEnums::Error::NoReadPermission:
        errorString = QString::fromUtf8("You don't have the permission to read the AppImage.");
        break;
    case QAppImageUpdateEnums::Error::AppimageNotFound:
        errorString = QString::fromUtf8("The AppImage does not exist.");
        break;
    case QAppImageUpdateEnums::Error::CannotOpenAppimage:
        errorString = QString::fromUtf8("The AppImage cannot be opened.");
        break;
    case QAppImageUpdateEnums::Error::EmptyUpdateInformation:
        errorString = QString::fromUtf8("The AppImage does not include any update information.");
        break;
    case QAppImageUpdateEnums::Error::InvalidAppimageType:
        errorString = QString::fromUtf8("The AppImage has an unknown type.");
        break;
    case QAppImageUpdateEnums::Error::InvalidMagicBytes:
        errorString = QString::fromUtf8("The AppImage is not valid.");
        break;
    case QAppImageUpdateEnums::Error::InvalidUpdateInformation:
        errorString = QString::fromUtf8("The AppImage has invalid update information.");
        break;
    case QAppImageUpdateEnums::Error::NotEnoughMemory:
        errorString = QString::fromUtf8("Not enough memory.");
        break;
    case QAppImageUpdateEnums::Error::SectionHeaderNotFound:
        errorString = QString::fromUtf8("The AppImage does not contain update information ");
        errorString += QString::fromUtf8("at a valid section header.");
        break;
    case QAppImageUpdateEnums::Error::UnsupportedElfFormat:
        errorString = QString::fromUtf8("The AppImage is not in supported ELF format.");
        break;
    case QAppImageUpdateEnums::Error::UnsupportedTransport:
        errorString = QString::fromUtf8("The AppImage specifies an unsupported update transport.");
        break;
    case QAppImageUpdateEnums::Error::IoReadError:
        errorString = QString::fromUtf8("Unknown IO read error.");
        break;
    case QAppImageUpdateEnums::Error::GithubApiRateLimitReached:
        errorString = QString::fromUtf8("GitHub API rate limit reached, please try again later.");
        break;
    case QAppImageUpdateEnums::Error::ErrorResponseCode:
        errorString = QString::fromUtf8("Bad response from the server, please try again later.");
        break;
    case QAppImageUpdateEnums::Error::NoMarkerFoundInControlFile:
    case QAppImageUpdateEnums::Error::InvalidZsyncHeadersNumber:
    case QAppImageUpdateEnums::Error::InvalidZsyncMakeVersion:
    case QAppImageUpdateEnums::Error::InvalidZsyncTargetFilename:
    case QAppImageUpdateEnums::Error::InvalidZsyncMtime:
    case QAppImageUpdateEnums::Error::InvalidZsyncBlocksize:
    case QAppImageUpdateEnums::Error::InvalidTargetFileLength:
    case QAppImageUpdateEnums::Error::InvalidHashLengthLine:
    case QAppImageUpdateEnums::Error::InvalidHashLengths:
    case QAppImageUpdateEnums::Error::InvalidTargetFileUrl:
    case QAppImageUpdateEnums::Error::InvalidTargetFileSha1:
    case QAppImageUpdateEnums::Error::HashTableNotAllocated:
    case QAppImageUpdateEnums::Error::InvalidTargetFileChecksumBlocks:
    case QAppImageUpdateEnums::Error::CannotOpenTargetFileChecksumBlocks:
    case QAppImageUpdateEnums::Error::CannotConstructHashTable:
    case QAppImageUpdateEnums::Error::QbufferIoReadError:
        errorString = QString::fromUtf8("Invalid zsync meta file.");
        break;
    case QAppImageUpdateEnums::Error::ZsyncControlFileNotFound:
        errorString = QString::fromUtf8("The zsync control file was not found in the specified location.");
        break;
    case QAppImageUpdateEnums::Error::SourceFileNotFound:
        errorString = QString::fromUtf8("The current AppImage could not be found, maybe it was deleted while updating?");
        break;
    case QAppImageUpdateEnums::Error::NoPermissionToReadSourceFile:
        errorString = QString::fromUtf8("You don't have the permission to read the current AppImage.");
        break;
    case QAppImageUpdateEnums::Error::CannotOpenSourceFile:
        errorString = QString::fromUtf8("The current AppImage cannot be opened.");
        break;
    case QAppImageUpdateEnums::Error::NoPermissionToReadWriteTargetFile:
        errorString = QString::fromUtf8("You have no write or read permissions for the new version.");
        break;
    case QAppImageUpdateEnums::Error::CannotOpenTargetFile:
        errorString = QString::fromUtf8("The new version cannot be opened to write or read.");
        break;
    case QAppImageUpdateEnums::Error::TargetFileSha1HashMismatch:
        errorString = QString::fromUtf8("The newly constructed AppImage failed the integrity check, please try again.");
        break;
    case QAppImageUpdateEnums::Error::TorrentNotSupported:
	errorString = QString::fromUtf8("The AppImage author does not support decentralized update.");
	break;
    case QAppImageUpdateEnums::Error::TorrentSeedFailed:
	errorString = QString::fromUtf8("The AppImage cannot be seeded.");
	break;
    case QAppImageUpdateEnums::Error::OutdatedAppImageForSeed:
	errorString = QString::fromUtf8("The AppImage is not the newest available for seeding.");
	break;
    case QAppImageUpdateEnums::Error::IncompleteAppImageForSeed:
	errorString = QString::fromUtf8("The AppImage is incomplete for seeding.");
	break;
    case QAppImageUpdateEnums::Error::UnsupportedActionForBuild:
        errorString = QString::fromUtf8("The current build of the core library does not support the requested action.");
        break;
    case QAppImageUpdateEnums::Error::InvalidAction:
        errorString = QString::fromUtf8("The requested action is invalid.");
        break;
    default:
        errorString = QString::fromUtf8("Unknown error.");
        break;
    }
    return errorString;
}
 
QString QAppImageUpdatePrivate::versionString() {
    return QString::fromUtf8("2.0.2");
}

V1048 The 'b_Running' variable was assigned the same value.

V1048 The 'b_Started' variable was assigned the same value.

V1048 The 'b_Canceled' variable was assigned the same value.

V519 The 'b_Canceled' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 962, 964.

V519 The 'b_Canceled' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1085, 1087.

V636 The 'bytesReceived / 1048576' expression was implicitly cast from 'long long' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.

V636 The 'bytesTotal / 1048576' expression was implicitly cast from 'long long' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.