#ifndef MISSION_DEVICES_GOMSPACEDEVICEHANDLER_H_
#define MISSION_DEVICES_GOMSPACEDEVICEHANDLER_H_

#include <mission/csp/CspCookie.h>
#include <mission/devices/devicedefinitions/GomSpacePackets.h>

#include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "mission/devices/devicedefinitions/GomspaceDefinitions.h"
#include "returnvalues/classIds.h"

struct TableConfig {
  uint16_t maxConfigTableAddress;
  uint16_t maxHkTableAddress;
  uint16_t hkTableSize;
  uint16_t cfgTableSize;
};
/**
 * @brief	This is the device handler class for all gomspace devices.
 *
 * @details
 * All gomspace devices are similar with respect to commanding. Thusmost of the functionality to
 * command a gomspace device can be accommodated in one class. For device specific functions, a new
 * class could be created by inheriting from the GomspaceDeviceHandler.
 *
 * Flight manual:
 * https://egit.irs.uni-stuttgart.de/redmine/projects/eive-flight-manual/wiki/Gomspace_PCDU_P60_System
 */
class GomspaceDeviceHandler : public DeviceHandlerBase {
 public:
  static constexpr uint8_t CLASS_ID = CLASS_ID::GOM_SPACE_HANDLER;
  static const ReturnValue_t PACKET_TOO_LONG = returnvalue::makeCode(CLASS_ID, 0);
  static const ReturnValue_t INVALID_TABLE_ID = returnvalue::makeCode(CLASS_ID, 1);
  static const ReturnValue_t INVALID_ADDRESS = returnvalue::makeCode(CLASS_ID, 2);
  static const ReturnValue_t INVALID_PARAM_SIZE = returnvalue::makeCode(CLASS_ID, 3);
  static const ReturnValue_t INVALID_PAYLOAD_SIZE = returnvalue::makeCode(CLASS_ID, 4);
  static const ReturnValue_t UNKNOWN_REPLY_ID = returnvalue::makeCode(CLASS_ID, 5);

  /**
   * @brief	Constructor
   *
   * @param maxConfigTableAddress	The maximum memory address of the configu-
   * 								ration table of a gomspace device.
   * @param maxHkTableAddress	The maximum memory address of a value in the
   * 							houskeeping (telemetry) table of a gomspace
   * 							device.
   */
  GomspaceDeviceHandler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie,
                        TableConfig &tableConfig, FailureIsolationBase *customFdir);
  virtual ~GomspaceDeviceHandler();

  /**
   * @brief   This function can be used to set a gomspace device to normal mode immediately after
   *          object creation.
   */
  void setModeNormal();

 protected:
  static const uint8_t MAX_PACKET_LEN = 36;
  static const uint8_t PARAM_SET_OK = 1;
  static const uint8_t PING_REPLY_SIZE = 2;

  uint8_t rememberRequestedSize = 0;
  uint8_t rememberCommandId = GOMSPACE::NONE;
  uint8_t cspPacket[MAX_PACKET_LEN];

  LocalPoolDataSetBase *hkTableDataset = nullptr;

  void initPduConfigTable();
  void doStartUp() override;
  void doShutDown() override;
  virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
  ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
  virtual 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;
  /**
   * @brief   The command to generate a request to receive the full housekeeping table is device
   *          specific. Thus the child has to build this command.
   */
  ReturnValue_t generateRequestFullHkTableCmd(GOMSPACE::DeviceType devType, uint16_t tableSize,
                                              DeviceCommandId_t id, CspCookie *cspCookie);
  /**
   * Unfortunately, it was not possible to specify the table ID (e.g. request table from
   * default store)
   * @param devType
   * @param tableSize
   * @param id
   * @param cspCookie
   * @return
   */
  ReturnValue_t generateRequestFullCfgTableCmd(GOMSPACE::DeviceType devType, uint16_t tableSize,
                                               DeviceCommandId_t id, CspCookie *cspCookie);
  ReturnValue_t getDevType(GOMSPACE::DeviceType &type) const;
  /**
   * This command handles printing the HK table to the console. This is useful for debugging
   * purposes
   * @return
   */
  virtual ReturnValue_t printStatus(DeviceCommandId_t cmd);

  /**
   * @brief	Because housekeeping tables are device specific the handling of the reply is
   * 			given to the child class.
   * @param id	The id of the command which initiates the full table request.
   * @param packet	Pointer to the reply containing the hk table.
   */
  virtual void letChildHandleHkReply(DeviceCommandId_t id, const uint8_t *packet) = 0;
  virtual void letChildHandleConfigReply(DeviceCommandId_t id, const uint8_t *packet) = 0;
  virtual LocalPoolDataSetBase *getDataSetHandle(sid_t sid) = 0;

  /**
   * @brief   Can be overriden by child classes to implement device specific commands.
   * @return Return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED to let this handler handle
   *   the command or returnvalue::OK if the child handles the command
   */
  virtual ReturnValue_t childCommandHook(DeviceCommandId_t cmd, const uint8_t *commandData,
                                         size_t commandDataLen);

  ReturnValue_t parsePduHkTable(PDU::PduCoreHk &coreHk, PDU::PduAuxHk &auxHk,
                                const uint8_t *packet);
  ReturnValue_t initializePduPool(localpool::DataPool &localDataPoolMap,
                                  LocalDataPoolManager &poolManager,
                                  std::array<uint8_t, PDU::CHANNELS_LEN> initOutEnb);

  template <typename T>
  T as(const uint8_t *);
  static bool validTableId(uint8_t id);

 private:
  SetParamMessageUnpacker setParamCacher;
  TableConfig &tableCfg;
  /**
   * @brief	Function to generate the command to set a parameter. Command
   * 			will be sent to the ComIF over the rawPacket buffer.
   */
  ReturnValue_t generateSetParamCommand(const uint8_t *commandData, size_t commandDataLen);

  /**
   * Callback is called on a parameter set command. It is called before executing it and after
   * after successful execution
   * @param unpacker Passed before
   * @param beforeSet False for callback before execution, true if called after successful
   *   execution
   * @return
   */
  virtual ReturnValue_t setParamCallback(SetParamMessageUnpacker &unpacker, bool afterExecution);

  /**
   * @brief	Function to generate the command to get a parameter from a
   * 			gomspace device. Command will be sent to the ComIF over the
   * 			rawPacket buffer.
   */
  ReturnValue_t generateGetParamCommand(const uint8_t *commandData, size_t commandDataLen);

  /**
   * @brief	Function to generate the ping command for the ComIF.
   */
  ReturnValue_t generatePingCommand(const uint8_t *commandData, size_t commandDataLen);

  /**
   * @brief	Function to generate the command to reboot a gomspace device
   * 			via the ComIF.
   */
  void generateRebootCommand();

  /**
   * @brief	Function to generate the command to force a ground watchdog
   * 			reset in a gomspace device.
   */
  ReturnValue_t generateResetWatchdogCmd();
};

template <typename T>
inline T GomspaceDeviceHandler::as(const uint8_t *ptr) {
  return *(reinterpret_cast<const T *>(ptr));
}

#endif /* MISSION_DEVICES_GOMSPACEDEVICEHANDLER_H_ */