/*
    SPDX-FileCopyrightText: 2007, 2009 Ryan P. Bitanga <ryan.bitanga@gmail.com>

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

#include "runnerjobs_p.h"

#include <QTimer>
#include <QMutexLocker>

#include "krunner_debug.h"
#include "runnermanager.h"
#include "querymatch.h"

using ThreadWeaver::Job;
using ThreadWeaver::Queue;

namespace Plasma {

DelayedRunnerPolicy::DelayedRunnerPolicy()
    : QueuePolicy()
{}

DelayedRunnerPolicy::~DelayedRunnerPolicy()
{}

DelayedRunnerPolicy& DelayedRunnerPolicy::instance()
{
    static DelayedRunnerPolicy policy;
    return policy;
}

bool DelayedRunnerPolicy::canRun(ThreadWeaver::JobPointer job)
{
    QSharedPointer<FindMatchesJob> aJob(job.dynamicCast<FindMatchesJob>());
    if (QTimer *t = aJob->delayTimer()) {
        // If the timer is active, the required delay has not been reached
        return !t->isActive(); // DATA RACE!  (with QTimer start/stop from runnermanager.cpp)
    }

    return true;
}

void DelayedRunnerPolicy::free(ThreadWeaver::JobPointer job)
{
    Q_UNUSED(job)
}

void DelayedRunnerPolicy::release(ThreadWeaver::JobPointer job)
{
    free(job);
}

void DelayedRunnerPolicy::destructed(ThreadWeaver::JobInterface* job)
{
    Q_UNUSED(job)
}

DefaultRunnerPolicy::DefaultRunnerPolicy()
    : QueuePolicy(),
      m_cap(2)
{}

DefaultRunnerPolicy::~DefaultRunnerPolicy()
{}

DefaultRunnerPolicy& DefaultRunnerPolicy::instance()
{
    static DefaultRunnerPolicy policy;
    return policy;
}

bool DefaultRunnerPolicy::canRun(ThreadWeaver::JobPointer job)
{
    Plasma::AbstractRunner *runner = job.dynamicCast<FindMatchesJob>()->runner();
    QMutexLocker l(&m_mutex);

    if (m_runCounts[runner->name()] > m_cap) {
        return false;
    } else {
        ++m_runCounts[runner->name()];
        return true;
    }
}

void DefaultRunnerPolicy::free(ThreadWeaver::JobPointer job)
{
    Plasma::AbstractRunner *runner = job.dynamicCast<FindMatchesJob>()->runner();
    QMutexLocker l(&m_mutex);

    --m_runCounts[runner->name()];
}

void DefaultRunnerPolicy::release(ThreadWeaver::JobPointer job)
{
    free(job);
}

void DefaultRunnerPolicy::destructed(ThreadWeaver::JobInterface* job)
{
    Q_UNUSED(job)
}

////////////////////
// Jobs
////////////////////

FindMatchesJob::FindMatchesJob(Plasma::AbstractRunner *runner,
                               Plasma::RunnerContext *context, QObject *)
    : ThreadWeaver::Job(),
      m_context(*context, nullptr),
      m_runner(runner),
      m_timer(nullptr)
{
    QMutexLocker l(mutex()); Q_UNUSED(l);
    if (runner->speed() == Plasma::AbstractRunner::SlowSpeed) {
        assignQueuePolicy(&DelayedRunnerPolicy::instance());
    } else {
        assignQueuePolicy(&DefaultRunnerPolicy::instance());
    }
}

FindMatchesJob::~FindMatchesJob()
{
}

QTimer* FindMatchesJob::delayTimer() const
{
    return m_timer;
}

void FindMatchesJob::setDelayTimer(QTimer *timer)
{
    m_timer = timer;
}

void FindMatchesJob::run(ThreadWeaver::JobPointer self, ThreadWeaver::Thread*)
{
    if (m_context.isValid()) {
        m_runner->performMatch(m_context);
    }
    Q_EMIT done(self);
}

int FindMatchesJob::priority() const
{
    return m_runner->priority();
}

Plasma::AbstractRunner* FindMatchesJob::runner() const
{
    return m_runner;
}

DelayedJobCleaner::DelayedJobCleaner(const QSet<QSharedPointer<FindMatchesJob> > &jobs, const QSet<AbstractRunner *> &runners)
    : QObject(Queue::instance()),
      m_weaver(Queue::instance()),
      m_jobs(jobs),
      m_runners(runners)
{
    connect(m_weaver, &ThreadWeaver::QueueSignals::finished, this, &DelayedJobCleaner::checkIfFinished);

    for (auto it = m_jobs.constBegin(); it != m_jobs.constEnd(); ++it) {
        connect(it->data(), &FindMatchesJob::done, this, &DelayedJobCleaner::jobDone);
    }
}

DelayedJobCleaner::~DelayedJobCleaner()
{
    qDeleteAll(m_runners);
}

void DelayedJobCleaner::jobDone(ThreadWeaver::JobPointer job)
{
    auto runJob = job.dynamicCast<FindMatchesJob>();

    if (!runJob) {
        return;
    }

    m_jobs.remove(runJob);

    if (m_jobs.isEmpty()) {
        deleteLater();
    }
}

void DelayedJobCleaner::checkIfFinished()
{
    if (m_weaver->isIdle()) {
        m_jobs.clear();
        deleteLater();
    }
}


} // Plasma namespace

