#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H

#include <etl/queue.h>
#include <fsfw/cfdp/handler/SourceHandler.h>
#include <fsfw/ipc/CommandMessage.h>
#include <fsfw/timemanager/Countdown.h>

#include <utility>

#include "fsfw/cfdp/handler/DestHandler.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
#include "fsfw/util/SeqCountProvider.h"

struct FsfwHandlerParams {
  FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest,
                    StorageManagerIF& tcStore, StorageManagerIF& tmStore,
                    StorageManagerIF& ipcStore, MessageQueueIF& tmtcQueue,
                    MessageQueueIF& cfdpQueue)
      : objectId(objectId),
        vfs(vfs),
        packetDest(packetDest),
        tcStore(tcStore),
        tmStore(tmStore),
        ipcStore(ipcStore),
        tmtcQueue(tmtcQueue),
        cfdpQueue(cfdpQueue) {}
  object_id_t objectId{};
  HasFileSystemIF& vfs;
  AcceptsTelemetryIF& packetDest;
  StorageManagerIF& tcStore;
  StorageManagerIF& tmStore;
  StorageManagerIF& ipcStore;
  MessageQueueIF& tmtcQueue;
  MessageQueueIF& cfdpQueue;
};

struct CfdpHandlerCfg {
  CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg,
                 cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler,
                 cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList,
                 cfdp::RemoteConfigTableIF& remoteCfgProvider)
      : id(std::move(localId)),
        indicCfg(indicationCfg),
        packetInfoList(packetInfo),
        lostSegmentsList(lostSegmentsList),
        remoteCfgProvider(remoteCfgProvider),
        userHandler(userHandler),
        faultHandler(userFaultHandler) {}

  cfdp::EntityId id;
  cfdp::IndicationCfg indicCfg;
  cfdp::PacketInfoListBase& packetInfoList;
  cfdp::LostSegmentsListBase& lostSegmentsList;
  cfdp::RemoteConfigTableIF& remoteCfgProvider;
  cfdp::UserBase& userHandler;
  cfdp::FaultHandlerBase& faultHandler;
};

class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
 public:
  explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg,
                       const std::atomic_bool& throttleSignal);

  [[nodiscard]] const char* getName() const override;
  [[nodiscard]] uint32_t getIdentifier() const override;
  [[nodiscard]] MessageQueueId_t getRequestQueue() const override;

  ReturnValue_t initialize() override;
  [[noreturn]] ReturnValue_t performOperation(uint8_t operationCode) override;

 private:
  MessageQueueIF& pduQueue;
  MessageQueueIF& cfdpRequestQueue;
  bool throttlePeriodOngoing = false;

  cfdp::LocalEntityCfg localCfg;
  cfdp::RemoteConfigTableIF& remoteCfgProvider;
  cfdp::FsfwParams fsfwParams;
  SeqCountProviderU16 seqCntProvider;
  cfdp::DestHandler destHandler;
  cfdp::SourceHandler srcHandler;
  etl::queue<store_address_t, 16> putRequestQueue;

  StorageManagerIF& ipcStore;
  StorageManagerIF* tcStore = nullptr;
  StorageManagerIF* tmStore = nullptr;

  const std::atomic_bool& throttleSignal;

  ReturnValue_t handlePduPacketMessages();
  ReturnValue_t handlePduPacket(TmTcMessage& msg);
  ReturnValue_t handleCfdpRequest(CommandMessage& msg);
  ReturnValue_t handleCfdpMessages();
};

#endif  // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H