#ifndef MISSION_SYSTEM_DUALLANEASSEMBLYBASE_H_
#define MISSION_SYSTEM_DUALLANEASSEMBLYBASE_H_

#include <fsfw/devicehandlers/AssemblyBase.h>
#include <mission/system/DualLanePowerStateMachine.h>

/**
 * @brief   Encapsulates assemblies which are also responsible for dual lane power switching
 * @details
 * This is the base class for both the ACS board and the SUS board. Both boards have redundant
 * power lanes and are required for the majority of satellite modes. Therefore, there is a lot
 * of common code, for example the power switching.
 */
class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
 public:
  static constexpr UniqueEventId_t TRANSITION_OTHER_SIDE_FAILED_ID = 0;
  static constexpr UniqueEventId_t NOT_ENOUGH_DEVICES_DUAL_MODE_ID = 1;
  static constexpr UniqueEventId_t POWER_STATE_MACHINE_TIMEOUT_ID = 2;
  static constexpr UniqueEventId_t SIDE_SWITCH_TRANSITION_NOT_ALLOWED_ID = 3;

  DualLaneAssemblyBase(object_id_t objectId, object_id_t parentId, PowerSwitchIF* pwrSwitcher,
                       pcdu::Switches switch1, pcdu::Switches switch2, Event pwrSwitchTimeoutEvent,
                       Event sideSwitchNotAllowedEvent, Event transitionOtherSideFailedEvent);

 protected:
  // This helper object complete encapsulates power switching
  DualLanePowerStateMachine pwrStateMachine;
  Event pwrTimeoutEvent;
  Event sideSwitchNotAllowedEvent;
  Event transitionOtherSideFailedEvent;
  uint8_t powerRetryCounter = 0;
  bool tryingOtherSide = false;
  bool dualModeErrorSwitch = true;
  duallane::Submodes defaultSubmode = duallane::Submodes::A_SIDE;

  enum RecoveryCustomStates {
    IDLE,
    POWER_SWITCHING_OFF,
    POWER_SWITCHING_ON,
    DONE
  } customRecoveryStates = RecoveryCustomStates::IDLE;

  MessageQueueIF* eventQueue = nullptr;

  /**
   * Check whether it makes sense to send mode commands to the device
   * @param object
   * @param mode
   * @return
   */
  bool isUseable(object_id_t object, Mode_t mode);

  /**
   * Thin wrapper function which is required because the helper class
   * can not access protected member functions.
   * @param mode
   * @param submode
   */
  virtual ReturnValue_t pwrStateMachineWrapper();
  virtual ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
  /**
   * Custom recovery implementation to ensure that the power lines are commanded off for a
   * recovery.
   * @return
   */
  virtual bool checkAndHandleRecovery() override;
  void setPreferredSide(duallane::Submodes submode);
  virtual void performChildOperation() override;
  virtual void startTransition(Mode_t mode, Submode_t submode) override;
  virtual void handleChildrenLostMode(ReturnValue_t result) override;
  virtual void handleModeTransitionFailed(ReturnValue_t result) override;
  virtual void handleModeReached() override;

  MessageQueueId_t getEventReceptionQueue() override;

  bool sideSwitchTransition(Mode_t mode, Submode_t submode);

  /**
   * Implemented by user. Will be called if a full mode operation has finished.
   * This includes both the regular mode state machine operations and the power state machine
   * operations
   */
  virtual void finishModeOp();

  template <size_t MAX_SIZE>
  void initModeTableEntry(object_id_t id, ModeListEntry& entry,
                          FixedArrayList<ModeListEntry, MAX_SIZE>& modeTable);

 private:
};

template <size_t MAX_SIZE>
inline void DualLaneAssemblyBase::initModeTableEntry(
    object_id_t id, ModeListEntry& entry, FixedArrayList<ModeListEntry, MAX_SIZE>& modeTable) {
  entry.setObject(id);
  entry.setMode(MODE_OFF);
  entry.setSubmode(SUBMODE_NONE);
  entry.setInheritSubmode(false);
  modeTable.insert(entry);
}

#endif /* MISSION_SYSTEM_DUALLANEASSEMBLYBASE_H_ */