#include "PlocSupervisorHandler.h"
#include "OBSWConfig.h"

#include <fsfw/globalfunctions/CRC.h>
#include <fsfw/datapool/PoolReadGuard.h>
#include <fsfw/timemanager/Clock.h>

PlocSupervisorHandler::PlocSupervisorHandler(object_id_t objectId, object_id_t uartComIFid, CookieIF * comCookie) :
        DeviceHandlerBase(objectId, uartComIFid, comCookie), hkset(this) {
    if (comCookie == NULL) {
        sif::error << "PlocSupervisorHandler: Invalid com cookie" << std::endl;
    }
}

PlocSupervisorHandler::~PlocSupervisorHandler() {
}

ReturnValue_t PlocSupervisorHandler::initialize() {
    ReturnValue_t result = RETURN_OK;
    result = DeviceHandlerBase::initialize();
    if (result != RETURN_OK) {
        return result;
    }
    uartComIf = dynamic_cast<UartComIF*>(communicationInterface);
    if (uartComIf == nullptr) {
        sif::warning << "PlocSupervisorHandler::initialize: Invalid uart com if" << std::endl;
        return INVALID_UART_COM_IF;
    }
    return result;
}


void PlocSupervisorHandler::doStartUp(){
#if OBSW_SWITCH_TO_NORMAL_MODE_AFTER_STARTUP == 1
    setMode(MODE_NORMAL);
#else
    setMode(_MODE_TO_ON);
#endif
}

void PlocSupervisorHandler::doShutDown(){

}

ReturnValue_t PlocSupervisorHandler::buildNormalDeviceCommand(
		DeviceCommandId_t * id) {
    return NOTHING_TO_SEND;
}

ReturnValue_t PlocSupervisorHandler::buildTransitionDeviceCommand(
		DeviceCommandId_t * id){
	return NOTHING_TO_SEND;
}

ReturnValue_t PlocSupervisorHandler::buildCommandFromCommand(
		DeviceCommandId_t deviceCommand, const uint8_t * commandData,
		size_t commandDataLen) {
    ReturnValue_t result = RETURN_FAILED;
	switch(deviceCommand) {
	case(PLOC_SPV::GET_HK_REPORT): {
	    prepareEmptyCmd(PLOC_SPV::APID_GET_HK_REPORT);
	    result = RETURN_OK;
	    break;
	}
	case(PLOC_SPV::RESTART_MPSOC): {
	    prepareEmptyCmd(PLOC_SPV::APID_RESTART_MPSOC);
	    result = RETURN_OK;
	    break;
	}
	case(PLOC_SPV::START_MPSOC): {
	    prepareEmptyCmd(PLOC_SPV::APID_START_MPSOC);
	    result = RETURN_OK;
	    break;
	}
	case(PLOC_SPV::SHUTDOWN_MPSOC): {
	    prepareEmptyCmd(PLOC_SPV::APID_SHUTWOWN_MPSOC);
	    result = RETURN_OK;
	    break;
	}
	case(PLOC_SPV::SEL_MPSOC_BOOT_IMAGE): {
	    prepareSelBootImageCmd(commandData);
	    result = RETURN_OK;
	    break;
	}
	case(PLOC_SPV::RESET_MPSOC): {
        prepareEmptyCmd(PLOC_SPV::APID_RESET_MPSOC);
        result = RETURN_OK;
        break;
    }
	case(PLOC_SPV::SET_TIME_REF): {
	    result = prepareSetTimeRefCmd();
	    break;
	}
	case(PLOC_SPV::SET_BOOT_TIMEOUT): {
	    prepareSetBootTimeoutCmd(commandData);
        result = RETURN_OK;
        break;
    }
	case(PLOC_SPV::SET_MAX_RESTART_TRIES): {
	    prepareRestartTriesCmd(commandData);
        result = RETURN_OK;
        break;
    }
	case(PLOC_SPV::DISABLE_PERIOIC_HK_TRANSMISSION): {
	    prepareDisableHk();
        result = RETURN_OK;
        break;
    }
	default:
	    sif::debug << "PlocSupervisorHandler::buildCommandFromCommand: Command not implemented"
	        << std::endl;
		result = DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
		break;
	}

	if (result == RETURN_OK) {
	    /**
	     * Flushing the receive buffer to make sure there are no data left from a faulty reply.
	     */
	    uartComIf->flushUartRxBuffer(comCookie);
	}

	return result;
}

