#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 "OBSWConfig.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "linux/obc/VcInterfaceIF.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 VcInterfaceIF, public HasReturnvaluesIF {
 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();

  ReturnValue_t write(const uint8_t* data, size_t size) 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);

  /**
   * 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 transfered packet
   * bit[3]: Signals to VcInterface the start of a new telemetry packet
   */
  static const uint32_t CONFIG_START = 0x8;

  /**
   * Writing this word to the VcInterface base address signals to the virtual channel interface
   * that a complete tm packet has been transferred.
   */
  static const 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;

  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;

  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();

  /**
   * @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 endPacketTransfer();

  /**
   * @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  RETURN_OK when ready to receive data else PAPB_BUSY.
   */
  ReturnValue_t pollPapbBusySignal();

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