#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_

#include "DeviceHandlerIF.h"
#include "DeviceCommunicationIF.h"
#include "DeviceHandlerFailureIsolation.h"
#include "DeviceHandlerThermalSet.h"

#include "../serviceinterface/ServiceInterface.h"
#include "../serviceinterface/serviceInterfaceDefintions.h"
#include "../objectmanager/SystemObject.h"
#include "../tasks/ExecutableObjectIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../action/HasActionsIF.h"
#include "../datapool/PoolVariableIF.h"
#include "../modes/HasModesIF.h"
#include "../power/PowerSwitchIF.h"
#include "../ipc/MessageQueueIF.h"
#include "../tasks/PeriodicTaskIF.h"
#include "../action/ActionHelper.h"
#include "../health/HealthHelper.h"
#include "../parameters/ParameterHelper.h"
#include "../datapoollocal/HasLocalDataPoolIF.h"
#include "../datapoollocal/LocalDataPoolManager.h"

#include <map>

namespace Factory{
void setStaticFrameworkObjectIds();
}

class StorageManagerIF;

/**
 * @defgroup devices Devices
 * Contains all devices and the DeviceHandlerBase class.
 */

/**
 * @brief This is the abstract base class for device handlers.
 * @details
 * Documentation: Dissertation Baetz p.138,139, p.141-149
 *
 * It features handling of @link DeviceHandlerIF::Mode_t Modes @endlink,
 * communication with physical devices, using the
 * @link DeviceCommunicationIF @endlink, and communication with commanding
 * objects. It inherits SystemObject and thus can be created by the
 * ObjectManagerIF.
 *
 * This class uses the opcode of ExecutableObjectIF to perform a
 * step-wise execution. For each step a different action is selected and
 * executed. Currently, the device handler base performs a 4-step
 * execution related to 4 communication steps (based on RMAP).
 * NOTE: RMAP is a standard which is used for Flying Laptop.
 * RMAP communication is not mandatory for projects implementing the FSFW.
 * However, the communication principles are similar to RMAP as there are
 * two write and two send calls involved.
 *
 * Device handler instances should extend this class and implement the abstract
 * functions. Components and drivers can send so called cookies which are used
 * for communication and contain information about the communcation (e.g. slave
 * address for I2C or RMAP structs).
 * The following abstract methods must be implemented by a device handler:
 *  1. doStartUp()
 *  2. doShutDown()
 *  3. buildTransitionDeviceCommand()
 *  4. buildNormalDeviceCommand()
 *  5. buildCommandFromCommand()
 *  6. fillCommandAndReplyMap()
 *  7. scanForReply()
 *  8. interpretDeviceReply()
 *
 * Other important virtual methods with a default implementation
 * are the getTransitionDelayMs() function and the getSwitches() function.
 * If a transition to MODE_ON is desired without commanding, override the
 * intialize() function and call setMode(_MODE_START_UP) before calling
 * DeviceHandlerBase::initialize().
 *
 * @ingroup devices
 */
class DeviceHandlerBase: public DeviceHandlerIF,
public HasReturnvaluesIF,
public ExecutableObjectIF,
public SystemObject,
public HasModesIF,
public HasHealthIF,
public HasActionsIF,
public ReceivesParameterMessagesIF,
public HasLocalDataPoolIF {
    friend void (Factory::setStaticFrameworkObjectIds)();
public:
    /**
     * The constructor passes the objectId to the SystemObject().
     *
     * @param setObjectId the ObjectId to pass to the SystemObject() Constructor
     * @param deviceCommuncation Communcation Interface object which is used
     * to implement communication functions
     * @param comCookie This object will be passed to the communication inter-
     * face and can contain user-defined information about the communication.
     * @param fdirInstance
     * @param cmdQueueSize
     */
    DeviceHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication,
            CookieIF * comCookie, FailureIsolationBase* fdirInstance = nullptr,
            size_t cmdQueueSize = 20);

    void setHkDestination(object_id_t hkDestination);

    /**
     * If the device handler is controlled by the FSFW thermal building blocks,
     * this function should be called to initialize all required components.
     * The device handler will then take care of creating local pool entries
     * for the device thermal state and device heating request.
     * Custom local pool IDs can be assigned as well.
     * @param thermalStatePoolId
     * @param thermalRequestPoolId
     */
    void setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId =
            DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID,
            lp_id_t thermalRequestPoolId =
                    DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID,
                    uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID);
    /**
     * @brief   Helper function to ease device handler development.
     * This will instruct the transition to MODE_ON immediately
     * (leading to doStartUp() being called for the transition to the ON mode),
     * so external mode commanding is not necessary anymore.
     *
     * This has to be called before the task is started!
     * (e.g. in the task factory). This is only a helper function for
     * development. Regular mode commanding should be performed by commanding
     * the AssemblyBase or Subsystem objects resposible for the device handler.
     */
    void setStartUpImmediately();

    /**
     * @brief 	This function is the device handler base core component and is
     * 			called periodically.
     * @details
     * General sequence, showing where abstract virtual functions are called:
     * If the State is SEND_WRITE:
     *   1. Set the cookie state to COOKIE_UNUSED and read the command queue
     *   2. Handles Device State Modes by calling doStateMachine().
     *      This function calls callChildStatemachine() which calls the
     *      abstract functions doStartUp() and doShutDown()
     *   3. Check switch states by calling checkSwitchStates()
     *   4. Decrements counter for timeout of replies by calling
     *      decrementDeviceReplyMap()
     *   5. Performs FDIR check for failures
     *   6. If the device mode is MODE_OFF, return RETURN_OK.
     *      Otherwise, perform the Action property and performs depending
     *      on value specified by input value counter (incremented in PST).
     *      The child class tells base class what to do by setting this value.
     *     - SEND_WRITE: Send data or commands to device by calling
     *       doSendWrite() which calls sendMessage function
     *       of #communicationInterface
     *     	 and calls buildInternalCommand if the cookie state is COOKIE_UNUSED
     *     - GET_WRITE: Get ackknowledgement for sending by calling doGetWrite()
     *       which calls getSendSuccess of #communicationInterface.
     *       Calls abstract functions scanForReply() and interpretDeviceReply().
     *     - SEND_READ: Request reading data from device by calling doSendRead()
     *       which calls requestReceiveMessage of #communcationInterface
     *     - GET_READ: Access requested reading data by calling doGetRead()
     *       which calls readReceivedMessage of #communicationInterface
     * @param counter Specifies which Action to perform
     * @return RETURN_OK for successful execution
     */
    virtual ReturnValue_t performOperation(uint8_t counter);

    /**
     * @brief  Initializes the device handler
     * @details
     * Initialize Device Handler as system object and
     * initializes all important helper classes.
     * Calls fillCommandAndReplyMap().
     * @return
     */
    virtual ReturnValue_t initialize();

    /**
     * @brief   Intialization steps performed after all tasks have been created.
     *          This function will be called by the executing task.
     * @return
     */
    virtual ReturnValue_t initializeAfterTaskCreation() override;

    /** Destructor. */
    virtual ~DeviceHandlerBase();


    /**
     * Implementation of ExecutableObjectIF function
     * Used to setup the reference of the task, that executes this component
     * @param task_ Pointer to the taskIF of this task
     */
    virtual  void setTaskIF(PeriodicTaskIF* task_) override;
    virtual MessageQueueId_t getCommandQueue(void) const override;

    /** Explicit interface implementation of getObjectId */
    virtual object_id_t getObjectId() const override;

    /**
     * @param parentQueueId
     */
    virtual void setParentQueue(MessageQueueId_t parentQueueId);

    /** @brief  Implementation required for HasActionIF */
    ReturnValue_t executeAction(ActionId_t actionId,
            MessageQueueId_t commandedBy, const uint8_t* data,
            size_t size) override;

    Mode_t getTransitionSourceMode() const;
    Submode_t getTransitionSourceSubMode() const;
    virtual void getMode(Mode_t *mode, Submode_t *submode);
    HealthState getHealth();
    ReturnValue_t setHealth(HealthState health);
    virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId,
            ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues,
            uint16_t startAtIndex) override;

