#ifndef MISSION_TMTC_TMSTORETASKBASE_H_
#define MISSION_TMTC_TMSTORETASKBASE_H_

#include <fsfw/modes/HasModesIF.h>
#include <fsfw/subsystem/ModeTreeChildIF.h>
#include <fsfw/subsystem/ModeTreeConnectionIF.h>
#include <fsfw/timemanager/CdsShortTimeStamper.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/com/VirtualChannel.h>
#include <mission/tmtc/PersistentTmStoreWithTmQueue.h>

/**
 * Generic class which composes a Virtual Channel and a persistent TM stores. This allows dumping
 * the TM store into the virtual channel directly.
 */
class TmStoreTaskBase : public SystemObject,
                        public HasModesIF,
                        public ModeTreeChildIF,
                        public ModeTreeConnectionIF {
 public:
  struct DumpContext {
    DumpContext(Event eventIfDone, Event eventIfCancelled)
        : eventIfDone(eventIfDone), eventIfCancelled(eventIfCancelled) {}
    void reset() {
      numberOfDumpedPackets = 0;
      dumpedBytes = 0;
      vcBusyDuringDump = false;
      packetWasDumped = false;
      bytesDumpedAtLastDelay = 0;
      ptmeBusyCounter = 0;
    }
    const Event eventIfDone;
    const Event eventIfCancelled;
    size_t numberOfDumpedPackets = 0;
    size_t bytesDumpedAtLastDelay = 0;
    size_t dumpedBytes = 0;
    uint32_t ptmeBusyCounter = 0;
    bool packetWasDumped = false;
    bool vcBusyDuringDump = false;
  };

  TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore, VirtualChannel& channel,
                  SdCardMountedIF& sdcMan, const std::atomic_bool& ptmeLocked);

  ReturnValue_t initialize() override;
  ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;

 protected:
  ModeHelper modeHelper;
  MessageQueueIF* requestQueue;
  StorageManagerIF& ipcStore;
  PusTmReader tmReader;
  CdsShortTimeStamper timeReader;
  VirtualChannel& channel;
  SdCardMountedIF& sdcMan;
  const std::atomic_bool& ptmeLocked;

  Mode_t mode = HasModesIF::MODE_OFF;
  Countdown sdCardCheckCd = Countdown(800);
  // 20 minutes are allowed as maximum dump time.
  Countdown cancelDumpCd = Countdown(60 * 20 * 1000);
  // If the TM sink is busy for 1 minute for whatever reason, cancel the dump.
  Countdown tmSinkBusyCd = Countdown(60 * 1000);

  bool storesInitialized = false;
  bool fileHasSwapped = false;

  void readCommandQueue(void);

  virtual bool initStoresIfPossible() = 0;
  virtual void startTransition(Mode_t mode, Submode_t submode) = 0;

  void cancelDump(DumpContext& ctx, PersistentTmStore& store, bool isTxOn);
  /**
   *
   * Handling for one store. Returns if anything was done.
   * @param store
   * @return
   */
  bool handleOneStore(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext);

  ReturnValue_t handleOneDump(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext,
                              bool& dumpPerformed);
  ReturnValue_t performDump(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext,
                            bool& dumpPerformed);

  /**
   * Occasionally check whether SD card is okay to be used. If not, poll whether it is ready to
   * be used again and re-initialize stores. Returns whether store is okay to be used.
   */
  bool cyclicStoreCheck();

  MessageQueueId_t getCommandQueue() const override;

  void getMode(Mode_t* mode, Submode_t* submode) override;

  ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
                                 uint32_t* msToReachTheMode) override;
  void dumpDoneHandler(PersistentTmStore& store, DumpContext& dumpContext);

  void announceMode(bool recursive) override;
  object_id_t getObjectId() const override;
  const HasHealthIF* getOptHealthIF() const override;
  const HasModesIF& getModeIF() const override;
  ModeTreeChildIF& getModeTreeChildIF() override;
};

#endif /* MISSION_TMTC_TMSTORETASKBASE_H_ */