#ifndef CCSDSHANDLER_H_
#define CCSDSHANDLER_H_

#include <fsfw/modes/HasModesIF.h>
#include <mission/tmtc/VirtualChannelWithQueue.h>

#include <cstdint>
#include <unordered_map>

#include "OBSWConfig.h"
#include "eive/definitions.h"
#include "fsfw/action/ActionHelper.h"
#include "fsfw/action/HasActionsIF.h"
#include "fsfw/events/EventMessage.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/parameters/ParameterHelper.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "fsfw/subsystem/ModeTreeConnectionIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/timemanager/Countdown.h"
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
#include "fsfw_hal/common/gpio/GpioIF.h"
#include "fsfw_hal/common/gpio/gpioDefinitions.h"
#include "linux/ipcore/PtmeConfig.h"
#include "mission/com/defs.h"

struct PtmeGpios {
  gpioId_t enableTxClock = gpio::NO_GPIO;
  gpioId_t enableTxData = gpio::NO_GPIO;
  gpioId_t ptmeResetn = gpio::NO_GPIO;
};

/**
 * @brief   This class handles the data exchange with the CCSDS IP cores implemented in the
 *          programmable logic of the Q7S.
 *
 * @details
 * After reboot default CADU bitrate is always set to 100 kbps (results in downlink rate
 * of 200 kbps due to convolutional code added by syrlinks transceiver). The IP core handler exposes
 * a parameter to enable the priority selection mode for the PTME core.
 *
 * If the transmitter is on, the selection mode will be enabled when the transmitter goes off.
 * If the transmitter is off, the update of the PTME will be done immediately on a parameter update.
 * This is done because changing this parameter requires a reset of the PTME core to avoid bugs
 * while the transmitter is enabled.
 *
 * @author  J. Meier
 */
class CcsdsIpCoreHandler : public SystemObject,
                           public ExecutableObjectIF,
                           public ModeTreeChildIF,
                           public ModeTreeConnectionIF,
                           public HasModesIF,
                           public AcceptsTelecommandsIF,
                           public ReceivesParameterMessagesIF,
                           public HasActionsIF {
 public:
  enum ParamId : uint8_t { BAT_PRIORITY = 0 };

  static const bool LINK_UP = true;
  static const bool LINK_DOWN = false;
  using VcId_t = uint8_t;

  /**
   * @brief Constructor
   *
   * @param objectId  Object ID of the CCSDS handler
   * @param ptmeId    Object ID of the PTME object providing access to the PTME IP Core.
   * @param tcDestination Object ID of object handling received TC space packets
   * @param txRateSetter  Object providing the functionality to switch the input bitrate of
   *                      the S-Band transceiver.
   * @param gpioIF    Required to enable TX data and TX clock RS485 transceiver chips.
   * @param enTxClock GPIO ID of RS485 tx clock enable
   * @param enTxData  GPIO ID of RS485 tx data enable
   */
  CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination, PtmeConfig& ptmeConfig,
                     std::atomic_bool& linkState, GpioIF* gpioIF, PtmeGpios gpioIds);

  ~CcsdsIpCoreHandler();

  ReturnValue_t performOperation(uint8_t operationCode = 0) override;
  ReturnValue_t initialize();
  MessageQueueId_t getCommandQueue() const override;

  // ModesIF
  void getMode(Mode_t* mode, Submode_t* submode) override;
  ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
                                 uint32_t* msToReachTheMode) override;
  void startTransition(Mode_t mode, Submode_t submode) override;
  void announceMode(bool recursive) override;

  ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
                             ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues,
                             uint16_t startAtIndex);

  uint32_t getIdentifier() const override;
  MessageQueueId_t getRequestQueue() const override;
  const char* getName() const override;

  virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
                                      const uint8_t* data, size_t size);
  const HasHealthIF* getOptHealthIF() const override;
  const HasModesIF& getModeIF() const override;
  object_id_t getObjectId() const override;
  ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;
  ModeTreeChildIF& getModeTreeChildIF() override;

 private:
  static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_HANDLER;
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_MPSOC_HANDLER;

  static const uint32_t QUEUE_SIZE = config::CCSDS_HANDLER_QUEUE_SIZE;

  static const ActionId_t SET_LOW_RATE = 0;
  static const ActionId_t SET_HIGH_RATE = 1;
  static const ActionId_t EN_TRANSMITTER = 2;
  static const ActionId_t DISABLE_TRANSMITTER = 3;
  static const ActionId_t ARBITRARY_RATE = 4;
  static const ActionId_t ENABLE_TX_CLK_MANIPULATOR = 5;
  static const ActionId_t DISABLE_TX_CLK_MANIPULATOR = 6;
  // Will update data with respect to tx clock signal of cadu bitstream on rising edge
  static const ActionId_t UPDATE_ON_RISING_EDGE = 7;
  // Will update data with respect to tx clock signal of cadu bitstream on falling edge
  static const ActionId_t UPDATE_ON_FALLING_EDGE = 8;

  // Syrlinks supports two bitrates (200 kbps and 1000 kbps)
  // Due to convolutional code added by the syrlinks the input frequency must be half the
  // target frequency
  static const uint32_t RATE_100KBPS = 100000;
  static const uint32_t RATE_500KBPS = 500000;

  //! [EXPORT] : [COMMENT] Received action message with unknown action id
  static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xA0);

  // using VirtualChannelMap = std::unordered_map<VcId_t, VirtualChannelWithQueue*>;
  // VirtualChannelMap virtualChannelMap;
  std::atomic_bool& linkState;

  object_id_t tcDestination;

  MessageQueueIF* commandQueue = nullptr;
  MessageQueueIF* eventQueue = nullptr;
  ParameterHelper parameterHelper;

  ActionHelper actionHelper;
  Mode_t mode = HasModesIF::MODE_OFF;
  Submode_t submode = static_cast<Submode_t>(com::CcsdsSubmode::UNSET);
  ModeHelper modeHelper;

  MessageQueueId_t tcDistributorQueueId = MessageQueueIF::NO_QUEUE;

  PtmeConfig& ptmeConfig;
  PtmeGpios ptmeGpios;
  // BAT priority bit on by default to enable priority selection mode for the PTME.
  uint8_t batPriorityParam = 0;
  bool updateBatPriorityOnTxOff = false;

  GpioIF* gpioIF = nullptr;

  void readCommandQueue(void);

  /**
   * @brief    Forward link state to virtual channels.
   */
  void updateLinkState();

  /**
   * @brief   Starts transmit timer and enables transmitter.
   */
  void enableTransmit();

  /**
   * @brief   Disables the transmitter by pulling the enable tx clock and tx data pin of the
   *          RS485 transceiver chips to high.
   */
  void disableTransmit();

  /**
   * The following set of functions configure the mode of the PTME bandwith allocation table (BAT)
   * module. This consists of the following 2 steps:
   *   1. Update the BAT priority bit in the PTME wrapper
   *   2. Reset the PTME as specified in the datasheet.
   */
  void enablePrioritySelectMode();
  void disablePrioritySelectMode();
  void updateBatPriorityFromParam();
};

#endif /* CCSDSHANDLER_H_ */