#include "executor.h"
#include "machine.h"
#include <QTimer>
#include <QThread>

Executor::Executor(Machine* m)
    : QObject(nullptr), machine(m), timer(nullptr)
{
}

Executor::~Executor()
{
    if (timer) {
        timer->stop();
        delete timer;
        timer = nullptr;
    }
}

bool Executor::hasEnded() const {
    return ended;
}

void Executor::resetProgram() {
    // resetiraj stanje, poruši izvajanje in spravi lučko v stopped
    ended = false;
    running = false;
    stepping = false;
    if (timer) timer->stop();
    emit signalStopped();
    emit updateRequested();
}

// START: pripravimo timer (v threadu, kjer je ta objekt) in ga zaženemo
void Executor::start() {
    // če je končano, pošljemo ended in ne začnemo
    if (ended) {
        emit signalEnded();
        return;
    }

    // če že tečemo, nič
    if (running) return;

    running = true;
    emit signalStarted();

    // timer naj bo kreiran v končni niti (start() bo klican v executorThread preko queued connection)
    if (!timer) {
        timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &Executor::runStep, Qt::DirectConnection);
    }

    // zaženi timer z intervalom
    timer->start(intervalMs);
}

// STOP: takoj ustavimo timer in sporočimo stopped
void Executor::stop() {
    if (!running && !timer) {
        emit signalStopped();
        return;
    }

    running = false;
    stepping = false;
    if (timer && timer->isActive()) timer->stop();

    emit signalStopped();
    emit updateRequested();
}

// runStep: izvede en ukaz — klican iz timer timeout
void Executor::runStep() {
    if (!running || ended) {
        if (timer && timer->isActive()) timer->stop();
        return;
    }

    // izvedemo en ukaz
    int pc_before = machine->getPC();
    machine->execute();
    int pc_after = machine->getPC();

    // obvestimo UI
    emit updateRequested();

    // preverimo halt / ended
    if (pc_before == pc_after) {
        ended = true;
        // ustavimo timer in pošljemo samo ended (ne stopped)
        if (timer && timer->isActive()) timer->stop();
        emit signalEnded();
        return;
    }

    // Če je uporabnik pritisnil stop med izvajanjem, ustavimo timer
    if (!running) {
        if (timer && timer->isActive()) timer->stop();
        emit signalStopped();
    }
}

// step: en sam korak, le če ne tečemo že v avtomatskem načinu
void Executor::step() {
    if (ended) {
        emit signalEnded();
        return;
    }

    // če je avtomatsko izvajanje v teku, ignoriramo manualni step
    if (running) return;

    // preprečimo reentrance
    if (stepping) return;
    stepping = true;

    int pc_before = machine->getPC();
    machine->execute();
    int pc_after = machine->getPC();

    emit updateRequested();

    if (pc_after == pc_before) {
        ended = true;
        emit signalEnded();
    } else {
        emit signalStopped();
    }

    stepping = false;
}
