#include "../headers/scstwtimer.h"

ScStwTimer::ScStwTimer(QObject *parent, bool directControlEnabled) : QObject(parent)
{
    this->directControlEnabled = directControlEnabled;
    this->startTime = 0;
    this->stopTime = 0;
    this->reactionTime = 0;

    this->state = IDLE;
}

bool ScStwTimer::start() {
    return this->start(QDateTime::currentMSecsSinceEpoch());
}

bool ScStwTimer::start(double timeOfStart) {
    switch (this->state) {
    case IDLE: {
        // in case of IDLE, start the race!

        this->startTime = timeOfStart;
        this->stopTime = 0;

        if(timeOfStart - QDateTime::currentMSecsSinceEpoch() > 0) {
            this->setState(STARTING);
            QTimer::singleShot(timeOfStart - QDateTime::currentMSecsSinceEpoch(), [=](){
                if(this->state == STARTING)
                    this->setState(RUNNING);
            });
            QTimer::singleShot(timeOfStart - QDateTime::currentMSecsSinceEpoch() - 1000, [=](){
                //this->handleClimberStart(QDateTime::currentMSecsSinceEpoch());
            });

        }
        else
            this->setState(RUNNING);
        return true;
    }
    default: {
        // otherwise the timer is not supposed to be started!
        return false;
    }
    }
}

void ScStwTimer::handleClimberStart(double timeOfStart) {
    this->reactionTime = timeOfStart - this->startTime;
    qDebug() << "+ [INFO][TIMER] reaction time: " << this->reactionTime;

    if(this->reactionTime <= 0){
        this->stop(FailStop);
        return;
    }

    emit this->reactionTimeChanged();
}

bool ScStwTimer::cancel() {
    if(!(this->state == IDLE || this->state == STARTING || this->state == RUNNING))
        return false;

    this->setState(CANCELLED);
    return true;
}

bool ScStwTimer::stop() {
    return this->stop(QDateTime::currentMSecsSinceEpoch());
}

bool ScStwTimer::stop(double timeOfStop) {
    return this->stop(ManualStop, timeOfStop);
}

bool ScStwTimer::stop(StopReason reason) {
    return this->stop(reason, QDateTime::currentMSecsSinceEpoch());
}

bool ScStwTimer::stop(StopReason reason, double timeOfStop) {
    if(this->state != STARTING && this->state != RUNNING && this->state != WAITING){
        return false;
    }

    switch (reason) {
    case ManualStop: {
        if(this->state == STARTING){
            this->setState(CANCELLED);
        }
        else {
            this->stopTime = timeOfStop;

            // trigger an external state refresh to set the state to either WON or LOST depending on the other timers values (see ScStwRace::refreshTimerStates())
            this->setState(WAITING);
        }
        break;
    }
    case FailStop: {
        qDebug() << "[INFO][TIMER] False Start detected: "  << "start Time: " << startTime << " reactionTime: " << reactionTime;
        this->setState(FAILED);
        break;
    }
    default: {
        return false;
    }
    }

    qDebug() << "[INFO][TIMER] Stopped: " << "start Time: " << startTime << " stopTime: " << stopTime << " stoppedTime: " << this->getCurrentTime() << " reactionTime: " << reactionTime;
    return true;
}

bool ScStwTimer::setResult(TimerState result) {
    if(this->state != WAITING)
        return false;

    switch (result) {
    case WON:
        this->setState(WON);
        return true;
    case LOST:
        this->setState(LOST);
        return true;
    default:
        return false;
    }
}

bool ScStwTimer::reset(){
    if( this->state < WON || this->state == DISABLED ){
        return false;
    }

    this->startTime = 0;
    this->stopTime = 0;
    this->reactionTime = 0;

    this->setState(IDLE);
    return true;
}

// ------------------------
// --- helper functions ---
// ------------------------

bool ScStwTimer::setStartTime(double startTime) {
    if(!this->directControlEnabled)
        return false;

    this->startTime = startTime;
    return true;
}

bool ScStwTimer::setStopTime(double stopTime) {
    if(!this->directControlEnabled)
        return false;

    this->stopTime = stopTime;
    return true;
}

bool ScStwTimer::setReactionTime(double reactionTime) {
    if(!this->directControlEnabled)
        return false;

    this->reactionTime = reactionTime;
    return true;
}

bool ScStwTimer::setState(TimerState newState, bool force) {
    if(!this->directControlEnabled || !force)
        return false;

    this->setState(newState);
    return true;
}

void ScStwTimer::setState(TimerState newState){
    if(this->state == DISABLED && newState != IDLE)
        return;

    if(this->state != newState) {
        this->state = newState;
        qDebug() << "+ [INFO][TIMER] timer state changed: " << newState;
        emit this->stateChanged();
    }
}

ScStwTimer::TimerState ScStwTimer::getState() {
    return this->state;
}

double ScStwTimer::getCurrentTime() {
    switch (this->state) {
    case RUNNING:
        return QDateTime::currentMSecsSinceEpoch() - this->startTime;
    default: {
        if(this->state == WAITING || this->state == WON || this->state == LOST)
            return this->stopTime - this->startTime;
        else
            return -1;
    }
    }
}

double ScStwTimer::getReactionTime() {
    return this->reactionTime;
}

QString ScStwTimer::getText() {
    //qDebug() << this->getState();
    QString newText;
    switch (this->state) {
    case ScStwTimer::IDLE:
        newText = "0.000 sec";
        break;
    case ScStwTimer::STARTING:
        newText = "0.000 sec";
        break;
    case ScStwTimer::WAITING:
        newText = "please wait...";
        break;
    case ScStwTimer::RUNNING:
        newText = QString::number( this->getCurrentTime() / 1000.0, 'f', 3 ) + " sec";
        break;
    case ScStwTimer::WON:
        newText = QString::number( this->getCurrentTime() / 1000.0, 'f', 3 ) + " sec";
        break;
    case ScStwTimer::LOST:
        newText = QString::number( this->getCurrentTime() / 1000.0, 'f', 3 ) + " sec";
        break;
    case ScStwTimer::FAILED:
        newText = "false start";
        break;
    case ScStwTimer::CANCELLED:
        newText = "cancelled";
        break;
    case ScStwTimer::DISABLED:
        newText = "---";
        break;
    }

    return newText;
}

void ScStwTimer::setDisabled(bool disabled) {
    if(disabled)
        this->setState(DISABLED);
    else
        this->setState(IDLE);
}
