#ifndef MISSION_DEVICES_HEATERHANDLER_H_
#define MISSION_DEVICES_HEATERHANDLER_H_

#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <fsfw/action/HasActionsIF.h>
#include <fsfw/power/PowerSwitchIF.h>
#include <fsfwconfig/devices/heaterSwitcherList.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/devicehandlers/CookieIF.h>
#include <fsfw/timemanager/Countdown.h>
#include <fsfw_hal/common/gpio/GpioIF.h>
#include <unordered_map>

/**
 * @brief	This class intends the control of heaters.
 *
 * @author	J. Meier
 */
class HeaterHandler: public ExecutableObjectIF,
        public PowerSwitchIF,
        public SystemObject,
        public HasActionsIF {
public:

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

    HeaterHandler(object_id_t setObjectId, object_id_t gpioDriverId, CookieIF * gpioCookie,
            object_id_t mainLineSwitcherObjectId, uint8_t mainLineSwitch);

    virtual ~HeaterHandler();

	virtual ReturnValue_t performOperation(uint8_t operationCode = 0) override;

	virtual void sendSwitchCommand(uint8_t switchNr, ReturnValue_t onOff) const override;
	virtual void sendFuseOnCommand(uint8_t fuseNr) const override;
	/**
	 * @brief	This function will be called from the Heater object to check
	 * 			the current switch state.
	 */
	virtual 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 MessageQueueId_t getCommandQueue() const override;
    virtual ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
            const uint8_t* data, size_t size) override;
	virtual ReturnValue_t initialize() override;

private:

	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);

	static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::HEATER_HANDLER;
	static const Event GPIO_PULL_HIGH_FAILED = MAKE_EVENT(0, severity::LOW);
	static const Event GPIO_PULL_LOW_FAILED = MAKE_EVENT(1, severity::LOW);
	static const Event SWITCH_ALREADY_ON = MAKE_EVENT(2, severity::LOW);
	static const Event SWITCH_ALREADY_OFF = MAKE_EVENT(3, severity::LOW);
	static const Event MAIN_SWITCH_TIMEOUT = MAKE_EVENT(4, severity::LOW);

	static const MessageQueueId_t NO_COMMANDER = 0;

	enum SwitchState : bool {
        ON = true,
        OFF = false
      };


    /**
     * @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.
     */
    typedef struct HeaterCommandInfo {
        uint8_t action;
        MessageQueueId_t replyQueue;
        bool active = false;
        bool waitMainSwitchOn = false;
        Countdown mainSwitchCountdown;
    } HeaterCommandInfo_t;

    enum SwitchAction {
        SET_SWITCH_OFF,
        SET_SWITCH_ON
    };

    using switchNr_t = uint8_t;
    using HeaterMap = std::unordered_map<switchNr_t, HeaterCommandInfo_t>;
    using HeaterMapIter = HeaterMap::iterator;

    HeaterMap heaterMap;

    bool switchStates[heaterSwitches::NUMBER_OF_SWITCHES];

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

    /**
     * The object ID of the GPIO driver which enables and disables the
     * heaters.
     */
    object_id_t gpioDriverId;

    CookieIF * gpioCookie;

    GpioIF* gpioInterface = nullptr;

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

    object_id_t mainLineSwitcherObjectId;

    /** Switch number of the heater power supply switch */
    uint8_t mainLineSwitch;

    /**
     * Power switcher object which controls the 8V main line of the heater
     * logic on the TCS board.
     */
    PowerSwitchIF *mainLineSwitcher = nullptr;

    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.
	 */
	bool checkSwitchState(int switchNr);

	/**
	 * @brief   Returns the ID of the GPIO related to a heater identified by the switch number
	 *          which is defined in the heaterSwitches list.
	 */
	gpioId_t getGpioIdFromSwitchNr(int switchNr);

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

	ReturnValue_t initializeHeaterMap();

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

	void handleSwitchOnCommand(HeaterMapIter heaterMapIter);

	void handleSwitchOffCommand(HeaterMapIter heaterMapIter);

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

};

#endif /* MISSION_DEVICES_HEATERHANDLER_H_ */