void PlocSupervisorHandler::fillCommandAndReplyMap() {
    this->insertInCommandMap(PLOC_SPV::GET_HK_REPORT);
    this->insertInCommandMap(PLOC_SPV::RESTART_MPSOC);
    this->insertInCommandMap(PLOC_SPV::START_MPSOC);
    this->insertInCommandMap(PLOC_SPV::SHUTDOWN_MPSOC);
    this->insertInCommandMap(PLOC_SPV::SEL_MPSOC_BOOT_IMAGE);
    this->insertInCommandMap(PLOC_SPV::SET_BOOT_TIMEOUT);
    this->insertInCommandMap(PLOC_SPV::SET_MAX_RESTART_TRIES);
    this->insertInCommandMap(PLOC_SPV::RESET_MPSOC);
    this->insertInCommandMap(PLOC_SPV::SET_TIME_REF);
    this->insertInCommandMap(PLOC_SPV::DISABLE_PERIOIC_HK_TRANSMISSION);
    this->insertInReplyMap(PLOC_SPV::ACK_REPORT, 3, nullptr, PLOC_SPV::SIZE_ACK_REPORT);
    this->insertInReplyMap(PLOC_SPV::EXE_REPORT, 3, nullptr, PLOC_SPV::SIZE_EXE_REPORT);
    this->insertInReplyMap(PLOC_SPV::HK_REPORT, 3, nullptr, PLOC_SPV::SIZE_HK_REPORT);
}

ReturnValue_t PlocSupervisorHandler::scanForReply(const uint8_t *start,
        size_t remainingSize, DeviceCommandId_t *foundId, size_t *foundLen) {

    ReturnValue_t result = RETURN_OK;

    uint16_t apid = (*(start) << 8 | *(start + 1)) & APID_MASK;

	switch(apid) {
	case(PLOC_SPV::APID_ACK_SUCCESS):
        *foundLen = PLOC_SPV::SIZE_ACK_REPORT;
	    *foundId = PLOC_SPV::ACK_REPORT;
		break;
	case(PLOC_SPV::APID_ACK_FAILURE):
        *foundLen = PLOC_SPV::SIZE_ACK_REPORT;
	    *foundId = PLOC_SPV::ACK_REPORT;
		break;
	case(PLOC_SPV::APID_HK_REPORT):
        *foundLen = PLOC_SPV::SIZE_HK_REPORT;
	    *foundId = PLOC_SPV::HK_REPORT;
	    break;
	case(PLOC_SPV::APID_EXE_SUCCESS):
        *foundLen = PLOC_SPV::SIZE_EXE_REPORT;
	    *foundId = PLOC_SPV::EXE_REPORT;
		break;
	case(PLOC_SPV::APID_EXE_FAILURE):
        *foundLen = PLOC_SPV::SIZE_EXE_REPORT;
	    *foundId = PLOC_SPV::EXE_REPORT;
	    break;
	default: {
        sif::debug << "PlocSupervisorHandler::scanForReply: Reply has invalid apid" << std::endl;
        *foundLen = remainingSize;
        return INVALID_APID;
	}
	}

    return result;
}

