#ifndef BSP_Q7S_CORE_CORECONTROLLER_H_
#define BSP_Q7S_CORE_CORECONTROLLER_H_

#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/SimpleRingBuffer.h>
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
#include <fsfw/parameters/ParameterHelper.h>
#include <fsfw/parameters/ReceivesParameterMessagesIF.h>
#include <libxiphos.h>
#include <mission/acs/archive/GPSDefinitions.h>
#include <mission/utility/trace.h>

#include <atomic>
#include <cstddef>

#include "CoreDefinitions.h"
#include "OBSWConfig.h"
#include "bsp_q7s/fs/SdCardManager.h"
#include "events/subsystemIdRanges.h"
#include "fsfw/controller/ExtendedControllerBase.h"
#include "mission/sysDefs.h"

class Timer;
class SdCardManager;

struct RebootFile {
  static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;

  bool enabled = true;
  size_t maxCount = DEFAULT_MAX_BOOT_CNT;
  uint32_t img00Cnt = 0;
  uint32_t img01Cnt = 0;
  uint32_t img10Cnt = 0;
  uint32_t img11Cnt = 0;
  bool img00Lock = false;
  bool img01Lock = false;
  bool img10Lock = false;
  bool img11Lock = false;
  uint32_t* relevantBootCnt = &img00Cnt;
  bool bootFlag = false;
  xsc::Chip lastChip = xsc::Chip::CHIP_0;
  xsc::Copy lastCopy = xsc::Copy::COPY_0;
  xsc::Chip mechanismNextChip = xsc::Chip::NO_CHIP;
  xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY;
};