protected:
    /**
     * @brief 	This is used to let the child class handle the transition from
     * 			mode @c _MODE_START_UP to @c MODE_ON
     * @details
     * It is only called when the device handler is in mode @c _MODE_START_UP.
     * That means, the device switch(es) are already set to on.
     * Device handler commands are read and can be handled by the child class.
     * If the child class handles a command, it should also send
     * an reply accordingly.
     * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE,
     * the base class handles rejecting the command and sends a reply.
     * The replies for mode transitions are handled by the base class.
     *
     *  - If the device is started and ready for operation, the mode should be
     *    set to MODE_ON. It is possible to set the mode to _MODE_TO_ON to
     *    use the to on transition if available.
     *  - If the power-up fails, the mode should be set to _MODE_POWER_DOWN
     *    which will lead to the device being powered off.
     *  - If the device does not change the mode, the mode will be changed
     *    to _MODE_POWER_DOWN, after the timeout (from getTransitionDelay())
     *    has passed.
     *
     * #transitionFailure can be set to a failure code indicating the reason
     * for a failed transition
     */
    virtual void doStartUp() = 0;
    /**
     * @brief 	This is used to let the child class handle the transition
     * 			from mode @c _MODE_SHUT_DOWN to @c _MODE_POWER_DOWN
     * @details
     * It is only called when the device handler is in mode @c _MODE_SHUT_DOWN.
     * Device handler commands are read and can be handled by the child class.
     * If the child class handles a command, it should also send an reply
     * accordingly.
     * If an Command is not handled (ie #DeviceHandlerCommand is not
     * @c CMD_NONE, the base class handles rejecting the command and sends a
     * reply. The replies for mode transitions are handled by the base class.
     *
     *  - If the device ready to be switched off,
     *    the mode should be set to _MODE_POWER_DOWN.
     *  - If the device should not be switched off, the mode can be changed to
     *     _MODE_TO_ON (or MODE_ON if no transition is needed).
     *  - If the device does not change the mode, the mode will be changed to
     *    _MODE_POWER_DOWN, when the timeout (from getTransitionDelay())
     *    has passed.
     *
     * #transitionFailure can be set to a failure code indicating the reason
     * for a failed transition
     */
    virtual void doShutDown() = 0;

    /* Command handling */
    /**
     * Build the device command to send for normal mode.
     *
     * This is only called in @c MODE_NORMAL. If multiple submodes for
     * @c MODE_NORMAL are supported, different commands can built,
     * depending on the submode.
     *
     * #rawPacket and #rawPacketLen must be set by this method to the
     * packet to be sent. If variable command frequence is required, a counter
     * can be used and the frequency in the reply map has to be set manually
     * by calling updateReplyMap().
     *
     * @param[out] id the device command id that has been built
     * @return
     *  - @c RETURN_OK to send command after setting #rawPacket and
     *    #rawPacketLen.
     *  - @c NOTHING_TO_SEND when no command is to be sent.
     *  - Anything else triggers an even with the returnvalue as a parameter.
     */
    virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) = 0;
    /**
     * Build the device command to send for a transitional mode.
     *
     * This is only called in @c _MODE_TO_NORMAL, @c _MODE_TO_ON, @c _MODE_TO_RAW,
     * @c _MODE_START_UP and @c _MODE_SHUT_DOWN. So it is used by doStartUp()
     * and doShutDown() as well as doTransition(), by setting those
     * modes in the respective functions.
     *
     * A good idea is to implement a flag indicating a command has to be built
     * and a variable containing the command number to be built
     * and filling them in doStartUp(), doShutDown() and doTransition() so no
     * modes have to be checked here.
     *
     * #rawPacket and #rawPacketLen must be set by this method to the
     * packet to be sent.
     *
     * @param[out] id the device command id built
     * @return
     *    - @c RETURN_OK when a command is to be sent
     *    - @c NOTHING_TO_SEND when no command is to be sent
     *    - Anything else triggers an even with the returnvalue as a parameter
     */
    virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t * id) = 0;
    /**
     * @brief 	Build a device command packet from data supplied by a direct
     *          command (PUS Service 8)
     * @details
     * This will be called if an functional command via PUS Service 8 is received and is
     * the primary interface for functional command instead of #executeAction for users. The
     * supplied ActionId_t action ID will be converted to a DeviceCommandId_t command ID after
     * an internal check whether the action ID is a key in the device command map.
     *
     * #rawPacket and #rawPacketLen should be set by this method to the packet to be sent.
     * The existence of the command in the command map and the command size check against 0 are
     * done by the base class.
     *
     * @param deviceCommand     The command to build, already checked against deviceCommandMap
     * @param commandData       Pointer to the data from the direct command
     * @param commandDataLen    Length of commandData
     * @return
     *  - @c RETURN_OK to send command after #rawPacket and #rawPacketLen
     *       have been set.
     *  - @c HasActionsIF::EXECUTION_COMPLETE to generate a finish reply immediately. This can
     *       be used if no reply is expected. Otherwise, the developer can call #actionHelper.finish
     *       to finish the command handling.
     *  - Anything else triggers an event with the return code as a parameter as well as a
     *    step reply failed with the return code
     */
    virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
            const uint8_t * commandData, size_t commandDataLen) = 0;

    /* Reply handling */
    /**
     * @brief   Scans a buffer for a valid reply.
     * @details
     * This is used by the base class to check the data received for valid packets.
     * It only checks if a valid packet starts at @c start.
     * It also only checks the structural validy of the packet,
     * e.g. checksums lengths and protocol data. No information check is done,
     * e.g. range checks etc.
     *
     * Errors should be reported directly, the base class does NOT report any
     * errors based on the return value of this function.
     *
     * @param start start of remaining buffer to be scanned
     * @param len length of remaining buffer to be scanned
     * @param[out] foundId the id of the data found in the buffer.
     * @param[out] foundLen length of the data found. Is to be set in function,
     *                       buffer is scanned at previous position + foundLen.
     * @return
     *  - @c RETURN_OK a valid packet was found at @c start, @c foundLen is valid
     *  - @c RETURN_FAILED no reply could be found starting at @c start,
     *    implies @c foundLen is not valid, base class will call scanForReply()
     *    again with ++start
     *  - @c DeviceHandlerIF::INVALID_DATA a packet was found but it is invalid,
     *    e.g. checksum error, implies @c foundLen is valid, can be used to
     *    skip some bytes
     *  - @c DeviceHandlerIF::LENGTH_MISSMATCH @c len is invalid
     *  - @c DeviceHandlerIF::IGNORE_REPLY_DATA Ignore this specific part of
     *    the packet
     *  - @c DeviceHandlerIF::IGNORE_FULL_PACKET Ignore the packet
     *  - @c APERIODIC_REPLY if a valid reply is received that has not been
     *    requested by a command, but should be handled anyway
     *    (@see also fillCommandAndCookieMap() )
     */
    virtual ReturnValue_t scanForReply(const uint8_t *start, size_t len,
            DeviceCommandId_t *foundId, size_t *foundLen) = 0;
    /**
     * @brief   Interpret a reply from the device.
     * @details
     * This is called after scanForReply() found a valid packet, it can be
     * assumed that the length and structure is valid.
     * This routine extracts the data from the packet into a DataSet and then
     * calls handleDeviceTM(), which either sends a TM packet or stores the
     * data in the DataPool depending on whether it was an external command.
     * No packet length is given, as it should be defined implicitly by the id.
     *
     * @param id the id found by scanForReply()
     * @param packet
     * @return
     *     - @c RETURN_OK when the reply was interpreted.
     *     - @c IGNORE_REPLY_DATA Ignore the reply and don't reset reply cycle
     *          counter.
     *     - @c RETURN_FAILED when the reply could not be interpreted,
     *     e.g. logical errors or range violations occurred
     */
    virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
            const uint8_t *packet) = 0;
    MessageQueueId_t getCommanderId(DeviceCommandId_t replyId) const;
    /**
     * Helper function to get pending command. This is useful for devices
     * like SPI sensors to identify the last sent command.
     * This only returns the command sent in the last SEND_WRITE cycle.
     * @return
     */
    DeviceCommandId_t getPendingCommand() const;

    /* Specifying commands and replies */
    /**
     * @brief   Fill the #DeviceCommandMap and #DeviceReplyMap called by the #initialize
     *          of the base class
     * @details
     * This is used to let the base class know which replies are expected.
     * There are different scenarios regarding this:
     *
     *  - "Normal" commands. These are commands, that trigger a direct reply
     *    from the device. In this case, the id of the command should be added
     *    to the command map with a commandData_t where maxDelayCycles is set
     *    to the maximum expected number of PST cycles the reply will take.
     *    Then, scanForReply returns the id of the command and the base class
     *    can handle time-out and missing replies.
     *
     *  - Periodic, unrequested replies. These are replies that, once enabled,
     *    are sent by the device on its own in a defined interval.
     *    In this case, the id of the reply or a placeholder id should be added
     *    to the deviceCommandMap with a commandData_t where maxDelayCycles is
     *    set to the maximum expected number of PST cycles between two replies
     *    (also a tolerance should be added, as an FDIR message will be
     *    generated if it is missed).
     *    From then on, the base class handles the reception.
     *    Then, scanForReply returns the id of the reply or the placeholder id
     *    and the base class will take care of checking that all replies are
     *    received and the interval is correct.
     *
     *  - Aperiodic, unrequested replies. These are replies that are sent
     *    by the device without any preceding command and not in a defined
     *    interval. These are not entered in the deviceCommandMap but
     *    handled by returning @c APERIODIC_REPLY in scanForReply().
     */
    virtual void fillCommandAndReplyMap() = 0;
    /**
     * This is a helper method to facilitate inserting entries in the command map.
     * @param deviceCommand	Identifier of the command to add.
     * @param maxDelayCycles The maximum number of delay cycles the command
     * waits until it times out.
     * @param replyLen Will be supplied to the requestReceiveMessage call of
     * the communication interface.
     * @param periodic	Indicates if the command is periodic (i.e. it is sent
     * by the device repeatedly without request) or not. Default is aperiodic (0)
     * @return	- @c RETURN_OK when the command was successfully inserted,
     *          - @c RETURN_FAILED else.
     */
    ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand,
            uint16_t maxDelayCycles,
            LocalPoolDataSetBase* replyDataSet = nullptr,
            size_t replyLen = 0, bool periodic = false,
            bool hasDifferentReplyId = false, DeviceCommandId_t replyId = 0);
    /**
     * @brief 	This is a helper method to insert replies in the reply map.
     * @param deviceCommand	Identifier of the reply to add.
     * @param maxDelayCycles The maximum number of delay cycles the reply waits
     * until it times out.
     * @param periodic	Indicates if the command is periodic (i.e. it is sent
     * by the device repeatedly without request) or not. Default is aperiodic (0)
     * @return	- @c RETURN_OK when the command was successfully inserted,
     *          - @c RETURN_FAILED else.
     */
    ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand,
            uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr,
            size_t replyLen = 0, bool periodic = false);
    /**
     * @brief 	A simple command to add a command to the commandList.
     * @param deviceCommand The command to add
     * @return - @c RETURN_OK when the command was successfully inserted,
     *         - @c RETURN_FAILED else.
     */
    ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand);
    /**
     * @brief 	This is a helper method to facilitate updating entries
     *        	in the reply map.
     * @param deviceCommand	Identifier of the reply to update.
     * @param delayCycles The current number of delay cycles to wait.
     * As stated in #fillCommandAndCookieMap, to disable periodic commands,
     * this is set to zero.
     * @param maxDelayCycles The maximum number of delay cycles the reply waits
     * until it times out. By passing 0 the entry remains untouched.
     * @param periodic Indicates if the command is periodic (i.e. it is sent
     * by the device repeatedly without request) or not.Default is aperiodic (0).
     * Warning: The setting always overrides the value that was entered in the map.
     * @return - @c RETURN_OK when the command was successfully inserted,
     *         - @c RETURN_FAILED else.
     */
    ReturnValue_t updateReplyMapEntry(DeviceCommandId_t deviceReply,
            uint16_t delayCycles, uint16_t maxDelayCycles,
            bool periodic = false);
    /**
     * @brief   Can be used to set the dataset corresponding to a reply ID manually.
     * @details
     * Used by the local data pool manager.
     */
    ReturnValue_t setReplyDataset(DeviceCommandId_t replyId,
            LocalPoolDataSetBase* dataset);

    /**
     * Get the time needed to transit from modeFrom to modeTo.
     *
     * Used for the following transitions:
     * modeFrom -> modeTo:
     * MODE_ON -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN]
     * MODE_NORMAL -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN]
     * MODE_RAW -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN]
     * _MODE_START_UP -> MODE_ON (do not include time to set the switches,
     * the base class got you covered)
     *
     * The default implementation returns 0 !
     * @param modeFrom
     * @param modeTo
     * @return time in ms
     */
    virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) = 0;

    /* Functions used by the local data pool manager */
    /**
     * This function is used to initialize the local housekeeping pool
     * entries. The default implementation leaves the pool empty.
     * @param localDataPoolMap
     * @return
     */
    virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
            LocalDataPoolManager& poolManager) override;
    /**
     * @brief   Set all datapool variables that are update periodically in
     *          normal mode invalid
     * @details
     * The default implementation will set all datasets which have been added
     * in #fillCommandAndReplyMap to invalid. It will also set all pool
     * variables inside the dataset to invalid. The user can override this
     * method optionally.
     */
    virtual void setNormalDatapoolEntriesInvalid();
    /**
     * @brief   Get the dataset handle for a given SID.
     * @details
     * The default implementation will use the deviceCommandMap to look for the corresponding
     * dataset handle. The user can override this function if this is not desired.
     * @param sid
     * @return
     */
    virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;

    /* HasModesIF overrides */
    virtual void startTransition(Mode_t mode, Submode_t submode) override;
    virtual void setToExternalControl() override;
    virtual void announceMode(bool recursive) override;
    /**
     * @brief   Set the device handler mode
     * @details
     * Sets #timeoutStart with every call Also sets #transitionTargetMode if necessary so
     * transitional states can be entered from everywhere without breaking the state machine
     * (which relies on a correct #transitionTargetMode).
     * The submode is left unchanged.
     *
     * @param newMode
     */
    void setMode(Mode_t newMode);
    /**
     * @overload
     * @param submode
     */
    void setMode(Mode_t newMode, Submode_t submode);
    /**
     * @brief   Should be implemented properly by child class.
     * @param mode
     * @param submode
     * @return
     *    - @c RETURN_OK if valid
     *    - @c RETURN_FAILED if invalid
     */
    virtual ReturnValue_t isModeCombinationValid(Mode_t mode,
            Submode_t submode);
    /**
     * @brief   Notify child about mode change.
     * @details
     * Can be overriden to be used like a callback.
     */
    virtual void modeChanged();

    /* Power handling functions */
    /**
     * Return the switches connected to the device.
     *
     * The default implementation returns one switch set in the ctor.
     *
     * @param[out] switches pointer to an array of switches
     * @param[out] numberOfSwitches length of returned array
     * @return
     *      - @c RETURN_OK if the parameters were set
     *      - @c RETURN_FAILED if no switches exist
     */
    virtual ReturnValue_t getSwitches(const uint8_t **switches,
            uint8_t *numberOfSwitches);

    /**
     * @brief   Helper function to report a missed reply
     * @details Can be overwritten by children to act on missed replies or to fake reporting Id.
     * @param id of the missed reply
     */
    virtual void missedReply(DeviceCommandId_t id);

    /* Miscellaneous functions */
    /**
     * @brief 	Hook function for child handlers which is called once per
     * 			performOperation(). Default implementation is empty.
     */
    virtual void performOperationHook();
    /**
     * @brief   Can be implemented by child handler to
     *          perform debugging
     * @details Example: Calling this in performOperation
     *          to track values like mode.
     * @param positionTracker Provide the child handler a way to know
     * where the debugInterface was called
     * @param objectId Provide the child handler object Id to
     * specify actions for spefic devices
     * @param parameter Supply a parameter of interest
     * Please delete all debugInterface calls in DHB after debugging is finished !
     */
    virtual void debugInterface(uint8_t positionTracker = 0,
            object_id_t objectId = 0, uint32_t parameter = 0);