ReturnValue_t PlocSupervisorHandler::interpretDeviceReply(DeviceCommandId_t id,
        const uint8_t *packet) {

    ReturnValue_t result = RETURN_OK;

    switch (id) {
    case PLOC_SPV::ACK_REPORT: {
        result = handleAckReport(packet);
        break;
    }
    case (PLOC_SPV::HK_REPORT): {
        result = handleHkReport(packet);
        break;
    }
    case (PLOC_SPV::EXE_REPORT): {
        result = handleExecutionReport(packet);
        break;
    }
    default: {
        sif::debug << "PlocSupervisorHandler::interpretDeviceReply: Unknown device reply id" << std::endl;
        return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
    }
    }

    return result;
}

void PlocSupervisorHandler::setNormalDatapoolEntriesInvalid(){

}

uint32_t PlocSupervisorHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo){
    return 500;
}

ReturnValue_t PlocSupervisorHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
        LocalDataPoolManager& poolManager) {

    localDataPoolMap.emplace(PLOC_SPV::NUM_TMS, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::TEMP_PS, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::TEMP_PL, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::SOC_STATE, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::NVM0_1_STATE, new PoolEntry<uint8_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::NVM3_STATE, new PoolEntry<uint8_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::MISSION_IO_STATE, new PoolEntry<uint8_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::FMC_STATE, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::NUM_TCS, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::UPTIME, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::CPULOAD, new PoolEntry<uint32_t>( { 0 }));
    localDataPoolMap.emplace(PLOC_SPV::AVAILABLEHEAP, new PoolEntry<uint32_t>( { 0 }));

    return HasReturnvaluesIF::RETURN_OK;
}

ReturnValue_t PlocSupervisorHandler::verifyPacket(const uint8_t* start, size_t foundLen) {

    uint16_t receivedCrc = *(start + foundLen - 2) << 8 | *(start + foundLen - 1);

    uint16_t recalculatedCrc = CRC::crc16ccitt(start, foundLen - 2);

    if (receivedCrc != recalculatedCrc) {
        return CRC_FAILURE;
    }

    return RETURN_OK;
}

ReturnValue_t PlocSupervisorHandler::handleAckReport(const uint8_t* data) {

    ReturnValue_t result = RETURN_OK;

    result = verifyPacket(data, PLOC_SPV::SIZE_ACK_REPORT);
    if(result == CRC_FAILURE) {
        sif::error << "PlocSupervisorHandler::handleAckReport: CRC failure" << std::endl;
        nextReplyId = PLOC_SPV::NONE;
        replyRawReplyIfnotWiretapped(data, PLOC_SPV::SIZE_ACK_REPORT);
        triggerEvent(SUPV_CRC_FAILURE_EVENT);
        sendFailureReport(PLOC_SPV::ACK_REPORT, CRC_FAILURE);
        disableAllReplies();
        return RETURN_OK;
    }

    uint16_t apid = (*(data) << 8 | *(data + 1)) & APID_MASK;

   switch(apid) {
   case PLOC_SPV::APID_ACK_FAILURE: {
       //TODO: Interpretation of status field in acknowledgment report
       sif::debug << "PlocSupervisorHandler::handleAckReport: Received Ack failure report" << std::endl;
       DeviceCommandId_t commandId = getPendingCommand();
       if (commandId != DeviceHandlerIF::NO_COMMAND_ID) {
           triggerEvent(SUPV_ACK_FAILURE, commandId);
       }
       sendFailureReport(PLOC_SPV::ACK_REPORT, RECEIVED_ACK_FAILURE);
       disableAllReplies();
       nextReplyId = PLOC_SPV::NONE;
       result = IGNORE_REPLY_DATA;
       break;
   }
   case PLOC_SPV::APID_ACK_SUCCESS: {
       setNextReplyId();
       break;
   }
   default: {
       sif::debug << "PlocSupervisorHandler::handleAckReport: Invalid APID in Ack report" << std::endl;
       result = RETURN_FAILED;
       break;
   }
   }

    return result;
}