class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
 public:
  enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS };

  static xsc::Chip CURRENT_CHIP;
  static xsc::Copy CURRENT_COPY;

  static constexpr char CHIP_PROT_SCRIPT[] = "get-chip-prot-status.sh";
  static constexpr char CHIP_STATE_FILE[] = "/tmp/chip_prot_status.txt";
  static constexpr char CURR_COPY_FILE[] = "/tmp/curr_copy.txt";

  const std::string VERSION_FILE =
      "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME);
  const std::string REBOOT_FILE =
      "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME);
  const std::string BACKUP_TIME_FILE =
      "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_FILE_NAME);

  static constexpr char CHIP_0_COPY_0_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-nom-rootfs";
  static constexpr char CHIP_0_COPY_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-gold-rootfs";
  static constexpr char CHIP_1_COPY_0_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi1-nom-rootfs";
  static constexpr char CHIP_1_COPY_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi1-gold-rootfs";
  static constexpr char LIST_DIR_DUMP_WORK_FILE[] = "/tmp/dir_listing.tmp";

  static constexpr dur_millis_t INIT_SD_CARD_CHECK_TIMEOUT = 5000;
  static constexpr dur_millis_t DEFAULT_SD_CARD_CHECK_TIMEOUT = 60000;

  CoreController(object_id_t objectId, bool enableHkSet);
  virtual ~CoreController();

  ReturnValue_t initialize() override;

  ReturnValue_t initializeAfterTaskCreation() override;

  ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
                              const uint8_t* data, size_t size) override;

  ReturnValue_t handleCommandMessage(CommandMessage* message) override;
  void performControlOperation() override;

  /**
   * Generate a file containing the chip lock/unlock states inside /tmp/chip_prot_status.txt
   * @return
   */
  static ReturnValue_t generateChipStateFile();
  static ReturnValue_t incrementAllocationFailureCount();
  static void getCurrentBootCopy(xsc::Chip& chip, xsc::Copy& copy);
  static const char* getXscMountDir(xsc::Chip chip, xsc::Copy copy);

  ReturnValue_t updateProtInfo(bool regenerateChipStateFile = true);

  /**
   * Checks whether the target chip and copy are write protected and protect set them to a target
   * state where applicable.
   * @param targetChip
   * @param targetCopy
   * @param protect       Target state
   * @param protOperationPerformed [out] Can be used to determine whether any operation
   * was performed
   * @param updateProtFile Specify whether the protection info file is updated
   * @return
   */
  ReturnValue_t setBootCopyProtection(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect,
                                      bool& protOperationPerformed, bool updateProtFile = true);

  bool sdInitFinished() const;

 private:
  static constexpr uint32_t BOOT_OFFSET_SECONDS = 15;
  static constexpr MutexIF::TimeoutType TIMEOUT_TYPE = MutexIF::TimeoutType::WAITING;
  static constexpr uint32_t MUTEX_TIMEOUT = 20;
  bool enableHkSet = false;
  GpsHyperion::FixMode gpsFix = GpsHyperion::FixMode::UNKNOWN;

  // States for SD state machine, which is used in non-blocking mode
  enum class SdStates {
    NONE,
    START,
    UPDATE_SD_INFO_START,
    SKIP_TWO_CYCLES_IF_SD_LOCKED,
    MOUNT_SELF,
    // Determine operations for other SD card, depending on redundancy configuration
    DETERMINE_OTHER,
    SET_STATE_OTHER,
    // Mount or unmount other
    MOUNT_UNMOUNT_OTHER,
    // Skip period because the shell command used to generate the info file sometimes is
    // missing the last performed operation if executed too early
    SKIP_CYCLE_BEFORE_INFO_UPDATE,
    UPDATE_SD_INFO_END,
    // SD initialization done
    IDLE
  };

  enum class SwUpdateSources { SD_0, SD_1, TMP_DIR };

  static constexpr bool BLOCKING_SD_INIT = false;

  SdCardManager* sdcMan = nullptr;
  MessageQueueIF* eventQueue = nullptr;

  uint8_t prefSdRaw = sd::SdCard::SLOT_0;
  SdStates sdFsmState = SdStates::START;
  SdStates fsmStateAfterDelay = SdStates::IDLE;
  enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT };

  struct SdFsmParams {
    SdCfgMode cfgMode = SdCfgMode::COLD_REDUNDANT;
    sd::SdCard active = sd::SdCard::NONE;
    sd::SdCard other = sd::SdCard::NONE;
    std::string activeChar = "0";
    std::string otherChar = "1";
    sd::SdState activeState = sd::SdState::OFF;
    sd::SdState otherState = sd::SdState::OFF;
    std::pair<bool, bool> mountSwitch = {true, true};
    // This flag denotes that the SD card usage is locked. This is relevant if SD cards go off
    // to leave appliation using the SD cards some time to detect the SD card is not usable anymore.
    // This is relevant if the active SD card is being switched. The SD card will also be locked
    // when going from hot-redundant mode to cold-redundant mode.
    bool lockSdCardUsage = false;
    bool commandPending = true;
    bool initFinished = false;
    SdCardManager::SdStatePair currentState;
    uint16_t cycleCount = 0;
    uint16_t skippedCyclesCount = 0;
  } sdInfo;

  struct SdCommanding {
    bool cmdPending = false;
    MessageQueueId_t commander = MessageQueueIF::NO_QUEUE;
    DeviceCommandId_t actionId;
  } sdCommandingInfo;

  struct DirListingDumpContext {
    bool active;
    bool firstDump;
    size_t dumpedBytes;
    size_t totalFileSize;
    size_t listingDataOffset;
    size_t maxDumpLen;
    uint32_t segmentIdx;
    MessageQueueId_t commander = MessageQueueIF::NO_QUEUE;
    DeviceCommandId_t actionId;
  };
  std::array<uint8_t, 1024> dirListingBuf{};
  DirListingDumpContext dumpContext{};

  RebootFile rebootFile = {};

  CommandExecutor cmdExecutor;
  SimpleRingBuffer cmdReplyBuf;
  DynamicFIFO<uint16_t> cmdRepliesSizes;
  bool shellCmdIsExecuting = false;
  MessageQueueId_t successRecipient = MessageQueueIF::NO_QUEUE;

  std::string currMntPrefix;
  bool timeFileInitDone = false;
  bool performOneShotSdCardOpsSwitch = false;
  uint8_t shortSdCardCdCounter = 0;
