#ifndef LINUX_OBC_PAPBVCINTERFACE_H_
#define LINUX_OBC_PAPBVCINTERFACE_H_

#include <fsfw_hal/common/gpio/gpioDefinitions.h>
#include <fsfw_hal/linux/gpio/LinuxLibgpioIF.h>
#include <linux/ipcore/VirtualChannelIF.h>

#include <atomic>

#include "OBSWConfig.h"
#include "fsfw/returnvalues/returnvalue.h"

/**
 * @brief   This class handles the transmission of data to a virtual channel of the PTME IP Core
 *          via the PAPB interface.
 *
 * @author  J. Meier
 */
class PapbVcInterface : public VirtualChannelIF {
 public:
  /**
   * @brief   Constructor
   *
   * @param papbBusyId    The ID of the GPIO which is connected to the PAPBBusy_N signal of the
   *                      VcInterface IP Core. A low logic level indicates the VcInterface is not
   *                      ready to receive more data.
   * @param papbEmptyId   The ID of the GPIO which is connected to the PAPBEmpty signal of the
   *                      VcInterface IP Core. The signal is high when there are no packets in the
   *                      external buffer memory (BRAM).
   * @param uioFile       UIO file providing access to the PAPB bus
   * @param mapNum        Map number of UIO map associated with this virtual channel
   */
  PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, gpioId_t papbEmptyId,
                  std::string uioFile, int mapNum);
  virtual ~PapbVcInterface();

  bool isBusy() const override;
  /**
   *
   * @param data
   * @param size
   * @return returnvalue::OK on successfull write, PAPB_BUSY if PAPB is busy.
   */
  ReturnValue_t write(const uint8_t* data, size_t size) override;

  void cancelTransfer() override;

  ReturnValue_t initialize() override;

 private:
  static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_IP_CORE_BRIDGE;

  static const ReturnValue_t PAPB_BUSY = MAKE_RETURN_CODE(0xA0);

  enum ByteWidthCfg : uint32_t { ONE = 0b00, TWO = 0b01, THREE = 0b10, FOUR = 0b11 };
  /**
   * Configuration bits:
   * bit[1:0]: Size of data (1,2,3 or 4 bytes). 1 Byte <=> b00
   * bit[2]: Set this bit to 1 to abort a transferred packet
   * bit[3]: Signals to VcInterface the start of a new telemetry packet
   */
  static constexpr uint32_t CONFIG_DATA_INPUT = 0b00001000;

  /**
   * Abort a transferred packet.
   */
  static constexpr uint32_t CONFIG_ABORT = 0b00000100;

  /**
   * Writing this word to the VcInterface base address signals to the virtual channel interface
   * that a complete tm packet has been transferred.
   */
  static constexpr uint32_t CONFIG_END = 0x0;

  /**
   * Writing to this offset within the memory space of a virtual channel will insert data for
   * encoding to the external buffer memory of the PTME IP Core.
   * The address offset is 0x400 (= 4 * 256)
   */
  static const int DATA_REG_OFFSET = 256;

  static constexpr long int FIRST_DELAY_PAPB_POLLING_NS = 10;
  static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40;

  LinuxLibgpioIF* gpioComIF = nullptr;

  /** Pulled to low when virtual channel not ready to receive data */
  gpioId_t papbBusyId = gpio::NO_GPIO;
  /** High when external buffer memory of virtual channel is empty */
  gpioId_t papbEmptyId = gpio::NO_GPIO;

  std::string uioFile;
  int mapNum = 0;
  mutable struct timespec nextDelay = {.tv_sec = 0, .tv_nsec = 0};
  const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 10};
  mutable struct timespec remDelay;

  volatile uint32_t* vcBaseReg = nullptr;

  uint32_t vcOffset = 0;

  /**
   * @brief   This function sends the config byte to the virtual channel of the PTME IP Core
   *          to initiate a packet transfer.
   */
  void startPacketTransfer(ByteWidthCfg initWidth);

  void abortPacketTransfer();

  /**
   * @brief   This function sends the config byte to the virtual channel interface of the PTME
   *          IP Core to signal the end of a  packet transfer.
   */
  void completePacketTransfer();

  /**
   * @brief   This function reads the papb busy signal indicating whether the virtual channel
   *          interface is ready to receive more data or not. PAPB is ready when
   *          PAPB_Busy_N == '1'.
   *
   * @return  returnvalue::OK when ready to receive data else PAPB_BUSY.
   */
  inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries, bool checkReadyState) const;

  /**
   * @brief   This function can be used for debugging to check whether there are packets in
   *          the packet buffer of the virtual channel or not.
   */
  void isVcInterfaceBufferEmpty();

  /**
   * @brief   This function sends a complete telemetry transfer frame data field (1105 bytes)
   *          to the papb interface of the PTME IP Core. Can be used to test the implementation.
   */
  ReturnValue_t sendTestFrame();
};

#endif /* LINUX_OBC_PAPBVCINTERFACE_H_ */