ReturnValue_t PlocSupervisorHandler::handleExecutionReport(const uint8_t* data) {

    ReturnValue_t result = RETURN_OK;

    result = verifyPacket(data, PLOC_SPV::SIZE_EXE_REPORT);
    if(result == CRC_FAILURE) {
        sif::error << "PlocSupervisorHandler::handleExecutionReport: CRC failure" << std::endl;
        nextReplyId = PLOC_SPV::NONE;
        return result;
    }

    uint16_t apid = (*(data) << 8 | *(data + 1)) & APID_MASK;

    switch (apid) {
    case (PLOC_SPV::APID_EXE_SUCCESS): {
        break;
    }
    case (PLOC_SPV::APID_EXE_FAILURE): {
        //TODO: Interpretation of status field in execution report
        sif::error << "PlocSupervisorHandler::handleExecutionReport: Received execution failure report"
                << std::endl;
        DeviceCommandId_t commandId = getPendingCommand();
        if (commandId != DeviceHandlerIF::NO_COMMAND_ID) {
            triggerEvent(SUPV_EXE_FAILURE, commandId);
        }
        else {
            sif::debug << "PlocSupervisorHandler::handleExecutionReport: Unknown command id" << std::endl;
        }
        sendFailureReport(PLOC_SPV::EXE_REPORT, RECEIVED_EXE_FAILURE);
        disableExeReportReply();
        result = IGNORE_REPLY_DATA;
        break;
    }
    default: {
        sif::error << "PlocSupervisorHandler::handleExecutionReport: Unknown APID" << std::endl;
        result = RETURN_FAILED;
        break;
    }
    }

    nextReplyId = PLOC_SPV::NONE;

    return result;
}

ReturnValue_t PlocSupervisorHandler::handleHkReport(const uint8_t* data) {

    ReturnValue_t result = RETURN_OK;

    result = verifyPacket(data, PLOC_SPV::SIZE_HK_REPORT);

    if(result == CRC_FAILURE) {
        sif::error << "PlocSupervisorHandler::handleHkReport: Hk report has invalid crc"
                << std::endl;
    }

    uint16_t offset = PLOC_SPV::DATA_FIELD_OFFSET;
    hkset.tempPs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.tempPl = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.tempSup = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.uptime = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.cpuLoad = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.availableHeap = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.numTcs = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.numTms = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.socState = *(data + offset) << 24 | *(data + offset + 1) << 16 | *(data + offset + 2) << 8
            | *(data + offset + 3);
    offset += 4;
    hkset.nvm0_1_state = *(data + offset);
    offset += 1;
    hkset.nvm3_state = *(data + offset);
    offset += 1;
    hkset.missionIoState = *(data + offset);
    offset += 1;
    hkset.fmcState = *(data + offset);
    offset += 1;

    nextReplyId = PLOC_SPV::EXE_REPORT;

#if OBSW_VERBOSE_LEVEL >= 1 && PLOC_SUPERVISOR_DEBUG == 1
    sif::info << "PlocSupervisorHandler::handleHkReport: temp_ps: " << hkset.tempPs << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: temp_pl: " << hkset.tempPl << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: temp_sup: " << hkset.tempSup << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: uptime: " << hkset.uptime << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: cpu_load: " << hkset.cpuLoad << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: available_heap: " << hkset.availableHeap << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: num_tcs: " << hkset.numTcs << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: num_tms: " << hkset.numTms << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: soc_state: " << hkset.socState << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: nvm0_1_state: "
            << static_cast<unsigned int>(hkset.nvm0_1_state.value) << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: nvm3_state: "
            << static_cast<unsigned int>(hkset.nvm3_state.value) << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: missoin_io_state: "
            << static_cast<unsigned int>(hkset.missionIoState.value) << std::endl;
    sif::info << "PlocSupervisorHandler::handleHkReport: fmc_state: "
            << static_cast<unsigned int>(hkset.fmcState.value) << std::endl;

#endif

    return result;
}

