#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_

#include "DeviceHandlerIF.h"
#include "DeviceCommunicationIF.h"
#include "DeviceHandlerFailureIsolation.h"
#include "DeviceHandlerThermalSet.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 "../datapool/HkSwitchHelper.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. Calls hkSwitcher.performOperation()
	 *   7. 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();

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;

	/**
	 * 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.
	 *
	 * @details
	 * #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.
	 *  - Anything else triggers an event with the
	 *    returnvalue as a parameter
	 */
	virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
			const uint8_t * commandData, size_t commandDataLen) = 0;

	/**
	 * @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;

	/**
	 * @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);

	ReturnValue_t setReplyDataset(DeviceCommandId_t replyId,
	        LocalPoolDataSetBase* dataset);

	/**
	 * @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);

	/**
	 * 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;

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

	/**
	 * 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(LocalDataPool& localDataPoolMap,
				LocalDataPoolManager& poolManager) override;

	/** Get the HK manager object handle */
	virtual LocalDataPoolManager* getHkManagerHandle() override;

	/**
	 * @brief 	Hook function for child handlers which is called once per
	 * 			performOperation(). Default implementation is empty.
	 */
	virtual void performOperationHook();

public:
	/** 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, uint16_t parameterId,
			ParameterWrapper *parameterWrapper,
			const ParameterWrapper *newValues, uint16_t startAtIndex) override;
	/**
	 * 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_);
	virtual MessageQueueId_t getCommandQueue(void) const;

protected:
	/**
	 * The Returnvalues id of this class, required by HasReturnvaluesIF
	 */
	static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE;

	static const ReturnValue_t INVALID_CHANNEL = MAKE_RETURN_CODE(0xA0);
	// Returnvalues for scanForReply()
	static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(0xB0); //!< This is used to specify for replies from a device which are not replies to requests
	static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(0xB1); //!< Ignore parts of the received packet
	static const ReturnValue_t IGNORE_FULL_PACKET = MAKE_RETURN_CODE(0xB2); //!< Ignore full received packet
	// Returnvalues for command building
	static const ReturnValue_t NOTHING_TO_SEND = MAKE_RETURN_CODE(0xC0); //!< Return this if no command sending in required
	static const ReturnValue_t COMMAND_MAP_ERROR = MAKE_RETURN_CODE(0xC2);
	// Returnvalues 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 hkManager;

	/**
	 *  @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;

	HkSwitchHelper hkSwitcher;

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

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

	//! 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   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();

	/**
	 * 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;

	/**
	 * Helper function to report a missed reply
	 *
	 * 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);

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

	void replyToCommand(ReturnValue_t status, uint32_t parameter = 0);

	/**
	 * Set the device handler mode
	 *
	 * Sets #timeoutStart with every call.
	 *
	 * 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);

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

	/**
	 * @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);

	/**
	 * 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();

	/**
	 * Build the device command to send for raw mode.
	 *
	 * 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();

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

	/**
	 * Construct a command reply containing a raw reply.
	 *
	 * 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);

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

	/**
	 * @brief	Notify child about mode change.
	 */
	virtual void modeChanged(void);

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

	/**
	 * 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(void);

	/**
	 * build a list of sids and pass it to the #hkSwitcher
	 */
	virtual void changeHK(Mode_t mode, Submode_t submode, bool enable);

	/**
	 * 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 commandId,
			bool neverInDataPool = false, bool forceDirectTm = false);

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

	/* HasModesIF overrides */
	virtual void startTransition(Mode_t mode, Submode_t submode) override;
	virtual void setToExternalControl() override;
	virtual void announceMode(bool recursive) override;

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

	/**
	 * 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 fuck 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 LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;

    virtual dur_millis_t getPeriodicOperationFrequency() const override;

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

    void handleTransitionToOnMode(Mode_t commandedMode,
    		Submode_t commandedSubmode);
};

#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */