#ifndef MISSION_DEVICES_RWHANDLER_H_
#define MISSION_DEVICES_RWHANDLER_H_

#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw_hal/linux/gpio/LinuxLibgpioIF.h>
#include <mission/devices/devicedefinitions/RwDefinitions.h>
#include <string.h>

/**
 * @brief	This is the device handler for the reaction wheel from nano avionics.
 *
 * @details Datasheet: https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_IRS/
 *                      Arbeitsdaten/08_Used%20Components/Nanoavionics_Reactionwheels&fileid=181622
 *
 * @note    Values are transferred in little endian format.
 *
 * @author	J. Meier
 */
class RwHandler : public DeviceHandlerBase {
 public:
  /**
   * @brief Constructor
   *
   * @param objectId
   * @param comIF
   * @param comCookie
   * @param gpioComIF	Pointer to gpio communication interface
   * @param enablePin 	GPIO connected to the enable pin of the reaction wheels. Must be pulled
   * 						to high to enable the device.
   */
  RwHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie, LinuxLibgpioIF* gpioComIF,
            gpioId_t enableGpio);
  virtual ~RwHandler();

  static const uint8_t INTERFACE_ID = CLASS_ID::RW_HANDLER;

  static const ReturnValue_t SPI_WRITE_FAILURE = MAKE_RETURN_CODE(0xB0);
  //! [EXPORT] : [COMMENT] Used by the spi send function to tell a failing read call
  static const ReturnValue_t SPI_READ_FAILURE = MAKE_RETURN_CODE(0xB1);
  //! [EXPORT] : [COMMENT] Can be used by the HDLC decoding mechanism to inform about a missing
  //! start sign 0x7E
  static const ReturnValue_t MISSING_START_SIGN = MAKE_RETURN_CODE(0xB2);
  //! [EXPORT] : [COMMENT] Can be used by the HDLC decoding mechanism to inform about an invalid
  //! substitution combination
  static const ReturnValue_t INVALID_SUBSTITUTE = MAKE_RETURN_CODE(0xB3);
  //! [EXPORT] : [COMMENT] HDLC decoding mechanism never receives the end sign 0x7E
  static const ReturnValue_t MISSING_END_SIGN = MAKE_RETURN_CODE(0xB4);
  //! [EXPORT] : [COMMENT] Reaction wheel only responds with empty frames.
  static const ReturnValue_t NO_REPLY = MAKE_RETURN_CODE(0xB5);
  //! [EXPORT] : [COMMENT] Expected a start marker as first byte
  static const ReturnValue_t NO_START_MARKER = MAKE_RETURN_CODE(0xB6);

 protected:
  void doStartUp() override;
  void doShutDown() override;
  ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
  ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
  void fillCommandAndReplyMap() override;
  ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
                                        size_t commandDataLen) override;
  ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
                             size_t* foundLen) override;
  ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) override;
  void setNormalDatapoolEntriesInvalid() override;
  uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
  ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                        LocalDataPoolManager& poolManager) override;

 private:
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::RW_HANDLER;

  //! [EXPORT] : [COMMENT] Action Message with invalid speed was received. Valid speeds must be in
  //! the range of [-65000; 1000] or [1000; 65000]
  static const ReturnValue_t INVALID_SPEED = MAKE_RETURN_CODE(0xA0);
  //! [EXPORT] : [COMMENT] Action Message with invalid ramp time was received.
  static const ReturnValue_t INVALID_RAMP_TIME = MAKE_RETURN_CODE(0xA1);
  //! [EXPORT] : [COMMENT] Received set speed command has invalid length. Should be 6.
  static const ReturnValue_t SET_SPEED_COMMAND_INVALID_LENGTH = MAKE_RETURN_CODE(0xA2);
  //! [EXPORT] : [COMMENT] Command execution failed
  static const ReturnValue_t EXECUTION_FAILED = MAKE_RETURN_CODE(0xA3);
  //! [EXPORT] : [COMMENT] Reaction wheel reply has invalid crc
  static const ReturnValue_t CRC_ERROR = MAKE_RETURN_CODE(0xA4);

  //! [EXPORT] : [COMMENT] Reaction wheel signals an error state
  static const Event ERROR_STATE = MAKE_EVENT(1, severity::HIGH);

  LinuxLibgpioIF* gpioComIF = nullptr;
  gpioId_t enableGpio = gpio::NO_GPIO;

  RwDefinitions::TemperatureSet temperatureSet;
  RwDefinitions::StatusSet statusSet;
  RwDefinitions::LastResetSatus lastResetStatusSet;
  RwDefinitions::TmDataset tmDataset;

  uint8_t commandBuffer[RwDefinitions::MAX_CMD_SIZE];

  enum class InternalState { GET_RESET_STATUS, CLEAR_RESET_STATUS, READ_TEMPERATURE, GET_RW_SATUS };

  InternalState internalState = InternalState::GET_RESET_STATUS;

  size_t sizeOfReply = 0;

  /**
   * @brief   This function can be used to build commands which do not contain any data apart
   *          from the command id and the CRC.
   * @param commandId The command id of the command to build.
   */
  void prepareSimpleCommand(DeviceCommandId_t id);

  /**
   * @brief   This function checks if the receiced speed and ramp time to set are in a valid
   *          range.
   * @return  RETURN_OK if successful, otherwise error code.
   */
  ReturnValue_t checkSpeedAndRampTime(const uint8_t* commandData, size_t commandDataLen);

  /**
   * @brief   This function prepares the set speed command from the commandData received with
   *          an action message.
   */
  void prepareSetSpeedCmd(const uint8_t* commandData, size_t commandDataLen);

  /**
   * @brief   This function writes the last reset status retrieved with the get last reset status
   *          command into the reset status dataset.
   *
   * @param packet Pointer to the buffer holding the reply data.
   */
  void handleResetStatusReply(const uint8_t* packet);

  /**
   * @brief   This function handles the reply of the get temperature command.
   *
   * @param packet    Pointer to the reply data
   */
  void handleTemperatureReply(const uint8_t* packet);

  /**
   * @brief   This function fills the status set with the data from the get-status-reply.
   */
  void handleGetRwStatusReply(const uint8_t* packet);

  /**
   * @brief   This function fills the tmDataset with the reply data requested with get telemetry
   *          command.
   */
  void handleGetTelemetryReply(const uint8_t* packet);
};

#endif /* MISSION_DEVICES_RWHANDLER_H_ */