#ifdef DECENTRALIZED_UPDATE_ENABLED
#include <QFileInfo>
#include <QDebug>
#include <QThread>
#include <QCoreApplication>
#include <QNetworkProxy>
#include <vector>
#include <iostream>
 
#include "torrentdownloader_p.hpp"
 
TorrentDownloaderPrivate::TorrentDownloaderPrivate(QNetworkAccessManager *manager)
    : QObject() {
    n_TargetFileLength = n_TargetFileDone = 0;
    lt::session_params p = lt::session_params();
    p.settings.set_int(lt::settings_pack::alert_mask,
                       lt::alert_category::status |
                       lt::alert_category::error |
                       lt::alert_category::storage);
    
    //// Set proxy for libtorrent.
    auto proxy = manager->proxy();
    if(proxy.type() != QNetworkProxy::NoProxy) {
	emit logger("Using proxy for torrent download");
	p.settings.set_str(lt::settings_pack::proxy_hostname,
			   proxy.hostName().toStdString());
	p.settings.set_int(lt::settings_pack::proxy_port,
			   (int)proxy.port());
	p.settings.set_str(lt::settings_pack::proxy_username,
			   proxy.user().toStdString());
	p.settings.set_str(lt::settings_pack::proxy_password,
			   proxy.password().toStdString());
	
	/// Set Proxy type.
	if(proxy.type() == QNetworkProxy::Socks5Proxy) {
		p.settings.set_int(lt::settings_pack::proxy_type,
				   lt::settings_pack::socks5_pw);
	}else if(proxy.type() == QNetworkProxy::HttpProxy) {
		p.settings.set_int(lt::settings_pack::proxy_type,
				   lt::settings_pack::http_pw);
	}else{
		emit logger("Cannot find proxy type. You have been warned");
	}
    }
 
    m_Manager = manager;
    m_Session.reset(new lt::session(p));
    m_TorrentMeta.reset(new QByteArray);
 
    m_TimeoutTimer.setSingleShot(true);
    m_TimeoutTimer.setInterval(100 * 1000); // 100 seconds
 
    connect(&m_TimeoutTimer, &QTimer::timeout,
            this, &TorrentDownloaderPrivate::handleTimeout,
            Qt::QueuedConnection);
 
    m_Timer.setSingleShot(false);
    m_Timer.setInterval(100); // 1ms?
    connect(&m_Timer, &QTimer::timeout,
            this, &TorrentDownloaderPrivate::torrentLoop,
            Qt::QueuedConnection);
}
TorrentDownloaderPrivate::~TorrentDownloaderPrivate() {
	m_Session->abort();
}
 
void TorrentDownloaderPrivate::setTargetFileDone(qint64 done) {
    if(b_Running) {
        return;
    }
 
    n_TargetFileDone = done;
}
 
void TorrentDownloaderPrivate::setTargetFileLength(qint64 n) {
    if(b_Running) {
        return;
    }
 
    n_TargetFileLength = n;
}
 
void TorrentDownloaderPrivate::setTargetFile(QTemporaryFile *file) {
    if(b_Running) {
        return;
    }
 
    m_File = file;
}
 
void TorrentDownloaderPrivate::setTorrentFileUrl(const QUrl &url) {
    if(b_Running) {
        return;
    }
    m_TorrentFileUrl = url;
}
 
void TorrentDownloaderPrivate::setTargetFileUrl(const QUrl &url) {
    if(b_Running) {
        return;
    }
    m_TargetFileUrl = url;
 
}
 
void TorrentDownloaderPrivate::start() {
    if(b_Running) {
        return;
    }
    b_Running = b_Finished = false;
 
 
    m_TorrentMeta->clear();
 
    QNetworkRequest request;
    request.setUrl(m_TorrentFileUrl);
    request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
 
    auto reply = m_Manager->get(request);
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
            this, SLOT(handleTorrentFileError(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(downloadProgress(qint64, qint64)),
            this, SLOT(handleTorrentFileData(qint64, qint64)));
    connect(reply, SIGNAL(finished()),
            this, SLOT(handleTorrentFileFinish()));
    b_Running = true;
    emit started();
}
 
void TorrentDownloaderPrivate::cancel() {
    if(!b_Running || b_CancelRequested) {
        return;
    }
    b_CancelRequested = true;
}
 
void TorrentDownloaderPrivate::handleTorrentFileError(QNetworkReply::NetworkError code) {
    QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());
    if(!reply) {
        return;
    }
 
    reply->disconnect();
    reply->deleteLater();
 
    emit error(code);
}
 
void TorrentDownloaderPrivate::handleTorrentFileData(qint64 br, qint64 bt) {
    Q_UNUSED(br);
    Q_UNUSED(bt);
 
    auto reply = qobject_cast<QNetworkReply*>(QObject::sender());
    if(!reply) {
        return;
    }
 
    if(reply->error() != QNetworkReply::NoError) {
        return;
    }
 
    if(reply->isReadable()) {
        m_TorrentMeta->append(reply->readAll());
    }
}
 