protected:

    static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE;

    static const ReturnValue_t INVALID_CHANNEL = MAKE_RETURN_CODE(0xA0);
    /* Return codes for scanForReply */
    //! This is used to specify for replies from a device which are not replies to requests
    static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(0xB0);
    //! Ignore parts of the received packet
    static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(0xB1);
    //! Ignore full received packet
    static const ReturnValue_t IGNORE_FULL_PACKET = MAKE_RETURN_CODE(0xB2);
    /* Return codes for command building */
    //! Return this if no command sending in required
    static const ReturnValue_t NOTHING_TO_SEND = MAKE_RETURN_CODE(0xC0);
    static const ReturnValue_t COMMAND_MAP_ERROR = MAKE_RETURN_CODE(0xC2);
    // Return codes for getSwitches */
    static const ReturnValue_t NO_SWITCH = MAKE_RETURN_CODE(0xD0);
    /* Mode handling error Codes */
    static const ReturnValue_t CHILD_TIMEOUT = MAKE_RETURN_CODE(0xE0);
    static const ReturnValue_t SWITCH_FAILED = MAKE_RETURN_CODE(0xE1);

    static const MessageQueueId_t NO_COMMANDER = 0;

    //! Pointer to the raw packet that will be sent.
    uint8_t *rawPacket = nullptr;
    //! Size of the #rawPacket.
    uint32_t rawPacketLen = 0;

    /**
     * The mode the device handler is currently in.
     * This should never be changed directly but only with setMode()
     */
    Mode_t mode;
    /**
     * The submode the device handler is currently in.
     * This should never be changed directly but only with setMode()
     */
    Submode_t submode;

    /** This is the counter value from performOperation(). */
    uint8_t pstStep = 0;
    uint8_t lastStep = 0;
    uint32_t pstIntervalMs = 0;

    /**
     * Wiretapping flag:
     *
     * indicates either that all raw messages to and from the device should be
     * sent to #defaultRawReceiver
     * or that all device TM should be downlinked to #defaultRawReceiver.
     */
    enum WiretappingMode {
        OFF = 0, RAW = 1, TM = 2
    } wiretappingMode;
    /**
     * @brief 	A message queue that accepts raw replies
     *
     * Statically initialized in initialize() to a configurable object.
     * Used when there is no method of finding a recipient, ie raw mode and
     * reporting erroneous replies
     */
    MessageQueueId_t defaultRawReceiver = MessageQueueIF::NO_QUEUE;
    store_address_t storedRawData;

    /**
     * @brief 	The message queue which wants to read all raw traffic
     * If #isWiretappingActive all raw communication from and to the device
     * will be sent to this queue
     */
    MessageQueueId_t requestedRawTraffic = 0;

    /**
     * Pointer to the IPCStore.
     * This caches the pointer received from the objectManager in the constructor.
     */
    StorageManagerIF *IPCStore = nullptr;
    /** The comIF object ID is cached for the intialize() function */
    object_id_t deviceCommunicationId;
    /** Communication object used for device communication */
    DeviceCommunicationIF * communicationInterface = nullptr;
    /** Cookie used for communication */
    CookieIF * comCookie;

    /* Health helper for HasHealthIF */
    HealthHelper healthHelper;
    /* Mode helper for HasModesIF */
    ModeHelper modeHelper;
    /* Parameter helper for ReceivesParameterMessagesIF */
    ParameterHelper parameterHelper;
    /* Action helper for HasActionsIF */
    ActionHelper actionHelper;
    /* Housekeeping Manager */
    LocalDataPoolManager poolManager;

    /**
     *  @brief Information about commands
     */
    struct DeviceCommandInfo {
        //! Indicates if the command is already executing.
        bool isExecuting;
        //! Dynamic value to indicate how many replies are expected.
        //! Inititated with 0.
        uint8_t expectedReplies;
        //! if this is != NO_COMMANDER, DHB was commanded externally and shall
        //! report everything to commander.
        MessageQueueId_t sendReplyTo;
    };
    using DeviceCommandMap = std::map<DeviceCommandId_t, DeviceCommandInfo> ;
    /**
     * Information about commands
     */
    DeviceCommandMap deviceCommandMap;

    /**
     * @brief Information about expected replies
     * This is used to keep track of pending replies.
     */
    struct DeviceReplyInfo {
        //! The maximum number of cycles the handler should wait for a reply
        //! to this command.
        uint16_t maxDelayCycles;
        //! The currently remaining cycles the handler should wait for a reply,
        //! 0 means there is no reply expected
        uint16_t delayCycles;
        size_t replyLen = 0; //!< Expected size of the reply.
        //! if this is !=0, the delayCycles will not be reset to 0 but to
        //! maxDelayCycles
        bool periodic = false;
        //! The dataset used to access housekeeping data related to the
        //! respective device reply. Will point to a dataset held by
        //! the child handler (if one is specified)
        LocalPoolDataSetBase* dataSet = nullptr;
        //! The command that expects this reply.
        DeviceCommandMap::iterator command;
    };

    using DeviceReplyMap = std::map<DeviceCommandId_t, DeviceReplyInfo> ;
    using DeviceReplyIter = DeviceReplyMap::iterator;
    /**
     * This map is used to check and track correct reception of all replies.
     *
     * It has multiple use:
     * - It stores the information on pending replies. If a command is sent,
     * 	 the DeviceCommandInfo.count is incremented.
     * - It is used to time-out missing replies. If a command is sent, the
     * 	 DeviceCommandInfo.DelayCycles is set to MaxDelayCycles.
     * - It is queried to check if a reply from the device can be interpreted.
     *   scanForReply() returns the id of the command a reply was found for.
     * The reply is ignored in the following cases:
     *     - No entry for the returned id was found
     *     - The deviceReplyInfo.delayCycles is == 0
     */
    DeviceReplyMap deviceReplyMap;

    //! The MessageQueue used to receive device handler commands
    //! and to send replies.
    MessageQueueIF* commandQueue = nullptr;

    DeviceHandlerThermalSet* thermalSet = nullptr;

    /**
     * Optional Error code. Can be set in doStartUp(), doShutDown() and
     * doTransition() to signal cause for Transition failure.
     */
    ReturnValue_t childTransitionFailure;

    /** Counts if communication channel lost a reply, so some missed
     * replys can be ignored. */
    uint32_t ignoreMissedRepliesCount = 0;

    /** Pointer to the used FDIR instance. If not provided by child,
     * default class is instantiated. */
    FailureIsolationBase* fdirInstance;

    //! To correctly delete the default instance.
    bool defaultFDIRUsed;

    //! Indicates if SWITCH_WENT_OFF was already thrown.
    bool switchOffWasReported;

    /** Pointer to the task which executes this component,
	is invalid before setTaskIF was called. */
    PeriodicTaskIF* executingTask = nullptr;

    //! Object which switches power on and off.
    static object_id_t powerSwitcherId;

    //! Object which receives RAW data by default.
    static object_id_t rawDataReceiverId;

    //! Object which may be the root cause of an identified fault.
    static object_id_t defaultFdirParentId;

    /**
     * @brief   Send a reply to a received device handler command.
     *
     * This also resets #DeviceHandlerCommand to 0.
     *
     * @param reply the reply type
     * @param parameter parameter for the reply
     */
    void replyReturnvalueToCommand(ReturnValue_t status, uint32_t parameter = 0);
    /**
     * TODO: Whats the difference between this and the upper command?
     * @param status
     * @param parameter
     */
    void replyToCommand(ReturnValue_t status, uint32_t parameter = 0);

    /**
     * Do the transition to the main modes (MODE_ON, MODE_NORMAL and MODE_RAW).
     *
     * If the transition is complete, the mode should be set to the target mode,
     * which can be deduced from the current mode which is
     * [_MODE_TO_ON, _MODE_TO_NORMAL, _MODE_TO_RAW]
     *
     * The intended target submode is already set.
     * The origin submode can be read in subModeFrom.
     *
     * If the transition can not be completed, the child class can try to reach
     * an working mode by setting the mode either directly
     * or setting the mode to an transitional mode (TO_ON, TO_NORMAL, TO_RAW)
     * if the device needs to be reconfigured.
     *
     * If nothing works, the child class can wait for the timeout and the base
     * class will reset the mode to the mode where the transition
     * originated from (the child should report the reason for the failed transition).
     *
     * The intended way to send commands is to set a flag (enum) indicating
     * which command is to be sent here and then to check in
     * buildTransitionCommand() for the flag. This flag can also be used by
     * doStartUp() and doShutDown() to get a nice and clean implementation of
     * buildTransitionCommand() without switching through modes.
     *
     * When the the condition for the completion of the transition is met, the
     * mode can be set, for example in the scanForReply() function.
     *
     * The default implementation goes into the target mode directly.
     *
     * #transitionFailure can be set to a failure code indicating the reason
     * for a failed transition
     *
     * @param modeFrom
     * The mode the transition originated from:
     * [MODE_ON, MODE_NORMAL, MODE_RAW and _MODE_POWER_DOWN (if the mode changed
     * from _MODE_START_UP to _MODE_TO_ON)]
     * @param subModeFrom the subMode of modeFrom
     */
    virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom);

    /**
     * Get the communication action for the current step.
     * The step number can be read from #pstStep.
     * @return The communication action to execute in this step
     */
    virtual CommunicationAction getComAction();

    /**
     * Checks state of switches in conjunction with mode and triggers an event
     * if they don't fit.
     */
    virtual void checkSwitchState();

    /**
     * Reserved for the rare case where a device needs to perform additional
     * operation cyclically in OFF mode.
     */
    virtual void doOffActivity();

    /**
     * Reserved for the rare case where a device needs to perform additional
     * operation cyclically in ON mode.
     */
    virtual void doOnActivity();

    /**
     * Required for HasLocalDataPoolIF, return a handle to the local pool manager.
     * @return
     */
    LocalDataPoolManager* getHkManagerHandle() override;

    /**
     * Returns the delay cycle count of a reply.
     * A count != 0 indicates that the command is already executed.
     * @param deviceCommand	The command to look for
     * @return
     * The current delay count. If the command does not exist  (should never
     * happen) it returns 0.
     */
    uint8_t getReplyDelayCycles(DeviceCommandId_t deviceCommand);

    /**
     * Calls replyRawData() with #defaultRawReceiver, but checks if wiretapping
     * is active and if so, does not send the data as the wiretapping will have
     * sent it already
     */
    void replyRawReplyIfnotWiretapped(const uint8_t *data, size_t len);

    /**
     * Enable the reply checking for a command
     *
     * Is only called, if the command was sent (i.e. the getWriteReply was
     * successful). Must ensure that all replies are activated and correctly
     * linked to the command that initiated it.
     * The default implementation looks for a reply with the same id as the
     * command id in the replyMap or uses the alternativeReplyId if flagged so.
     * When found, copies maxDelayCycles to delayCycles in the reply information
     * and sets the command to expect one reply.
     *
     * Can be overwritten by the child, if a command activates multiple replies
     * or replyId differs from commandId.
     * Notes for child implementations:
     * 	- If the command was not found in the reply map,
     * 	  NO_REPLY_EXPECTED MUST be returned.
     * 	- A failure code may be returned if something went fundamentally wrong.
     *
     * @param deviceCommand
     * @return 	- RETURN_OK if a reply was activated.
     * 			- NO_REPLY_EXPECTED if there was no reply found. This is not an
     * 			  error case as many commands do not expect a reply.
     */
    virtual ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator cmd,
            uint8_t expectedReplies = 1, bool useAlternateId = false,
            DeviceCommandId_t alternateReplyID = 0);

    /**
     * @brief   Build the device command to send for raw mode.
     * @details
     * This is only called in @c MODE_RAW. It is for the rare case that in
     * raw mode packets are to be sent by the handler itself. It is NOT needed
     * for the raw commanding service. Its only current use is in the STR
     * handler which gets its raw packets from a different source.
     * Also it can be used for transitional commands, to get the device ready
     * for @c MODE_RAW
     *
     * As it is almost never used, there is a default implementation
     * returning @c NOTHING_TO_SEND.
     *
     * #rawPacket and #rawPacketLen must be set by this method to the packet
     * to be sent.
     *
     * @param[out] id the device command id built
     * @return
     *    - @c RETURN_OK when a command is to be sent
     *    - not @c NOTHING_TO_SEND when no command is to be sent
     */
    virtual ReturnValue_t buildChildRawCommand();

    /**
     * @brief   Construct a command reply containing a raw reply.
     * @details
     * It gets space in the #IPCStore, copies data there, then sends a raw reply
     * containing the store address. This method is virtual, as devices can have different channels
     * to send raw replies
     *
     * @param data data to send
     * @param len length of @c data
     * @param sendTo the messageQueueId of the one to send to
     * @param isCommand marks the raw data as a command, the message then
     * will be of type raw_command
     */
    virtual void replyRawData(const uint8_t *data, size_t len,
            MessageQueueId_t sendTo, bool isCommand = false);

    /**
     * Get the state of the PCDU switches in the local datapool
     * @return
     *  - @c PowerSwitchIF::SWITCH_ON if all switches specified
     *       by #switches are on
     *  - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by
     *       #switches are off
     *  - @c PowerSwitchIF::RETURN_FAILED if an error occured
     */
    ReturnValue_t getStateOfSwitches();

    /**
     * Children can overwrite this function to suppress checking of the
     * command Queue
     *
     * This can be used when the child does not want to receive a command in
     * a certain situation. Care must be taken that checking is not
     * permanentely disabled as this would render the handler unusable.
     *
     * @return whether checking the queue should NOT be done
     */
    virtual bool dontCheckQueue();

    Mode_t getBaseMode(Mode_t transitionMode);

    bool isAwaitingReply();

    void handleDeviceTM(SerializeIF *dataSet, DeviceCommandId_t replyId,
            bool forceDirectTm = false);
    //	void handleDeviceTM(uint8_t* data, size_t dataSize, DeviceCommandId_t replyId,
    //	        bool forceDirectTm);

    virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
            uint32_t *msToReachTheMode);

    virtual ReturnValue_t letChildHandleMessage(CommandMessage *message);

    /**
     * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper"
     * faster about executed events.
     * This is a bit sneaky, but improves responsiveness of the device FDIR.
     * @param event	The event to be thrown
     * @param parameter1	Optional parameter 1
     * @param parameter2	Optional parameter 2
     */
    void triggerEvent(Event event, uint32_t parameter1 = 0,
            uint32_t parameter2 = 0);
    /**
     * Same as triggerEvent, but for forwarding if object is used as proxy.
     */
    virtual void forwardEvent(Event event, uint32_t parameter1 = 0,
            uint32_t parameter2 = 0) const;

    /**
     * Checks if current mode is transitional mode.
     * @return true if mode is transitional, false else.
     */
    bool isTransitionalMode();

    /**
     * Checks if current handler state allows reception of external device commands.
     * Default implementation allows commands only in plain MODE_ON and MODE_NORMAL.
     * @return RETURN_OK if commands are accepted, anything else otherwise.
     */
    virtual ReturnValue_t acceptExternalDeviceCommands();

    bool commandIsExecuting(DeviceCommandId_t commandId);

    /**
     * set all switches returned by getSwitches()
     *
     * @param onOff on == @c SWITCH_ON; off != @c SWITCH_ON
     */
    void commandSwitch(ReturnValue_t onOff);