#if OBSW_THREAD_TRACING == 1
  uint32_t opCounter;
#endif
  Countdown sdCardCheckCd = Countdown(INIT_SD_CARD_CHECK_TIMEOUT);

  /**
   * Index 0: Chip 0 Copy 0
   * Index 1: Chip 0 Copy 1
   * Index 2: Chip 1 Copy 0
   * Index 3: Chip 1 Copy 1
   */
  std::array<bool, 4> protArray;
  PeriodicOperationDivider opDivider5;
  PeriodicOperationDivider opDivider10;

  PoolEntry<float> tempPoolEntry = PoolEntry<float>(0.0);
  PoolEntry<float> psVoltageEntry = PoolEntry<float>(0.0);
  PoolEntry<float> plVoltageEntry = PoolEntry<float>(0.0);

  core::HkSet hkSet;

  ParameterHelper paramHelper;

#if OBSW_SD_CARD_MUST_BE_ON == 1
  bool remountAttemptFlag = true;
#endif

  MessageQueueId_t getCommandQueue() const override;
  ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
                             ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues,
                             uint16_t startAtIndex) override;
  ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                        LocalDataPoolManager& poolManager) override;

  LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
  ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t* msToReachTheMode);
  void performMountedSdCardOperations();
  ReturnValue_t initVersionFile();

  ReturnValue_t initClockFromTimeFile();
  ReturnValue_t performSdCardCheck();
  ReturnValue_t backupTimeFileHandler();
  ReturnValue_t initBootCopyFile();
  ReturnValue_t initSdCardBlocking();
  bool startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mode, MessageQueueId_t commander,
                           DeviceCommandId_t actionId);
  void initPrint();

  ReturnValue_t sdStateMachine();
  void updateInternalSdInfo();
  ReturnValue_t sdCardSetup(sd::SdCard sdCard, sd::SdState targetState, std::string sdChar,
                            bool printOutput = true);
  ReturnValue_t executeSwUpdate(SwUpdateSources sourceDir, const uint8_t* data, size_t size);
  ReturnValue_t sdColdRedundantBlockingInit();

  void currentStateSetter(sd::SdCard sdCard, sd::SdState newState);
  void executeNextExternalSdCommand();
  void checkExternalSdCommandStatus();
  void performRebootFileHandling(bool recreateFile);

  ReturnValue_t actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy,
                                            const uint8_t* data, size_t size);
  ReturnValue_t actionListDirectoryDumpDirectly(ActionId_t actionId, MessageQueueId_t commandedBy,
                                                const uint8_t* data, size_t size);

  ReturnValue_t actionListDirectoryCommonCommandCreator(const uint8_t* data, size_t size,
                                                        std::ostringstream& oss);

  ReturnValue_t actionXscReboot(const uint8_t* data, size_t size);
  ReturnValue_t actionReboot(const uint8_t* data, size_t size);

  ReturnValue_t gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy, bool& protOpPerformed);

  ReturnValue_t handleProtInfoUpdateLine(std::string nextLine);
  int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect,
                                bool& protOperationPerformed, bool selfChip, bool selfCopy,
                                bool allChips, bool allCopies, uint8_t arrIdx);
  void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip,
                                 xsc::Copy& tgtCopy);
  void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy);
  void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy);
  bool parseRebootFile(std::string path, RebootFile& file);
  void rewriteRebootFile(RebootFile file);
  void announceBootCounts();
  void readHkData();
  void dirListingDumpHandler();
  bool isNumber(const std::string& s);
};

#endif /* BSP_Q7S_CORE_CORECONTROLLER_H_ */