ReturnValue_t PlocSupervisorHandler::enableReplyInReplyMap(DeviceCommandMap::iterator command,
                uint8_t expectedReplies, bool useAlternateId,
                DeviceCommandId_t alternateReplyID) {

    ReturnValue_t result = RETURN_OK;

    uint8_t enabledReplies = 0;

    switch (command->first) {
    case PLOC_SPV::GET_HK_REPORT: {
        enabledReplies = 3;
        result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true,
                PLOC_SPV::HK_REPORT);
        if (result != RETURN_OK) {
            sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
                    << PLOC_SPV::HK_REPORT << " not in replyMap" << std::endl;
        }
        break;
    }
    case PLOC_SPV::RESTART_MPSOC:
    case PLOC_SPV::START_MPSOC:
    case PLOC_SPV::SHUTDOWN_MPSOC:
    case PLOC_SPV::SEL_MPSOC_BOOT_IMAGE:
    case PLOC_SPV::SET_BOOT_TIMEOUT:
    case PLOC_SPV::SET_MAX_RESTART_TRIES:
    case PLOC_SPV::RESET_MPSOC:
    case PLOC_SPV::SET_TIME_REF:
        enabledReplies = 2;
        break;
    default:
        sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Unknown command id" << std::endl;
        break;
    }

    /**
     * Every command causes at least one acknowledgment and one execution report. Therefore both
     * replies will be enabled here.
     */
    result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true,
            PLOC_SPV::ACK_REPORT);
    if (result != RETURN_OK) {
        sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
                << PLOC_SPV::ACK_REPORT << " not in replyMap" << std::endl;
    }

    result = DeviceHandlerBase::enableReplyInReplyMap(command, enabledReplies, true,
            PLOC_SPV::EXE_REPORT);
    if (result != RETURN_OK) {
        sif::debug << "PlocSupervisorHandler::enableReplyInReplyMap: Reply with id "
                << PLOC_SPV::EXE_REPORT << " not in replyMap" << std::endl;
    }

    return RETURN_OK;
}

void PlocSupervisorHandler::setNextReplyId() {
    switch(getPendingCommand()) {
    case PLOC_SPV::GET_HK_REPORT:
        nextReplyId = PLOC_SPV::HK_REPORT;
        break;
    default:
        /* If no telemetry is expected the next reply is always the execution report */
        nextReplyId = PLOC_SPV::EXE_REPORT;
        break;
    }
}
size_t PlocSupervisorHandler::getNextReplyLength(DeviceCommandId_t commandId){

    size_t replyLen = 0;

    if (nextReplyId == PLOC_SPV::NONE) {
        return replyLen;
    }

    DeviceReplyIter iter = deviceReplyMap.find(nextReplyId);
    if (iter != deviceReplyMap.end()) {
        if (iter->second.delayCycles == 0) {
            /* Reply inactive */
            return replyLen;
        }
        replyLen = iter->second.replyLen;
    }
    else {
        sif::debug << "PlocSupervisorHandler::getNextReplyLength: No entry for reply with reply id "
                << std::hex << nextReplyId << " in deviceReplyMap" << std::endl;
    }

    return replyLen;
}

void PlocSupervisorHandler::handleDeviceTM(const uint8_t* data, size_t dataSize, DeviceCommandId_t replyId) {

    ReturnValue_t result = RETURN_OK;

    if (wiretappingMode == RAW) {
        /* Data already sent in doGetRead() */
        return;
    }

    DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId);
    if (iter == deviceReplyMap.end()) {
        sif::debug << "PlocSupervisorHandler::handleDeviceTM: Unknown reply id" << std::endl;
        return;
    }
    MessageQueueId_t queueId = iter->second.command->second.sendReplyTo;

    if (queueId == NO_COMMANDER) {
        return;
    }

    result = actionHelper.reportData(queueId, replyId, data, dataSize);
    if (result != RETURN_OK) {
        sif::debug << "PlocSupervisorHandler::handleDeviceTM: Failed to report data" << std::endl;
    }
}