void TorrentDownloaderPrivate::handleTorrentFileFinish() {
    auto reply = qobject_cast<QNetworkReply*>(QObject::sender());
    m_TorrentMeta->append(reply->readAll());
 
    reply->disconnect();
    reply->deleteLater();
 
    if(b_CancelRequested) {
        m_File->setAutoRemove(true);
        m_File->open();
        b_CancelRequested = false;
        b_Running = b_Finished = false;
        emit canceled();
        return;
    }
 
    lt::add_torrent_params params;
    QString savePath = QFileInfo(m_File->fileName()).path() + "/";
 
    params.save_path = savePath.toStdString();
    auto ti = std::make_shared<lt::torrent_info>(m_TorrentMeta->constData(), (int)m_TorrentMeta->size());
 
    /// We know that MakeAppImageTorrent only packs a single file that is the
    /// the Target AppImage. So We just need to check if it is bundled correctly.
    if(ti->num_files() != 1) {
        emit error(QNetworkReply::ProtocolFailure);
        return;
    }
 
    /// Since only 1 file is packaged in the torrent, we can
    /// assume that the file index for our Target AppImage is 0
    ti->rename_file(0,
                    QFileInfo(m_File->fileName()).fileName().toStdString());
 
 
    /// Add the target file url as web seed
    /// See BEP 17 and BEP 19
    ti->add_url_seed(m_TargetFileUrl.toString().toStdString());
 
    params.ti = ti;
    m_Handle = m_Session->add_torrent(params);
    if(!m_Handle.is_valid()) {
        emit error(QNetworkReply::ProtocolFailure);
        return;
    }
 
    m_Timer.setSingleShot(false);
    m_Timer.setInterval(100);
    m_Timer.start();
    m_TimeoutTimer.start();
    return;
}
 
void TorrentDownloaderPrivate::handleTimeout() {
    m_TimeoutTimer.stop();
 
    emit logger(QString::fromStdString(" handleTimeout: Torrent Downloader Timeout, falling back to range downloader."));
    m_File->setAutoRemove(true);
    m_File->open();
    m_Session->abort();
    b_Running = false;
    b_Finished = false;
    emit error(QNetworkReply::ProtocolFailure);
}
 
void TorrentDownloaderPrivate::torrentLoop() {
    if(!b_Running) {
        /// To avoid queued calls from being called
        return;
    }
    if(b_CancelRequested) {
        m_TimeoutTimer.stop();
        m_Timer.stop();
        {
	// The destruction of session proxy 
	// assures that all call writes and everything
	// is finished. This is sync.
	auto sess_proxy = m_Session->abort();
	}  
	m_File->setAutoRemove(true);
        m_File->open();
        b_CancelRequested = false;
        b_Running = b_Finished = false;
        emit canceled();
        return;
    }
    auto status = m_Handle.status();
 
    emit torrentStatus(status.num_seeds, status.num_peers);
    if(status.state == lt::torrent_status::seeding) {
        emit progress((int)(status.progress * 100),
                      (qint64)(status.total_done),
                      n_TargetFileLength,
                      (double)(status.download_payload_rate/1024),
                      QString::fromUtf8(" KB/s "));
        m_Timer.stop();
        m_TimeoutTimer.stop();
        {
	// The destruction of session proxy 
	// assures that all call writes and everything
	// is finished. This is sync.
	auto sess_proxy = m_Session->abort();
	}  
	m_File->setAutoRemove(true);
        m_File->open();
        b_Running = false;
        b_Finished = true;
        emit finished();
        return;
 
    }
 
    if(status.state == lt::torrent_status::downloading) {
        m_TimeoutTimer.start(); // Reset timeout timer on every progress in download.
 
        emit progress((int)(status.progress * 100),
                      (qint64)(status.total_done),
                      n_TargetFileLength,
                      (double)(status.download_payload_rate/1024),
                      QString::fromUtf8(" KB/s "));
    }
 
    std::vector<lt::alert*> alerts;
    m_Session->pop_alerts(&alerts);
    for (lt::alert const* a : alerts) {
        if (lt::alert_cast<lt::torrent_error_alert>(a)) {
            emit logger(QString::fromStdString(a->message()));
            m_Timer.stop();
            m_TimeoutTimer.stop();
	    {
		// The destruction of session proxy 
		// assures that all call writes and everything
		// is finished. This is sync.
	    	auto sess_proxy = m_Session->abort();
	    }
       	    m_File->setAutoRemove(true);
            m_File->open();	
            b_Running = false;
            b_Finished = false;
            emit error(QNetworkReply::ProtocolFailure);
            return;
        }
        QCoreApplication::processEvents();
    }
}
 
#endif // DECENTRALIZED_UPDATE_ENABLED

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