#include "../headers/scstwremotemonitorrace.h"

ScStwRemoteMonitorRace::ScStwRemoteMonitorRace(ScStwClient *monitorClient, QObject *parent) : ScStwRace(parent)
{
    this->scStwClient = monitorClient;

    connect(this->scStwClient, &ScStwClient::stateChanged, this, &ScStwRemoteMonitorRace::handleClientStateChanged);
    connect(this->scStwClient, &ScStwClient::gotSignal, this, &ScStwRemoteMonitorRace::handleBaseStationSignal);
}

// --------------------------
// --- Main Functionality ---
// --------------------------

int ScStwRemoteMonitorRace::start() {

    if(this->getState() != ScStwRace::IDLE) {
        return 904;
    }

    qDebug() << "+ --- starting race";

    int returnCode = 900;

        QVariantMap reply = this->scStwClient->sendCommand(1000);

        if(reply["status"] != 200){
            //handle Error!!
            returnCode = reply["status"].toInt();
        }
        else {

            returnCode = 200;

        }


    return returnCode;
}

int ScStwRemoteMonitorRace::cancel() {
    return this->stop();

}

int ScStwRemoteMonitorRace::stop() {

    if(this->getState() != ScStwRace::RUNNING && this->getState() != ScStwRace::STARTING) {
        return 904;
    }

    // type can be:
    // 0: stopp
    // 1: cancel
    // 2: fail (fase start)

    qDebug() << "+ --- stopping race";

    int returnCode = 900;

        QVariantMap reply = this->scStwClient->sendCommand(1001);

        if(reply["status"] != 200){
            returnCode = reply["status"].toInt();
        }
        else {
            returnCode = 200;
        }


    return returnCode;
}

int ScStwRemoteMonitorRace::reset() {

    if(this->getState() != ScStwRace::STOPPED) {
        return 904;
    }

    qDebug() << "+ --- resetting race";

    int returnCode = 900;


        QVariantMap reply = this->scStwClient->sendCommand(1002);

        if(reply["status"] != 200){
            //handle Error!!
            returnCode = reply["status"].toInt();
        }
        else {
            returnCode = 200;
        }



    return returnCode;
}


// -------------------------
// --- Base Station sync ---
// -------------------------

void ScStwRemoteMonitorRace::handleClientStateChanged() {
    // TODO
    switch (this->scStwClient->getState()) {
    case ScStwClient::CONNECTED:
        break;
    default:
        this->timers.clear();
        this->setState(IDLE);
        break;
    }
}

/**
 * @brief ScStwAppBackend::handleBaseStationUpdate
 *
 * Function to handle an update, sent by the base station, which indicates
 * that some remote value (like a state) has changed
 *
 * @param data
 */
void ScStwRemoteMonitorRace::handleBaseStationSignal(ScStw::SignalKey key, QVariant data) {
    //qDebug() << "got signal: " << data;
    switch (key) {
    case ScStw::RaceStateChanged:
    {
        // the remote race state has changed
        this->setState( ScStwRace::RaceState( data.toInt() ) );
        break;
    }
    case ScStw::TimersChanged:
    {
        // the remote timers have changed
        this->refreshRemoteTimers(data.toList());
        break;
    }
    case ScStw::NextStartActionChanged:
    {
        // the next start action has changed
        this->nextStartActionTotalDelay = data.toList()[ScStwRace::NextStartActionTotalDelay].toDouble();
        this->nextStartActionDelayStartedAt = QDateTime::currentMSecsSinceEpoch() - (this->nextStartActionTotalDelay * data.toList()[ScStwRace::NextStartActionDelayProgress].toDouble());
        this->nextStartAction =  ScStwRace::StartAction( data.toList()[ScStwRace::NextStartAction].toInt() );

        emit this->nextStartActionChanged();
        break;
    }
    case ScStw::InvalidSignal:
        return;

    default:
        return;
    }
}

bool ScStwRemoteMonitorRace::refreshRemoteTimers(QVariantList remoteTimers) {

        if(remoteTimers.length() != this->timers.length()){
            // local timers are out of sync

            // delete all current timers
            foreach(ScStwTimer * locTimer, this->timers){
                delete locTimer;
            }

            this->timers.clear();

            foreach(QVariant remoteTimer, remoteTimers){
                // create a local timer for each remote timer
                ScStwTimer * timer = new ScStwTimer(this, true);
                this->timers.append(timer);
                connect(timer, &ScStwTimer::stateChanged, this, &ScStwRace::timersChanged);
                connect(timer, &ScStwTimer::reactionTimeChanged, this, &ScStwRace::timersChanged);
            }
        }

        foreach(QVariant remoteTimer, remoteTimers){
            int currId = remoteTimer.toMap()["id"].toInt();
            ScStwTimer::TimerState newState = ScStwTimer::TimerState(remoteTimer.toMap()["state"].toInt());
            double currentMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();

            this->timers[currId]->setStartTime(currentMSecsSinceEpoch - remoteTimer.toMap()["currentTime"].toDouble());

            if(newState >= ScStwTimer::WAITING)
                this->timers[currId]->setStopTime(currentMSecsSinceEpoch);

            this->timers[currId]->setReactionTime(remoteTimer.toMap()["reactionTime"].toDouble());

            this->timers[currId]->setState(newState, true);
        }

        return true;
}

bool ScStwRemoteMonitorRace::addTimer(ScStwTimer* timer) {
    Q_UNUSED(timer)
    return false;
}

QVariantList ScStwRemoteMonitorRace::getNextStartActionDetails() {
    int nextActionDelay = 0;
    double nextActionDelayProg = -1;

    if(this->nextStartAction == AtYourMarks || this->nextStartAction == Ready) {
        // get the total delay and the delay progress of the next action timer
        double elapsed = QDateTime::currentMSecsSinceEpoch() - this->nextStartActionDelayStartedAt;
        nextActionDelay = this->nextStartActionTotalDelay;
        if(elapsed < 0 || elapsed > nextActionDelay) {
            elapsed = nextActionDelay;
        }
        nextActionDelayProg = elapsed / nextActionDelay;
    }

    return {
        this->nextStartAction,
        nextActionDelay,
        nextActionDelayProg
    };
}