void PlocSupervisorHandler::prepareEmptyCmd(uint16_t apid) {
    PLOC_SPV::EmptyPacket packet(apid);
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
}

void PlocSupervisorHandler::prepareSelBootImageCmd(const uint8_t * commandData) {
    PLOC_SPV::MPSoCBootSelect packet(*commandData, *(commandData + 1), *(commandData + 2),
            *(commandData + 3));
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
}

ReturnValue_t PlocSupervisorHandler::prepareSetTimeRefCmd() {
    Clock::TimeOfDay_t time;
    ReturnValue_t result = Clock::getDateAndTime(&time);
    if (result != RETURN_OK) {
        sif::warning << "PlocSupervisorHandler::prepareSetTimeRefCmd: Failed to get current time"
                << std::endl;
        return GET_TIME_FAILURE;
    }
    PLOC_SPV::SetTimeRef packet(&time);
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
    return RETURN_OK;
}

void PlocSupervisorHandler::prepareDisableHk() {
    PLOC_SPV::DisablePeriodicHkTransmission packet;
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
}

void PlocSupervisorHandler::prepareSetBootTimeoutCmd(const uint8_t * commandData) {
    uint32_t timeout = *(commandData) << 24 | *(commandData + 1) << 16 | *(commandData + 2) << 8
            | *(commandData + 3);
    PLOC_SPV::SetBootTimeout packet(timeout);
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
}

void PlocSupervisorHandler::prepareRestartTriesCmd(const uint8_t * commandData) {
    uint8_t restartTries = *(commandData);
    PLOC_SPV::SetRestartTries packet(restartTries);
    memcpy(commandBuffer, packet.getWholeData(), packet.getFullSize());
    rawPacket = commandBuffer;
    rawPacketLen = packet.getFullSize();
    nextReplyId = PLOC_SPV::ACK_REPORT;
}

void PlocSupervisorHandler::disableAllReplies() {

    DeviceReplyMap::iterator iter;

    /* Disable ack reply */
    iter = deviceReplyMap.find(PLOC_SPV::ACK_REPORT);
    DeviceReplyInfo *info = &(iter->second);
    info->delayCycles = 0;
    info->command = deviceCommandMap.end();

    DeviceCommandId_t commandId = getPendingCommand();

    /* If the command expects a telemetry packet the appropriate tm reply will be disabled here */
    switch (commandId) {
    case PLOC_SPV::GET_HK_REPORT: {
        iter = deviceReplyMap.find(PLOC_SPV::GET_HK_REPORT);
        info = &(iter->second);
        info->delayCycles = 0;
        info->command = deviceCommandMap.end();
        break;
    }
    default: {
        break;
    }
    }

    /* We must always disable the execution report reply here */
    disableExeReportReply();
}

void PlocSupervisorHandler::sendFailureReport(DeviceCommandId_t replyId, ReturnValue_t status) {

    DeviceReplyIter iter = deviceReplyMap.find(replyId);

    if (iter == deviceReplyMap.end()) {
        sif::debug << "PlocSupervisorHandler::sendFailureReport: Reply not in reply map" << std::endl;
        return;
    }

    DeviceCommandInfo* info = &(iter->second.command->second);

    if (info == nullptr) {
        sif::debug << "PlocSupervisorHandler::sendFailureReport: Reply has no active command" << std::endl;
        return;
    }

    if (info->sendReplyTo != NO_COMMANDER) {
        actionHelper.finish(false, info->sendReplyTo, iter->first, status);
    }
    info->isExecuting = false;
}

void PlocSupervisorHandler::disableExeReportReply() {
    DeviceReplyIter iter = deviceReplyMap.find(PLOC_SPV::EXE_REPORT);
    DeviceReplyInfo *info = &(iter->second);
    info->delayCycles = 0;
    info->command = deviceCommandMap.end();
    /* Expected replies is set to one here. The value will set to 0 in replyToReply() */
    info->command->second.expectedReplies = 1;
}