private:

    /**
     * State a cookie is in.
     *
     * Used to keep track of the state of the RMAP communication.
     */
    enum CookieState_t {
        COOKIE_UNUSED,    //!< The Cookie is unused
        COOKIE_WRITE_READY, //!< There's data available to send.
        COOKIE_READ_SENT, //!< A sendRead command was sent with this cookie
        COOKIE_WRITE_SENT //!< A sendWrite command was sent with this cookie
    };
    /**
     * Information about a cookie.
     *
     * This is stored in a map for each cookie, to not only track the state,
     * but also information about the sent command. Tracking this information
     * is needed as the state of a commandId (waiting for reply) is done when a
     * write reply is received.
     */
    struct CookieInfo {
        CookieState_t state;
        DeviceCommandMap::iterator pendingCommand;
    };

    /**
     * @brief   Info about the #cookie
     * Used to track the state of the communication
     */
    CookieInfo cookieInfo;

    /** the object used to set power switches */
    PowerSwitchIF *powerSwitcher = nullptr;

    /** HK destination can also be set individually */
    object_id_t hkDestination = objects::NO_OBJECT;

    /**
     * @brief   Used for timing out mode transitions.
     * Set when setMode() is called.
     */
    uint32_t timeoutStart = 0;

    bool setStartupImmediately = false;

    /**
     * Delay for the current mode transition, used for time out
     */
    uint32_t childTransitionDelay;

    /**
     * @brief   The mode the current transition originated from
     *
     * This is private so the child can not change it and mess up the timeouts
     *
     * IMPORTANT: This is not valid during _MODE_SHUT_DOWN and _MODE_START_UP!!
     * (it is _MODE_POWER_DOWN during this modes)
     *
     * is element of [MODE_ON, MODE_NORMAL, MODE_RAW]
     */
    Mode_t transitionSourceMode;

    /**
     * the submode of the source mode during a transition
     */
    Submode_t transitionSourceSubMode;

    /**
     * read the command queue
     */
    void readCommandQueue(void);

    /**
     * Handle the device handler mode.
     *
     * - checks whether commands are valid for the current mode, rejects
     *   them accordingly
     * - checks whether commanded mode transitions are required and calls
     *   handleCommandedModeTransition()
     * - does the necessary action for the current mode or calls
     *   doChildStateMachine in modes @c MODE_TO_ON and @c MODE_TO_OFF
     * - actions that happen in transitions (e.g. setting a timeout) are
     *   handled in setMode()
     */
    void doStateMachine(void);

    void buildRawDeviceCommand(CommandMessage* message);
    void buildInternalCommand(void);
    /**
     * Decrement the counter for the timout of replies.
     *
     * This is called at the beginning of each cycle. It checks whether a
     * reply has timed out (that means a reply was expected but not received).
     */
    void decrementDeviceReplyMap(void);
    /**
     * Convenience function to handle a reply.
     *
     * Called after scanForReply() has found a packet. Checks if the found ID
     * is in the #deviceCommandMap, if so,  calls
     * #interpretDeviceReply for further action.
     *
     * It also resets the timeout counter for the command id.
     *
     * @param data the found packet
     * @param id the found id
     * @foundLen the length of the packet
     */
    void handleReply(const uint8_t *data, DeviceCommandId_t id, uint32_t foundLen);
    void replyToReply(DeviceReplyMap::iterator iter, ReturnValue_t status);

    /**
     * Build and send a command to the device.
     *
     * This routine checks whether a raw or direct command has been received,
     * checks the content of the received command and calls
     * buildCommandFromCommand() for direct commands or sets #rawpacket
     * to the received raw packet.
     * If no external command is received or the received command is invalid and
     * the current mode is @c MODE_NORMAL or a transitional mode, it asks the
     * child class to build a command (via getNormalDeviceCommand() or
     * getTransitionalDeviceCommand() and buildCommand()) and
     * sends the command via RMAP.
     */
    void doSendWrite(void);
    /**
     * Check if the RMAP sendWrite action was successful.
     *
     * Depending on the result, the following is done
     * - if the device command was external commanded, a reply is sent
     *   indicating the result
     * - if the action was successful, the reply timout counter is initialized
     */
    void doGetWrite(void);
    /**
     * Send a RMAP getRead command.
     *
     * The size of the getRead command is #maxDeviceReplyLen.
     * This is always executed, independently from the current mode.
     */
    void doSendRead(void);
    /**
     * Check the getRead reply and the contained data.
     *
     * If data was received scanForReply() and, if successful, handleReply()
     * are called. If the current mode is @c MODE_RAW, the received packet
     * is sent to the commanding object via commandQueue.
     */
    void doGetRead(void);

    /**
     * Retrive data from the #IPCStore.
     *
     * @param storageAddress
     * @param[out] data
     * @param[out] len
     * @return
     *   - @c RETURN_OK @c data is valid
     *   - @c RETURN_FAILED IPCStore is nullptr
     *   - the return value from the IPCStore if it was not @c RETURN_OK
     */
    ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data,
            uint32_t *len);

    /**
     * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW, nothing else!
     */
    void setTransition(Mode_t modeTo, Submode_t submodeTo);

    /**
     * Calls the right child function for the transitional submodes
     */
    void callChildStatemachine();

    ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message);

    virtual dur_millis_t getPeriodicOperationFrequency() const override;

    void parseReply(const uint8_t* receivedData,
            size_t receivedDataLen);

    void handleTransitionToOnMode(Mode_t commandedMode,
            Submode_t commandedSubmode);

    /**
     * Generic internal printer function which also handles printing the object ID.
     * @param errorType
     * @param functionName
     * @param errorCode
     * @param errorPrint
     */
    void printWarningOrError(sif::OutputTypes errorType,
            const char* functionName,
            ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED,
            const char* errorPrint = nullptr);
};

#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */