#ifndef MISSION_DEVICES_HEATERHANDLER_H_
#define MISSION_DEVICES_HEATERHANDLER_H_

#include <fsfw/action/HasActionsIF.h>
#include <fsfw/devicehandlers/CookieIF.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/devicehandlers/HealthDevice.h>
#include <fsfw/health/HasHealthIF.h>
#include <fsfw/health/HealthHelper.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/power/PowerSwitchIF.h>
#include <fsfw/returnvalues/returnvalue.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/timemanager/Countdown.h>
#include <fsfw_hal/common/gpio/GpioIF.h>

#include <array>
#include <vector>

#include "devices/heaterSwitcherList.h"
#include "events/subsystemIdRanges.h"
#include "returnvalues/classIds.h"

class PowerSwitchIF;
class HealthTableIF;

using HeaterPair = std::pair<HealthDevice*, gpioId_t>;

struct HeaterHelper {
 public:
  HeaterHelper(std::array<HeaterPair, heater::NUMBER_OF_SWITCHES> heaters) : heaters(heaters) {}
  std::array<HeaterPair, heater::NUMBER_OF_SWITCHES> heaters = {};
};
/**
 * @brief	This class intends the control of heaters.
 *
 * @author	J. Meier
 */
class HeaterHandler : public ExecutableObjectIF,
                      public PowerSwitchIF,
                      public SystemObject,
                      public HasActionsIF {
 public:
  static const uint8_t INTERFACE_ID = CLASS_ID::HEATER_HANDLER;

  static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA1);
  static const ReturnValue_t INIT_FAILED = MAKE_RETURN_CODE(0xA2);
  static const ReturnValue_t INVALID_SWITCH_NR = MAKE_RETURN_CODE(0xA3);
  static const ReturnValue_t MAIN_SWITCH_SET_TIMEOUT = MAKE_RETURN_CODE(0xA4);
  static const ReturnValue_t COMMAND_ALREADY_WAITING = MAKE_RETURN_CODE(0xA5);

  enum CmdSourceParam : uint8_t { INTERNAL = 0, EXTERNAL = 1 };

  /** Device command IDs */
  static const DeviceCommandId_t SWITCH_HEATER = 0x0;

  HeaterHandler(object_id_t setObjectId, GpioIF* gpioInterface_, HeaterHelper helper,
                PowerSwitchIF* mainLineSwitcherObjectId, power::Switch_t mainLineSwitch);

  virtual ~HeaterHandler();

  ReturnValue_t performOperation(uint8_t operationCode = 0) override;

  ReturnValue_t sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) override;
  ReturnValue_t sendFuseOnCommand(uint8_t fuseNr) override;
  /**
   * @brief	This function will be called from the Heater object to check
   * 			the current switch state.
   */
  ReturnValue_t getSwitchState(uint8_t switchNr) const override;
  ReturnValue_t getFuseState(uint8_t fuseNr) const override;
  uint32_t getSwitchDelayMs(void) const override;

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

 private:
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::HEATER_HANDLER;
  static constexpr Event GPIO_PULL_HIGH_FAILED = MAKE_EVENT(0, severity::LOW);
  static constexpr Event GPIO_PULL_LOW_FAILED = MAKE_EVENT(1, severity::LOW);
  static constexpr Event HEATER_WENT_ON = event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO);
  static constexpr Event HEATER_WENT_OFF = event::makeEvent(SUBSYSTEM_ID, 3, severity::INFO);
  static constexpr Event SWITCH_ALREADY_ON = MAKE_EVENT(4, severity::LOW);
  static constexpr Event SWITCH_ALREADY_OFF = MAKE_EVENT(5, severity::LOW);
  static constexpr Event MAIN_SWITCH_TIMEOUT = MAKE_EVENT(6, severity::MEDIUM);
  //! A faulty heater was one. The SW will autonomously attempt to shut it down. P1: Heater Index
  static constexpr Event FAULTY_HEATER_WAS_ON = event::makeEvent(SUBSYSTEM_ID, 7, severity::LOW);

  static const MessageQueueId_t NO_COMMANDER = 0;

  enum SwitchState : bool { ON = true, OFF = false };
  enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE };

  /**
   * @brief   Struct holding information about a heater command to execute.
   *
   * @param action    The action to perform.
   * @param replyQueue    The queue of the commander to which status replies
   *                      will be sent.
   * @param active    True if command is waiting for execution, otherwise false.
   * @param waitSwitchOn  True if the command is waiting for the main switch being set on.
   * @param mainSwitchCountdown   Sets timeout to wait for main switch being set on.
   */
  struct HeaterWrapper {
    HeaterWrapper(HeaterPair pair, SwitchState initState)
        : healthDevice(pair.first), gpioId(pair.second), switchState(initState) {}
    HealthDevice* healthDevice = nullptr;
    gpioId_t gpioId = gpio::NO_GPIO;
    SwitchAction action = SwitchAction::NONE;
    MessageQueueId_t replyQueue = MessageQueueIF::NO_QUEUE;
    bool cmdActive = false;
    SwitchState switchState = SwitchState::OFF;
    bool waitMainSwitchOn = false;
    Countdown mainSwitchCountdown;
  };

  using HeaterMap = std::vector<HeaterWrapper>;

  HeaterMap heaterVec = {};

  MutexIF* heaterMutex = nullptr;

  HeaterHelper helper;

  /** Size of command queue */
  size_t cmdQueueSize = 20;

  GpioIF* gpioInterface = nullptr;

  /** Queue to receive messages from other objects. */
  MessageQueueIF* commandQueue = nullptr;

  /**
   * Power switcher object which controls the 8V main line of the heater
   * logic on the TCS board.
   */
  PowerSwitchIF* mainLineSwitcher = nullptr;
  /** Switch number of the heater power supply switch */
  power::Switch_t mainLineSwitch;

  ActionHelper actionHelper;

  StorageManagerIF* ipcStore = nullptr;

  void readCommandQueue();

  /**
   * @brief   Returns the state of a switch (ON - true, or OFF - false).
   * @param switchNr  The number of the switch to check.
   */
  SwitchState checkSwitchState(heater::Switchers switchNr) const;

  /**
   * @brief	This function runs commands waiting for execution.
   */
  void handleSwitchHandling();

  ReturnValue_t initializeHeaterMap();

  /**
   * @brief	Sets all switches to OFF.
   */
  void setInitialSwitchStates();

  void handleSwitchOnCommand(heater::Switchers heaterIdx);

  void handleSwitchOffCommand(heater::Switchers heaterIdx);

  /**
   * @brief   Checks if all switches are off.
   * @return  True if all switches are off, otherwise false.
   */
  bool allSwitchesOff();
};

#endif /* MISSION_DEVICES_HEATERHANDLER_H_ */