#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_RWDEFINITIONS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_RWDEFINITIONS_H_

#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include "objects/systemObjectList.h"

namespace RwDefinitions {

static const uint32_t SPI_REPLY_DELAY = 70000; //us

enum PoolIds: lp_id_t {
    TEMPERATURE_C,
    CURR_SPEED,
    REFERENCE_SPEED,
    STATE,
    CLC_MODE,
    LAST_RESET_STATUS,
    CURRRENT_RESET_STATUS,
    TM_LAST_RESET_STATUS,
    TM_MCU_TEMPERATURE,
    PRESSURE_SENSOR_TEMPERATURE,
    PRESSURE,
    TM_RW_STATE,
    TM_CLC_MODE,
    TM_RW_CURR_SPEED,
    TM_RW_REF_SPEED,
    INVALID_CRC_PACKETS,
    INVALID_LEN_PACKETS,
    INVALID_CMD_PACKETS,
    EXECUTED_REPLIES,
    COMMAND_REPLIES,
    UART_BYTES_WRITTEN,
    UART_BYTES_READ,
    UART_PARITY_ERRORS,
    UART_NOISE_ERRORS,
    UART_FRAME_ERRORS,
    UART_REG_OVERRUN_ERRORS,
    UART_TOTAL_ERRORS,
    TOTAL_ERRORS,
    SPI_BYTES_WRITTEN,
    SPI_BYTES_READ,
    SPI_REG_OVERRUN_ERRORS,
    SPI_TOTAL_ERRORS
};

enum States: uint8_t {
    ERROR,
    IDLE,
    COASTING,
    RUNNING_SPEED_STABLE,
    RUNNING_SPEED_CHANGING
};

enum LastResetStatus: uint8_t {
    CLEARED = 0,
    PIN_RESET = 1,
    POR_PDR_BOR_RESET = 2,
    SOFTWARE_RESET = 4,
    INDEPENDENT_WATCHDOG_RESET = 8,
    WINDOW_WATCHDOG_RESET = 16,
    LOW_POWER_RESET = 32
};

static const DeviceCommandId_t RESET_MCU = 1;
static const DeviceCommandId_t GET_LAST_RESET_STATUS = 2;
static const DeviceCommandId_t CLEAR_LAST_RESET_STATUS = 3;
static const DeviceCommandId_t GET_RW_STATUS = 4;
/** This command is needed to recover from error state */
static const DeviceCommandId_t INIT_RW_CONTROLLER = 5;
static const DeviceCommandId_t SET_SPEED = 6;
static const DeviceCommandId_t GET_TEMPERATURE = 8;
static const DeviceCommandId_t GET_TM = 9;

static const uint32_t TEMPERATURE_SET_ID = GET_TEMPERATURE;
static const uint32_t STATUS_SET_ID = GET_RW_STATUS;
static const uint32_t LAST_RESET_ID = GET_LAST_RESET_STATUS;
static const uint32_t TM_SET_ID = GET_TM;

static const size_t SIZE_GET_RESET_STATUS = 5;
static const size_t SIZE_CLEAR_RESET_STATUS = 4;
static const size_t SIZE_INIT_RW = 4;
static const size_t SIZE_GET_RW_STATUS = 14;
static const size_t SIZE_SET_SPEED_REPLY = 4;
static const size_t SIZE_GET_TEMPERATURE_REPLY = 8;
/** Max size when requesting telemetry */
static const size_t SIZE_GET_TELEMETRY_REPLY = 91;

/** Set speed command has maximum size */
static const size_t MAX_CMD_SIZE = 9;
/**
 * Max reply is reached when each byte is replaced by its substitude which should normally never
 * happen.
 */
static const size_t MAX_REPLY_SIZE = 2 * SIZE_GET_TELEMETRY_REPLY;

static const uint8_t LAST_RESET_ENTRIES = 2;
static const uint8_t TEMPERATURE_SET_ENTRIES = 1;
static const uint8_t STATUS_SET_ENTRIES = 4;
static const uint8_t TM_SET_ENTRIES = 24;

/**
 * @brief   This dataset can be used to store the temperature of a reaction wheel.
 */
class TemperatureSet:
        public StaticLocalDataSet<TEMPERATURE_SET_ENTRIES> {
public:

    TemperatureSet(HasLocalDataPoolIF* owner):
        StaticLocalDataSet(owner, TEMPERATURE_SET_ID) {
    }

    TemperatureSet(object_id_t objectId):
        StaticLocalDataSet(sid_t(objectId, TEMPERATURE_SET_ID)) {
    }

    lp_var_t<int32_t> temperatureCelcius = lp_var_t<int32_t>(sid.objectId,
            PoolIds::TEMPERATURE_C, this);
};

/**
 * @brief   This dataset can be used to store the reaction wheel status.
 */
class StatusSet:
        public StaticLocalDataSet<STATUS_SET_ENTRIES> {
public:

    StatusSet(HasLocalDataPoolIF* owner):
        StaticLocalDataSet(owner, STATUS_SET_ID) {
    }

    StatusSet(object_id_t objectId):
        StaticLocalDataSet(sid_t(objectId, STATUS_SET_ID)) {
    }

    lp_var_t<int32_t> currSpeed = lp_var_t<int32_t>(sid.objectId,
            PoolIds::CURR_SPEED, this);
    lp_var_t<int32_t> referenceSpeed = lp_var_t<int32_t>(sid.objectId,
            PoolIds::REFERENCE_SPEED, this);
    lp_var_t<uint8_t> state = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::STATE, this);
    lp_var_t<uint8_t> clcMode = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::CLC_MODE, this);
};

