#ifndef BSP_Q7S_DEVICES_STRHELPER_H_
#define BSP_Q7S_DEVICES_STRHELPER_H_

#include <string>

#include "ArcsecDatalinkLayer.h"
#include "OBSWConfig.h"

#ifdef XIPHOS_Q7S
#include "bsp_q7s/fs/SdCardManager.h"
#endif

#include "fsfw/devicehandlers/CookieIF.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/osal/linux/BinarySemaphore.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw_hal/linux/serial/SerialComIF.h"

extern "C" {
#include "thirdparty/arcsec_star_tracker/client/generated/actionreq.h"
#include "thirdparty/arcsec_star_tracker/common/generated/tmtcstructs.h"
}

/**
 * @brief   Helper class for the star tracker handler to accelerate large data transfers.
 *
 * @author  J. Meier
 */
class StrHelper : public SystemObject, public ExecutableObjectIF {
 public:
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::STR_HELPER;

  //! [EXPORT] : [COMMENT] Image upload failed
  static const Event IMAGE_UPLOAD_FAILED = MAKE_EVENT(0, severity::LOW);
  //! [EXPORT] : [COMMENT] Image download failed
  static const Event IMAGE_DOWNLOAD_FAILED = MAKE_EVENT(1, severity::LOW);
  //! [EXPORT] : [COMMENT] Uploading image to star tracker was successfulop
  static const Event IMAGE_UPLOAD_SUCCESSFUL = MAKE_EVENT(2, severity::LOW);
  //! [EXPORT] : [COMMENT] Image download was successful
  static const Event IMAGE_DOWNLOAD_SUCCESSFUL = MAKE_EVENT(3, severity::LOW);
  //! [EXPORT] : [COMMENT] Finished flash write procedure successfully
  static const Event FLASH_WRITE_SUCCESSFUL = MAKE_EVENT(4, severity::LOW);
  //! [EXPORT] : [COMMENT] Finished flash read procedure successfully
  static const Event FLASH_READ_SUCCESSFUL = MAKE_EVENT(5, severity::LOW);
  //! [EXPORT] : [COMMENT] Flash read procedure failed
  static const Event FLASH_READ_FAILED = MAKE_EVENT(6, severity::LOW);
  //! [EXPORT] : [COMMENT] Firmware update was successful
  static const Event FIRMWARE_UPDATE_SUCCESSFUL = MAKE_EVENT(7, severity::LOW);
  //! [EXPORT] : [COMMENT] Firmware update failed
  static const Event FIRMWARE_UPDATE_FAILED = MAKE_EVENT(8, severity::LOW);
  //! [EXPORT] : [COMMENT] Failed to read communication interface reply data
  //! P1: Return code of failed communication interface read call
  //! P1: Upload/download position for which the read call failed
  static const Event STR_HELPER_READING_REPLY_FAILED = MAKE_EVENT(9, severity::LOW);
  //! [EXPORT] : [COMMENT] Unexpected stop of decoding sequence
  //! P1: Return code of failed communication interface read call
  //! P1: Upload/download position for which the read call failed
  static const Event STR_HELPER_COM_ERROR = MAKE_EVENT(10, severity::LOW);
  //! [EXPORT] : [COMMENT] Star tracker did not send replies (maybe device is powered off)
  //! P1: Position of upload or download packet for which no reply was sent
  static const Event STR_HELPER_NO_REPLY = MAKE_EVENT(11, severity::LOW);
  //! [EXPORT] : [COMMENT] Error during decoding of received reply occurred
  // P1: Return value of decoding function
  // P2: Position of upload/download packet, or address of flash write/read request
  static const Event STR_HELPER_DEC_ERROR = MAKE_EVENT(12, severity::LOW);
  //! [EXPORT] : [COMMENT] Position mismatch
  //! P1: The expected position and thus the position for which the image upload/download failed
  static const Event POSITION_MISMATCH = MAKE_EVENT(13, severity::LOW);
  //! [EXPORT] : [COMMENT] Specified file does not exist
  //! P1: Internal state of str helper
  static const Event STR_HELPER_FILE_NOT_EXISTS = MAKE_EVENT(14, severity::LOW);
  //! [EXPORT] : [COMMENT] Sending packet to star tracker failed
  //! P1: Return code of communication interface sendMessage function
  //! P2: Position of upload/download packet, or address of flash write/read request for which
  //! sending failed
  static const Event STR_HELPER_SENDING_PACKET_FAILED = MAKE_EVENT(15, severity::LOW);
  //! [EXPORT] : [COMMENT] Communication interface requesting reply failed
  //! P1: Return code of failed request
  //! P1: Upload/download position, or address of flash write/read request for which transmission
  //! failed
  static const Event STR_HELPER_REQUESTING_MSG_FAILED = MAKE_EVENT(16, severity::LOW);

