#ifndef LINUX_OBC_PDECCONFIG_H_
#define LINUX_OBC_PDECCONFIG_H_

#include <string>

#include "bsp_q7s/fs/SdCardManager.h"
#include "bsp_q7s/memory/LocalParameterHandler.h"
#include "fsfw/returnvalues/returnvalue.h"
#include "pdec.h"

/**
 * @brief   This class generates the configuration words for the configuration memory of the PDEC
 *          IP Cores.
 *
 * @details Fields are initialized according to specification in PDEC datasheet section 6.11.3.1
 *          PROM usage.
 *
 * @author  J. Meier
 */
class PdecConfig {
 public:
  /**
   * @brief	Constructor
   */
  PdecConfig();
  virtual ~PdecConfig();

  /**
   * @brief	Sets the memory base address pointer
   */
  void setMemoryBaseAddress(uint32_t* memoryBaseAddress_);

  /**
   * @brief	Will write the config to the PDEC configuration memory. New config
   * 				becomes active after resetting PDEC.
   */
  ReturnValue_t write();

  /**
   * @brief	Returns the value to write to the interrupt mask register. This
   * 				value defines which interrupts should be enabled/disabled.
   */
  uint32_t getImrReg();

  ReturnValue_t setPositiveWindow(uint8_t pw);
  ReturnValue_t setNegativeWindow(uint8_t nw);

  ReturnValue_t getPositiveWindow(uint8_t& positiveWindow);
  ReturnValue_t getNegativeWindow(uint8_t& negativeWindow);

 private:
  // TC transfer frame configuration parameters
  static const uint8_t VERSION_ID = 0;
  // BD Frames
  static const uint8_t BYPASS_FLAG = 1;
  static const uint8_t CONTROL_COMMAND_FLAG = 0;

  static const uint8_t VIRTUAL_CHANNEL = 0;
  static const uint8_t RESERVED_FIELD_A = 0;
  static const uint16_t SPACECRAFT_ID = 0x3DC;
  static const uint16_t DUMMY_BITS = 0;
  static const uint8_t HIGH_AU_MAP_ID = 0xF;
  static const uint8_t ENABLE_DERANDOMIZER = 1;

  static const uint8_t CONFIG_WORDS_NUM = 2;

  // 0x200 / 4 = 0x80
  static const uint32_t FRAME_HEADER_OFFSET = 0x80;

  static const uint32_t MAP_ADDR_LUT_OFFSET = 0xA0;
  static const uint32_t MAP_CLK_FREQ_OFFSET = 0x90;
  // MAP clock frequency. Must be a value between 1 and 13 otherwise the TC segment will be
  // discarded
  static const uint8_t MAP_CLK_FREQ = 2;

  static const uint8_t MAX_MAP_ADDR = 63;
  // Writing this to the map address in the look up table will invalidate a MAP ID.
  static const uint8_t NO_DESTINATION = 0;
  static const uint8_t VALID_POSITION = 6;
  static const uint8_t PARITY_POSITION = 7;

  /**
   * TCs with map addresses (also know as Map IDs) assigned to this channel will be stored in
   * the PDEC memory.
   */
  static const uint8_t PM_BUFFER = 7;

  uint32_t* memoryBaseAddress = nullptr;

  // Pointer to object providing access to persistent configuration parameters
  LocalParameterHandler localParameterHandler;

  uint32_t configWords[CONFIG_WORDS_NUM];
  bool enableTcNewIrq = true;
  bool enableTcAbortIrq = true;
  bool enableNewFarIrq = true;

  ReturnValue_t initializePersistentParameters();
  /**
   * @brief	If the json file containing the persistent config parameters does
   * 				not exist it will be created here.
   */
  ReturnValue_t createPersistentConfig();

  ReturnValue_t writeFrameHeaderFirstOctet();
  ReturnValue_t writeFrameHeaderSecondOctet();
  void writeMapConfig();

  /**
   * @brief   This function calculates the entry for the configuration of the MAP ID routing.
   *
   * @param mapAddr   The MAP ID to configure
   * @param moduleId  The destination module where all TCs with the map id mapAddr will be routed
   *                  to.
   *
   * @details The PDEC has different modules where the TCs can be routed to. A lookup table is
   *          used which links the MAP ID field to the destination module. The entry for this
   *          lookup table is created by this function and must be stored in the configuration
   *          memory region of the PDEC. The entry has a specific format
   */
  uint8_t calcMapAddrEntry(uint8_t moduleId);

  /**
   * @brief   This functions calculates the odd parity of the bits in number.
   *
   * @param number    The number from which to calculate the odd parity.
   */
  uint8_t getOddParity(uint8_t number);
};

#endif /* LINUX_OBC_PDECCONFIG_H_ */