/*
    SPDX-FileCopyrightText: 2009 Joris Guisson <joris.guisson@gmail.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "datacheckerjob.h"

#include <KIO/Global>
#include <KLocalizedString>

#include "datacheckerthread.h"
#include "multidatachecker.h"
#include "singledatachecker.h"
#include <torrent/torrentcontrol.h>
#include <util/functions.h>

using namespace Qt::Literals::StringLiterals;

namespace bt
{
static ResourceManager data_checker_slot(1);

DataCheckerJob::DataCheckerJob(bool auto_import, bt::TorrentControl *tc, bt::Uint32 from, bt::Uint32 to)
    : Job(true, tc)
    , Resource(&data_checker_slot, tc->getInfoHash().toString())
    , dcheck_thread(nullptr)
    , killed(false)
    , auto_import(auto_import)
    , started(false)
    , from(from)
    , to(to)
{
    if (this->from >= tc->getStats().total_chunks) {
        this->from = 0;
    }
    if (this->to >= tc->getStats().total_chunks) {
        this->to = tc->getStats().total_chunks - 1;
    }
}

DataCheckerJob::~DataCheckerJob()
{
}

void DataCheckerJob::start()
{
    registerWithTracker();
    std::unique_ptr<DataChecker> dc;
    const TorrentStats &stats = torrent()->getStats();
    if (stats.multi_file_torrent) {
        dc = std::make_unique<MultiDataChecker>(from, to);
    } else {
        dc = std::make_unique<SingleDataChecker>(from, to);
    }

    connect(dc.get(), &DataChecker::progress, this, &DataCheckerJob::progress, Qt::QueuedConnection);
    connect(dc.get(), &DataChecker::status, this, &DataCheckerJob::status, Qt::QueuedConnection);

    const TorrentControl *tor = torrent();
    dcheck_thread = new DataCheckerThread(std::move(dc),
                                          tor->downloadedChunksBitSet(),
                                          stats.output_path,
                                          tor->getTorrent(),
                                          tor->getTorDir() + "dnd"_L1 + bt::DirSeparator());

    connect(dcheck_thread, &DataCheckerThread::finished, this, &DataCheckerJob::threadFinished, Qt::QueuedConnection);

    torrent()->beforeDataCheck();

    setTotalAmount(Bytes, to - from + 1);
    data_checker_slot.add(this);
    if (!started) {
        Q_EMIT infoMessage(this, i18n("Waiting for other data checks to finish"));
    }
}

void DataCheckerJob::acquired()
{
    started = true;
    Q_EMIT description(this, i18n("Checking data"));
    dcheck_thread->start(QThread::IdlePriority);
}

void DataCheckerJob::kill(bool quietly)
{
    killed = true;
    if (dcheck_thread && dcheck_thread->isRunning()) {
        dcheck_thread->getDataChecker()->stop();
        dcheck_thread->wait();
        dcheck_thread->deleteLater();
        dcheck_thread = nullptr;
    }
    bt::Job::kill(quietly);
}

void DataCheckerJob::threadFinished()
{
    if (!killed) {
        const DataChecker *dc = dcheck_thread->getDataChecker();
        torrent()->afterDataCheck(this, dc->getResult());
        if (!dcheck_thread->getError().isEmpty()) {
            setErrorText(dcheck_thread->getError());
            setError(KIO::ERR_UNKNOWN);
        } else {
            setError(0);
        }

        dcheck_thread->deleteLater();
        dcheck_thread = nullptr;
        emitResult();
    } else {
        setError(0);
    }

    release();
}

void DataCheckerJob::progress(quint32 num, quint32 total)
{
    Q_UNUSED(total);
    setProcessedAmount(Bytes, num);
}

void DataCheckerJob::status(quint32 num_failed, quint32 num_found, quint32 num_downloaded, quint32 num_not_downloaded)
{
    const QPair<QString, QString> field1 = qMakePair(QString::number(num_failed), QString::number(num_found));
    const QPair<QString, QString> field2 = qMakePair(QString::number(num_downloaded), QString::number(num_not_downloaded));
    Q_EMIT description(this, i18n("Checking Data"), field1, field2);
}
}
