#ifndef MISSION_DEVICES_IMTQHANDLER_H_
#define MISSION_DEVICES_IMTQHANDLER_H_

#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <mission/devices/devicedefinitions/IMTQHandlerDefinitions.h>
#include <string.h>

/**
 * @brief	This is the device handler for the ISIS Magnetorquer iMTQ.
 *
 * @author	J. Meier
 */
class IMTQHandler: public DeviceHandlerBase {
public:

    IMTQHandler(object_id_t objectId, object_id_t comIF, CookieIF * comCookie);
	virtual ~IMTQHandler();

	/**
     * @brief   Sets mode to MODE_NORMAL. Can be used for debugging.
     */
    void setModeNormal();

protected:
	void doStartUp() override;
	void doShutDown() override;
	ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) override;
	ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t * id) override;
	void fillCommandAndReplyMap() override;
	ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand,
				const uint8_t * commandData,size_t commandDataLen) override;
	ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize,
			DeviceCommandId_t *foundId, size_t *foundLen) override;
	ReturnValue_t interpretDeviceReply(DeviceCommandId_t id,
			const uint8_t *packet) override;
	void setNormalDatapoolEntriesInvalid() override;
	virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
	uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
	ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
            LocalDataPoolManager& poolManager) override;

private:

    static const uint8_t INTERFACE_ID = CLASS_ID::IMTQ_HANDLER;

    static const ReturnValue_t INVALID_COMMAND_CODE = MAKE_RETURN_CODE(0xA0);
    static const ReturnValue_t PARAMETER_MISSING = MAKE_RETURN_CODE(0xA1);
    static const ReturnValue_t PARAMETER_INVALID = MAKE_RETURN_CODE(0xA2);
    static const ReturnValue_t CC_UNAVAILABLE = MAKE_RETURN_CODE(0xA3);
    static const ReturnValue_t INTERNAL_PROCESSING_ERROR = MAKE_RETURN_CODE(0xA4);
    static const ReturnValue_t REJECTED_WITHOUT_REASON = MAKE_RETURN_CODE(0xA5);
    static const ReturnValue_t CMD_ERR_UNKNOWN = MAKE_RETURN_CODE(0xA6);
    //! [EXPORT] : [COMMENT] The status reply to a self test command was received but no self test command has been sent. This should normally never happen.
    static const ReturnValue_t UNEXPECTED_SELF_TEST_REPLY = MAKE_RETURN_CODE(0xA7);

    static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::IMTQ_HANDLER;

    //! [EXPORT] : [COMMENT] Get self test result returns I2C failure
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_I2C_FAILURE = MAKE_EVENT(1, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns SPI failure. This concerns the MTM connectivity.
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_SPI_FAILURE = MAKE_EVENT(2, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns failure in measurement of current and temperature.
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_ADC_FAILURE = MAKE_EVENT(3, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns PWM failure which concerns the coil actuation.
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_PWM_FAILURE = MAKE_EVENT(4, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns TC failure (system failure)
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_TC_FAILURE = MAKE_EVENT(5, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns failure that MTM values were outside of the expected range.
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_MTM_RANGE_FAILURE = MAKE_EVENT(6, severity::LOW);
    //! [EXPORT] : [COMMENT] Get self test result returns failure indicating that the coil current was outside of the expected range
    //! P1: Indicates on which axis the failure occurred. 0 -> INIT, 1 -> +X, 2 -> -X, 3 -> +Y, 4 -> -Y, 5 -> +Z, 6 -> -Z, 7 -> FINA
    static const Event SELF_TEST_COIL_CURRENT_FAILURE = MAKE_EVENT(7, severity::LOW);
    //! [EXPORT] : [COMMENT] Received invalid error byte. This indicates an error of the communication link between IMTQ and OBC.
    static const Event INVALID_ERROR_BYTE = MAKE_EVENT(8, severity::LOW);

	IMTQ::EngHkDataset engHkDataset;
	IMTQ::CalibratedMtmMeasurementSet calMtmMeasurementSet;
	IMTQ::RawMtmMeasurementSet rawMtmMeasurementSet;
	IMTQ::PosXSelfTestSet posXselfTestDataset;
	IMTQ::NegXSelfTestSet negXselfTestDataset;
	IMTQ::PosYSelfTestSet posYselfTestDataset;
	IMTQ::NegYSelfTestSet negYselfTestDataset;
	IMTQ::PosZSelfTestSet posZselfTestDataset;
	IMTQ::NegZSelfTestSet negZselfTestDataset;

	uint8_t commandBuffer[IMTQ::MAX_COMMAND_SIZE];

	enum class CommunicationStep {
        GET_ENG_HK_DATA,
        START_MTM_MEASUREMENT,
        GET_CAL_MTM_MEASUREMENT,
        GET_RAW_MTM_MEASUREMENT
    };

	CommunicationStep communicationStep = CommunicationStep::GET_ENG_HK_DATA;

	enum class StartupStep {
	    NONE,
	    COMMAND_SELF_TEST,
	    GET_SELF_TEST_RESULT
    };

	StartupStep startupStep = StartupStep::COMMAND_SELF_TEST;

	bool selfTestPerformed = false;

	/**
	 * @brief   In case of a status reply to a single axis self test command, this function
	 *          searches for the actual pending command.
	 */
	ReturnValue_t getSelfTestCommandId(DeviceCommandId_t* id);

	/**
	 * @brief   Each reply contains a status byte giving information about a request. This function
	 *          parses this byte and returns the associated failure message.
	 *
	 * @param packet    Pointer to the received message containing the status byte.
	 *
	 * @return  The return code derived from the received status byte.
	 */
	ReturnValue_t parseStatusByte(const uint8_t* packet);

	/**
	 * @brief   This function fills the engineering housekeeping dataset with the received data.

	 * @param packet    Pointer to the received data.
	 *
	 */
	void fillEngHkDataset(const uint8_t* packet);

	/**
     * @brief   This function sends a command reply to the requesting queue.
     *
     * @param data  Pointer to the data to send.
     * @param dataSize  Size of the data to send.
     * @param relplyId  Reply id which will be inserted at the beginning of the action message.
     */
    void handleDeviceTM(const uint8_t* data, size_t dataSize, DeviceCommandId_t replyId);

    /**
     * @brief   This function handles the reply containing the commanded dipole.
     *
     * @param packet    Pointer to the reply data.
     */
    void handleGetCommandedDipoleReply(const uint8_t* packet);

    /**
     * @brief   This function parses the reply containing the calibrated MTM measurement and writes
     *          the values to the appropriate dataset.
     * @param packet    Pointer to the reply data.
     */
    void fillCalibratedMtmDataset(const uint8_t* packet);

    /**
     * @brief   This function copies the raw MTM measurements to the MTM raw dataset.
     * @param packet    Pointer to the reply data requested with the GET_RAW_MTM_MEASUREMENTS
     *                  command.
     */
    void fillRawMtmDataset(const uint8_t* packet);

    /**
     * @brief   This function handles all self test results. This comprises parsing the error byte
     *          and step byte and calling the function to fill the respective dataset.
     */
    void handleSelfTestReply(const uint8_t* packet);

    /**
     * @brief   The following functions fill the respective dataset of the single axis self tests.
     * @param packet    Pointer to the reply data holding the self test result.
     */
    void handlePositiveXSelfTestReply(const uint8_t* packet);
    void handleNegativeXSelfTestReply(const uint8_t* packet);
    void handlePositiveYSelfTestReply(const uint8_t* packet);
    void handleNegativeYSelfTestReply(const uint8_t* packet);
    void handlePositiveZSelfTestReply(const uint8_t* packet);
    void handleNegativeZSelfTestReply(const uint8_t* packet);

    /**
     * @brief   This function checks the error byte of a self test measurement.
     *
     * @param errorByte The received error byte to check
     * @param step  The step of the error byte.
     */
    void checkErrorByte(const uint8_t errorByte, const uint8_t step);

    std::string makeStepString(const uint8_t step);
};

#endif /* MISSION_DEVICES_IMTQHANDLER_H_ */