  StrHelper(object_id_t objectId);
  virtual ~StrHelper();

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

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

  /**
   * @brief   Starts sequence to upload image to star tracker
   *
   * @param uploadImage_  Name including absolute path of the image to upload. Must be previously
   *                      transferred to the OBC with the CFDP protocol.
   */
  ReturnValue_t startImageUpload(std::string uploadImage_);

  /**
   * @brief   Calling this function initiates the download of an image from the star tracker.
   *
   * @param path  Path where downloaded image will be stored
   */
  ReturnValue_t startImageDownload(std::string path);

  /**
   * @brief   Will start the firmware update
   *
   * @param fullname    Full name including absolute path of file containing firmware
   *                    update.
   */
  ReturnValue_t startFirmwareUpdate(std::string fullname);

  /**
   * @brief   Starts the flash read procedure
   *
   * @param path                 Path where file with read flash data will be created
   * @param startRegion          Region form where to start reading
   * @param length               Number of bytes to read from flash
   */
  ReturnValue_t startFlashRead(std::string path, uint8_t startRegion, uint32_t length);

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

  /**
   * @brief   Changes the dafault name of downloaded images
   */
  void setDownloadImageName(std::string filename);

  /**
   * @brief   Sets the name of the file which will be created to store the data read from flash
   */
  void setFlashReadFilename(std::string filename);

  /**
   * @brief	Disables timestamp generation when new file is created
   */
  void disableTimestamping();

  /**
   * @brief	Enables timestamp generation when new file is created
   */
  void enableTimestamping();

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

  //! [EXPORT] : [COMMENT] SD card specified in path string not mounted
  static const ReturnValue_t SD_NOT_MOUNTED = MAKE_RETURN_CODE(0xA0);
  //! [EXPORT] : [COMMENT] Specified file does not exist on filesystem
  static const ReturnValue_t FILE_NOT_EXISTS = MAKE_RETURN_CODE(0xA1);
  //! [EXPORT] : [COMMENT] Specified path does not exist
  static const ReturnValue_t PATH_NOT_EXISTS = MAKE_RETURN_CODE(0xA2);
  //! [EXPORT] : [COMMENT] Failed to create download image or read flash file
  static const ReturnValue_t FILE_CREATION_FAILED = MAKE_RETURN_CODE(0xA3);
  //! [EXPORT] : [COMMENT] Region in flash write/read reply does not match expected region
  static const ReturnValue_t REGION_MISMATCH = MAKE_RETURN_CODE(0xA4);
  //! [EXPORT] : [COMMENT] Address in flash write/read reply does not match expected address
  static const ReturnValue_t ADDRESS_MISMATCH = MAKE_RETURN_CODE(0xA5);
  //! [EXPORT] : [COMMENT] Length in flash write/read reply does not match expected length
  static const ReturnValue_t LENGTH_MISMATCH = MAKE_RETURN_CODE(0xA6);
  //! [EXPORT] : [COMMENT] Status field in reply signals error
  static const ReturnValue_t STATUS_ERROR = MAKE_RETURN_CODE(0xA7);
  //! [EXPORT] : [COMMENT] Reply has invalid type ID (should be of action reply type)
  static const ReturnValue_t INVALID_TYPE_ID = MAKE_RETURN_CODE(0xA8);

  // Size of one image part which can be sent per action request
  static const size_t SIZE_IMAGE_PART = 1024;
  static const uint32_t FLASH_REGION_SIZE = 0x20000;

  struct ImageDownload {
    static const uint32_t LAST_POSITION = 4095;
  };

  static const uint32_t MAX_POLLS = 10000;

  static const uint8_t ACTION_DATA_OFFSET = 2;
  static const uint8_t POS_OFFSET = 2;
  static const uint8_t IMAGE_DATA_OFFSET = 5;
  static const uint8_t FLASH_READ_DATA_OFFSET = 8;
  static const uint8_t REGION_OFFSET = 2;
  static const uint8_t ADDRESS_OFFSET = 3;
  static const uint8_t LENGTH_OFFSET = 7;
  static const size_t CHUNK_SIZE = 1024;
  static const size_t CONFIG_MAX_DOWNLOAD_RETRIES = 3;
  static const uint32_t FLASH_ERASE_DELAY = 500;

  enum class InternalState { IDLE, UPLOAD_IMAGE, DOWNLOAD_IMAGE, FLASH_READ, FIRMWARE_UPDATE };

  InternalState internalState = InternalState::IDLE;

