#ifndef MISSION_DEVICES_RWHANDLER_H_
#define MISSION_DEVICES_RWHANDLER_H_

#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw_hal/common/gpio/gpioDefinitions.h>
#include <mission/acs/rwHelpers.h>
#include <string.h>

#include "events/subsystemIdRanges.h"
#include "returnvalues/classIds.h"

static constexpr bool ACTUATION_WIRETAPPING = false;

class GpioIF;

/**
 * @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, GpioIF* gpioComIF,
            gpioId_t enableGpio, uint8_t rwIdx);

  void setDebugMode(bool enable);

  virtual ~RwHandler();

 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;
  uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
  ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                        LocalDataPoolManager& poolManager) override;
  LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;

 private:
  //! [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);
  static const ReturnValue_t VALUE_NOT_READ = MAKE_RETURN_CODE(0xA5);

  GpioIF* gpioComIF = nullptr;
  gpioId_t enableGpio = gpio::NO_GPIO;
  bool debugMode = false;
  Countdown offTransitionCountdown = Countdown(5000);
  rws::RwRequest currentRequest;

  rws::StatusSet statusSet;
  rws::LastResetSatus lastResetStatusSet;
  rws::TmDataset tmDataset;
  rws::RwSpeedActuationSet rwSpeedActuationSet;

  uint8_t commandBuffer[32];
  uint8_t rwIdx;

  PoolEntry<int32_t> rwSpeed = PoolEntry<int32_t>({0});
  PoolEntry<uint16_t> rampTime = PoolEntry<uint16_t>({10});

  enum class InternalState { DEFAULT, GET_TM, INIT_RW_CONTROLLER, RESET_MCU, SHUTDOWN };
  enum class ShutdownState {
    NONE,
    SET_SPEED_ZERO,
    STOP_POLLING,
    DONE,
  } shutdownState = ShutdownState::NONE;

  InternalState internalState = InternalState::DEFAULT;

  /**
   * @brief   This function checks if the receiced speed and ramp time to set are in a valid
   *          range.
   * @return  returnvalue::OK if successful, otherwise error code.
   */
  ReturnValue_t checkSpeedAndRampTime();

  /**
   * @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_ */