#ifndef BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_
#define BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_

#include <string>

#include "fsfw/devicehandlers/CookieIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/osal/linux/BinarySemaphore.h"
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/tmtcservices/SourceSequenceCounter.h"
#include "fsfw_hal/linux/uart/UartComIF.h"
#include "linux/devices/devicedefinitions/PlocMPSoCDefinitions.h"
#ifdef XIPHOS_Q7S
#include "bsp_q7s/memory/SdCardManager.h"
#endif

/**
 * @brief   Helper class for MPSoC of PLOC intended to accelerate large data transfers between
 *          MPSoC and OBC.
 * @author  J. Meier
 */
class PlocMPSoCHelper : public SystemObject, public ExecutableObjectIF, public HasReturnvaluesIF {
 public:
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PLOC_MPSOC_HELPER;

  //! [EXPORT] : [COMMENT] Flash write fails
  static const Event MPSOC_FLASH_WRITE_FAILED = MAKE_EVENT(0, severity::LOW);
  //! [EXPORT] : [COMMENT] Flash write successful
  static const Event MPSOC_FLASH_WRITE_SUCCESSFUL = MAKE_EVENT(1, severity::LOW);
  //! [EXPORT] : [COMMENT] Communication interface returned failure when trying to send the command
  //! ot the PLOC
  //! P1: Return value returned by the communication interface sendMessage function
  //! P2: Internal state of MPSoC helper
  static const Event SENDING_COMMAND_FAILED = MAKE_EVENT(2, severity::LOW);
  //! [EXPORT] : [COMMENT] Request receive message of communication interface failed
  //! P1: Return value returned by the communication interface requestReceiveMessage function
  //! P2: Internal state of MPSoC helper
  static const Event MPSOC_HELPER_REQUESTING_REPLY_FAILED = MAKE_EVENT(3, severity::LOW);
  //! [EXPORT] : [COMMENT] Reading receive message of communication interface failed
  //! P1: Return value returned by the communication interface readingReceivedMessage function
  //! P2: Internal state of MPSoC helper
  static const Event MPSOC_HELPER_READING_REPLY_FAILED = MAKE_EVENT(4, severity::LOW);
  //! [EXPORT] : [COMMENT] Did not receive acknowledgement report
  //! P1: Number of bytes missing
  //! P2: Internal state of MPSoC helper
  static const Event MISSING_ACK = MAKE_EVENT(5, severity::LOW);
  //! [EXPORT] : [COMMENT] Did not receive execution report
  //! P1: Number of bytes missing
  //! P2: Internal state of MPSoC helper
  static const Event MISSING_EXE = MAKE_EVENT(6, severity::LOW);
  //! [EXPORT] : [COMMENT] Received acknowledgement failure report
  //! P1: Internal state of MPSoC
  static const Event ACK_FAILURE_REPORT = MAKE_EVENT(7, severity::LOW);
  //! [EXPORT] : [COMMENT] Received execution failure report
  //! P1: Internal state of MPSoC
  static const Event EXE_FAILURE_REPORT = MAKE_EVENT(8, severity::LOW);
  //! [EXPORT] : [COMMENT] Expected acknowledgement report but received space packet with other apid
  //! P1: Apid of received space packet
  //! P2: Internal state of MPSoC
  static const Event ACK_INVALID_APID = MAKE_EVENT(9, severity::LOW);
  //! [EXPORT] : [COMMENT] Expected execution report but received space packet with other apid
  //! P1: Apid of received space packet
  //! P2: Internal state of MPSoC
  static const Event EXE_INVALID_APID = MAKE_EVENT(10, severity::LOW);
  //! [EXPORT] : [COMMENT] Received sequence count does not match expected sequence count
  //! P1: Expected sequence count
  //! P2: Received sequence count
  static const Event MPSOC_HELPER_SEQ_CNT_MISMATCH = MAKE_EVENT(11, severity::LOW);

  PlocMPSoCHelper(object_id_t objectId);
  virtual ~PlocMPSoCHelper();

  ReturnValue_t initialize() override;
  ReturnValue_t performOperation(uint8_t operationCode = 0) override;

  ReturnValue_t setComIF(DeviceCommunicationIF* communicationInterface_);
  void setComCookie(CookieIF* comCookie_);

  /**
   * @brief   Starts flash write sequence
   *
   * @param obcFile   File where to read from the data
   * @param mpsocFile The file of the MPSoC where should be written to
   *
   * @return  RETURN_OK if successful, otherwise error return value
   */
  ReturnValue_t startFlashWrite(std::string obcFile, std::string mpsocFile);

  /**
   * @brief   Can be used to interrupt a running data transfer.
   */
  void stopProcess();

  /**
   * @brief   Sets the sequence count object responsible for the sequence count handling
   */
  void setSequenceCount(SourceSequenceCounter* sequenceCount_);

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

  //! [EXPORT] : [COMMENT] File accidentally close
  static const ReturnValue_t FILE_CLOSED_ACCIDENTALLY = MAKE_RETURN_CODE(0xA0);

  // Maximum number of times the communication interface retries polling data from the reply
  // buffer
  static const int RETRIES = 10000;

  struct FlashWrite {
    std::string obcFile;
    std::string mpsocFile;
  };

  struct FlashWrite flashWrite;

  enum class InternalState { IDLE, FLASH_WRITE, FLASH_READ };

  InternalState internalState = InternalState::IDLE;

  BinarySemaphore semaphore;
#ifdef XIPHOS_Q7S
  SdCardManager* sdcMan = nullptr;
#endif
  uint8_t commandBuffer[mpsoc::MAX_COMMAND_SIZE];

  bool terminate = false;

  /**
   * Communication interface of MPSoC responsible for low level access. Must be set by the
   * MPSoC Handler.
   */
  UartComIF* uartComIF = nullptr;
  // Communication cookie. Must be set by the MPSoC Handler
  CookieIF* comCookie = nullptr;
  // Sequence count, must be set by Ploc MPSoC Handler
  SourceSequenceCounter* sequenceCount;

  ReturnValue_t resetHelper();
  ReturnValue_t performFlashWrite();
  ReturnValue_t flashfopen();
  ReturnValue_t flashfclose();
  ReturnValue_t handlePacketTransmission(mpsoc::TcBase& tc);
  ReturnValue_t sendCommand(mpsoc::TcBase& tc);
  ReturnValue_t receive(uint8_t* data, size_t* readBytes, size_t requestBytes);
  ReturnValue_t handleAck();
  ReturnValue_t handleExe();
  void handleAckApidFailure(uint16_t apid);
  void handleExeApidFailure(uint16_t apid);
  ReturnValue_t handleTmReception(mpsoc::TmPacket* tmPacket, size_t remainingBytes);
};

#endif /* BSP_Q7S_DEVICES_PLOCMPSOCHELPER_H_ */