  ArcsecDatalinkLayer datalinkLayer;

  BinarySemaphore semaphore;

  struct UploadImage {
    // Name including absolute path of image to upload
    std::string uploadFile;
  };
  UploadImage uploadImage;

  struct DownloadImage {
    // Path where the downloaded image will be stored
    std::string path;
    // Default name of downloaded image, can be changed via command
    std::string filename = "image.bin";
  };
  DownloadImage downloadImage;

  struct FlashWrite {
    // File which contains data to write when executing the flash write command
    std::string fullname;
    // The first region to write to
    uint8_t firstRegion = 0;
    // Maximum region the flash write command is allowed to write to
    uint8_t lastRegion = 0;
    // Will be set with the flash write command and specifies the start address where to write the
    // flash data to
    uint32_t address = 0;
  };
  FlashWrite flashWrite;

  struct FlashRead {
    // Path where the file containing the read data will be stored
    std::string path = "";
    // Default name of file containing the data read from flash, can be changed via command
    std::string filename = "flashread.bin";
    // Will be set with the flash read command
    uint8_t startRegion = 0;
    // Number of bytes to read from flash
    uint32_t size = 0;
  };
  FlashRead flashRead;

#ifdef XIPHOS_Q7S
  SdCardManager* sdcMan = nullptr;
#endif

  uint8_t commandBuffer[startracker::MAX_FRAME_SIZE];

  bool terminate = false;

#ifdef EGSE
  bool timestamping = false;
#else
  bool timestamping = true;
#endif

  /**
   * UART communication object responsible for low level access of star tracker
   * Must be set by star tracker handler
   */
  SerialComIF* uartComIF = nullptr;
  // Communication cookie. Must be set by the star tracker handler
  CookieIF* comCookie = nullptr;

  // Queue id of raw data receiver
  MessageQueueId_t rawDataReceiver = MessageQueueIF::NO_QUEUE;

  /**
   * @brief   Performs image uploading
   */
  ReturnValue_t performImageUpload();

  /**
   * @brief Performs firmware update
   *
   * @return    returnvalue::OK if successful, otherwise error return value
   */
  ReturnValue_t performFirmwareUpdate();

  /**
   * @brief   Performs download of last taken image from the star tracker.
   *
   * @details Download is split over multiple packets transporting each a maximum of 1024 bytes.
   *          In case the download of one position fails, the same packet will be again
   *          requested. If the download of the packet fails CONFIG_MAX_DOWNLOAD_RETRIES times,
   *          the download will be stopped.
   */
  ReturnValue_t performImageDownload();

  /**
   * @brief   Handles flash write procedure
   *
   * @param  ID of first region to write to
   *
   * @return returnvalue::OK if successful, otherwise returnvalue::FAILED
   */
  ReturnValue_t performFlashWrite();

  /**
   * @brief   Sends a sequence of commands to the star tracker to read larger parts from the
   *          flash memory.
   */
  ReturnValue_t performFlashRead();

  /**
   * @brief   Sends packet to the star tracker and reads reply by using the communication
   *          interface
   *
   * @param size        Size of data beforehand written to the commandBuffer
   * @param parameter   Parameter 2 of trigger event function
   * @param delayMs     Delay in milliseconds between send and receive call
   *
   * @return  returnvalue::OK if successful, otherwise returnvalue::FAILED
   */
  ReturnValue_t sendAndRead(size_t size, uint32_t parameter, uint32_t delayMs = 0);

  /**
   * @brief   Checks the header (type id and status fields) of the action reply
   *
   * @return  returnvalue::OK if reply confirms success of packet transfer, otherwise REUTRN_FAILED
   */
  ReturnValue_t checkActionReply();

  /**
   * @brief   Checks the position field in a star tracker upload/download reply.
   *
   * @param expectedPosition  Value of expected position
   *
   * @return returnvalue::OK if received position matches expected position, otherwise
   * returnvalue::FAILED
   */
  ReturnValue_t checkReplyPosition(uint32_t expectedPosition);

#ifdef XIPHOS_Q7S
  /**
   * @brief   Checks if a path points to an sd card and whether the SD card is monuted.
   *
   * @return  SD_NOT_MOUNTED id SD card is not mounted, otherwise returnvalue::OK
   */
  ReturnValue_t checkPath(std::string name);
#endif

  /**
   * @brief Unlocks a range of flash regions
   *
   * @param from    First region in range to unlock
   * @param to      Last region in range to unlock
   *
   */
  ReturnValue_t unlockAndEraseRegions(uint32_t from, uint32_t to);
};

#endif /* BSP_Q7S_DEVICES_STRHELPER_H_ */