#ifndef MISSION_POWER_PCDUHANDLER_H_
#define MISSION_POWER_PCDUHANDLER_H_

#include <devices/powerSwitcherList.h>
#include <fsfw/datapool/PoolEntry.h>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/power/PowerSwitchIF.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/timemanager/CCSDSTime.h>
#include <mission/power/GomspaceDeviceHandler.h>
#include <mission/power/defs.h>
#include <mission/power/gsDefs.h>

/**
 * @brief	The PCDUHandler provides a compact interface to handle all devices related to the
 * 			control of power.
 * @details
 * This is necessary because the FSFW manages all power related functionalities via one
 * power object. This includes for example switching on and off of devices.
 */
class PcduHandler : public PowerSwitchIF,
                    public HasLocalDataPoolIF,
                    public SystemObject,
                    public ExecutableObjectIF {
 public:
  PcduHandler(object_id_t setObjectId, size_t cmdQueueSize = 20);
  virtual ~PcduHandler();

  virtual ReturnValue_t initialize() override;
  virtual ReturnValue_t performOperation(uint8_t counter) override;
  virtual void handleChangedDataset(sid_t sid, store_address_t storeId = store_address_t::invalid(),
                                    bool* clearMessage = nullptr) override;

  virtual ReturnValue_t sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) override;
  virtual ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) override;
  /**
   * @param switchNr
   * @return returnvalue::FAILED if the switch state has not been updated yet.
   */
  ReturnValue_t getSwitchState(uint8_t switchNr) const override;
  virtual ReturnValue_t getFuseState(uint8_t fuseNr) const override;
  virtual uint32_t getSwitchDelayMs(void) const override;
  virtual object_id_t getObjectId() const override;
  virtual LocalDataPoolManager* getHkManagerHandle() override;

  virtual MessageQueueId_t getCommandQueue() const override;
  virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                                LocalDataPoolManager& poolManager) override;
  virtual uint32_t getPeriodicOperationFrequency() const override;
  virtual ReturnValue_t initializeAfterTaskCreation() override;
  virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
  virtual void setTaskIF(PeriodicTaskIF* task_);

 private:
  uint32_t pstIntervalMs = 0;
  MutexIF* pwrLock = nullptr;
  static constexpr MutexIF::TimeoutType LOCK_TYPE = MutexIF::TimeoutType::WAITING;
  static constexpr uint32_t LOCK_TIMEOUT = 20;
  static constexpr char LOCK_CTX[] = "PcduHandler";

  /** Housekeeping manager. Handles updates of local pool variables. */
  LocalDataPoolManager poolManager;

  P60Dock::CoreHkSet p60CoreHk;

  /** Hk table dataset of PDU1 */
  PDU1::Pdu1CoreHk pdu1CoreHk;
  /**
   * The dataset holding the hk table of PDU2. This dataset is a copy of the PDU2 HK dataset
   * of the PDU2Handler. Each time the PDU2Handler updates his HK dataset, a copy is sent
   * to this object via a HousekeepingMessage.
   */
  PDU2::Pdu2CoreHk pdu2CoreHk;

  pcdu::SwitcherStates switcherSet;

  PoolEntry<uint8_t> pdu1Switches =
      PoolEntry<uint8_t>(pcdu::INIT_SWITCHES_PDU1.data(), pcdu::INIT_SWITCHES_PDU1.size());
  PoolEntry<uint8_t> pdu2Switches =
      PoolEntry<uint8_t>(pcdu::INIT_SWITCHES_PDU2.data(), pcdu::INIT_SWITCHES_PDU2.size());
  PoolEntry<uint8_t> p60Dock5VSwitch = PoolEntry<uint8_t>();

  /** The timeStamp of the current pdu2HkTableDataset */
  CCSDSTime::CDS_short timeStampPdu2HkDataset;

  /** The timeStamp of the current pdu1HkTableDataset */
  CCSDSTime::CDS_short timeStampPdu1HkDataset;

  uint8_t SWITCH_STATE_UNKNOWN = 2;
  uint8_t switchStates[pcdu::NUMBER_OF_SWITCHES];
  /**
   * Pointer to the IPCStore.
   * This caches the pointer received from the objectManager in the constructor.
   */
  StorageManagerIF* IPCStore = nullptr;

  /**
   * Message queue to communicate with other objetcs. Used for example to receive
   * local pool messages from ACU, PDU1 and PDU2.
   */
  MessageQueueIF* commandQueue = nullptr;

  size_t cmdQueueSize;
  bool firstSwitchInfoPdu1 = true;
  bool firstSwitchInfoPdu2 = true;

  PeriodicTaskIF* executingTask = nullptr;

  void readCommandQueue();

  /**
   * @brief   This function sets all switchStates to the initial switch configuration of the
   *          two PDUs after reboot.
   */
  void initializeSwitchStates();

  /**
   * @brief   Updates all switchStates related to the PDU2.
   *          Function is called each time a new hk dataset has been received from the PDU2Handler.
   */
  void updatePdu2SwitchStates();

  /**
   * @brief   Updates all switchStates related to the PDU1. Called each time the PDU1Handler
   *          sends a new hk dataset.
   */
  void updatePdu1SwitchStates();

  /**
   * @brief   In case of an update snapshot message this function handles the update of the
   *          local dataset.
   * @param storeId   Storage id of updated dataset.
   * @param dataset   Pointer to the local dataset.
   * @param datasetTimeStamp  Pointer to a variable which will hold the timestamp of the updated
   *                          dataset.
   */
  void updateHkTableDataset(store_address_t storeId, LocalPoolDataSetBase* dataset,
                            CCSDSTime::CDS_short* datasetTimeStamp);
  void checkAndUpdatePduSwitch(GOMSPACE::Pdu pdu, pcdu::Switches switchIdx, uint8_t setValue);
};

#endif /* MISSION_POWER_PCDUHANDLER_H_ */