/**
 * @brief   This dataset stores the last reset status.
 */
class LastResetSatus:
        public StaticLocalDataSet<LAST_RESET_ENTRIES> {
public:

    LastResetSatus(HasLocalDataPoolIF* owner):
        StaticLocalDataSet(owner, LAST_RESET_ID) {
    }

    LastResetSatus(object_id_t objectId):
        StaticLocalDataSet(sid_t(objectId, LAST_RESET_ID)) {
    }

    lp_var_t<uint8_t> lastResetStatus = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::LAST_RESET_STATUS, this);
    lp_var_t<uint8_t> currentResetStatus = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::CURRRENT_RESET_STATUS, this);
};

/**
 * @brief   This dataset stores telemetry data as specified in the datasheet of the nano avionics
 *          reaction wheels. https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/
 *          EIVE_IRS/Arbeitsdaten/08_Used%20Components/Nanoavionics_Reactionwheels&fileid=181622
 */
class TmDataset:
        public StaticLocalDataSet<TM_SET_ENTRIES> {
public:

    TmDataset(HasLocalDataPoolIF* owner):
        StaticLocalDataSet(owner, TM_SET_ID) {
    }

    TmDataset(object_id_t objectId):
        StaticLocalDataSet(sid_t(objectId, TM_SET_ID)) {
    }

    lp_var_t<uint8_t> lastResetStatus = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::TM_LAST_RESET_STATUS, this);
    lp_var_t<int32_t> mcuTemperature = lp_var_t<int32_t>(sid.objectId,
            PoolIds::TM_MCU_TEMPERATURE, this);
    lp_var_t<float> pressureSensorTemperature = lp_var_t<float>(sid.objectId,
            PoolIds::PRESSURE_SENSOR_TEMPERATURE, this);
    lp_var_t<float> pressure = lp_var_t<float>(sid.objectId,
            PoolIds::PRESSURE, this);
    lp_var_t<uint8_t> rwState = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::TM_RW_STATE, this);
    lp_var_t<uint8_t> rwClcMode = lp_var_t<uint8_t>(sid.objectId,
            PoolIds::TM_CLC_MODE, this);
    lp_var_t<int32_t> rwCurrSpeed = lp_var_t<int32_t>(sid.objectId,
            PoolIds::TM_RW_CURR_SPEED, this);
    lp_var_t<int32_t> rwRefSpeed = lp_var_t<int32_t>(sid.objectId,
            PoolIds::TM_RW_REF_SPEED, this);
    lp_var_t<uint32_t> numOfInvalidCrcPackets = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::INVALID_CRC_PACKETS, this);
    lp_var_t<uint32_t> numOfInvalidLenPackets = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::INVALID_LEN_PACKETS, this);
    lp_var_t<uint32_t> numOfInvalidCmdPackets = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::INVALID_CMD_PACKETS, this);
    lp_var_t<uint32_t> numOfCmdExecutedReplies = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::EXECUTED_REPLIES, this);
    lp_var_t<uint32_t> numOfCmdReplies = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::COMMAND_REPLIES, this);
    lp_var_t<uint32_t> uartNumOfBytesWritten = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_BYTES_WRITTEN, this);
    lp_var_t<uint32_t> uartNumOfBytesRead = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_BYTES_READ, this);
    lp_var_t<uint32_t> uartNumOfParityErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_PARITY_ERRORS, this);
    lp_var_t<uint32_t> uartNumOfNoiseErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_NOISE_ERRORS, this);
    lp_var_t<uint32_t> uartNumOfFrameErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_FRAME_ERRORS, this);
    lp_var_t<uint32_t> uartNumOfRegisterOverrunErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_REG_OVERRUN_ERRORS, this);
    lp_var_t<uint32_t> uartTotalNumOfErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::UART_TOTAL_ERRORS, this);
    lp_var_t<uint32_t> spiNumOfBytesWritten = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::SPI_BYTES_WRITTEN, this);
    lp_var_t<uint32_t> spiNumOfBytesRead = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::SPI_BYTES_READ, this);
    lp_var_t<uint32_t> spiNumOfRegisterOverrunErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::SPI_REG_OVERRUN_ERRORS, this);
    lp_var_t<uint32_t> spiTotalNumOfErrors = lp_var_t<uint32_t>(sid.objectId,
            PoolIds::SPI_TOTAL_ERRORS, this);
};

}

#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_RWDEFINITIONS_H_ */