#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_GOMSPACEPACKETS_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_GOMSPACEPACKETS_H_

#include <fsfw/serialize/SerialBufferAdapter.h>
#include <fsfw/serialize/SerializeElement.h>
#include <fsfw/serialize/SerialLinkedListAdapter.h>
#include <fsfw/serialize/SerialFixedArrayListAdapter.h>
#include <mission/devices/devicedefinitions/GomspaceDefinitions.h>


/**
 * @brief	This class can be used to generated the command for the CspComIF
 * 			to reset the watchdog in a gomspace device.
 */
class WatchdogResetCommand : public SerialLinkedListAdapter<SerializeIF> {
public:

	WatchdogResetCommand() {
		setLinks();
	}

private:
	WatchdogResetCommand(const WatchdogResetCommand &command);
	void setLinks() {
		setStart(&cspPort);
		cspPort.setNext(&querySize);
		querySize.setNext(&magic);
	}
	SerializeElement<uint8_t> cspPort = GOMSPACE::P60_PORT_GNDWDT_RESET;
	SerializeElement<uint16_t> querySize = 1;
	/* Sending 0x78 to port 9 of a gomspace device resets the ground watchdog */
	SerializeElement<uint8_t> magic = 0x78;
};


/**
 * @brief	A serial linked list adapter implementation to generate ping
 * 			commands for devices supporting the CSP protocol. This command can
 * 			be sent to the CspComIF which will send out the ping request.
 *
 * @details	A ping request simply sends back the received data provided by the
 * 			data buffer. cspPort and querySize are only informations required
 * 			by the CspComI and other than the data array not physically
 * 			transmitted to the target device.
 */
class CspPingCommand : public SerialLinkedListAdapter<SerializeIF> {
public:
	/**
	 * @brief	Constructor
	 *
	 * @param querySize_	The size of bytes replied by the ping request.
	 * 						Amounts to the number of bytes send.
	 * @param data_			Pointer to data which should be sent to the device.
	 * 						All data will be sent back by the ping target.
	 */
	CspPingCommand(const uint8_t* data_, uint16_t querySize_) :
			querySize(querySize_), data(data_, querySize_) {
		setLinks();
	}

private:
	CspPingCommand(const CspPingCommand &command);
	void setLinks() {
		setStart(&cspPort);
		cspPort.setNext(&querySize);
		querySize.setNext(&data);
	}
	SerializeElement<uint8_t> cspPort = GOMSPACE::PING_PORT;
	SerializeElement<uint16_t> querySize;
	SerializeElement<SerialBufferAdapter<uint8_t>> data;
};


/**
 * @brief	A serial linked list adapter implementation of the gs_rparam_query_t
 * 			struct defined in rparam.h. Can be used to build the message to set
 * 			a parameter in gomspace devices.
 *
 * @note	cspPort and querySize will not be sent with the CSP packet to the
 * 			gomspace device but are required for the CspComIF to get the port
 * 			and the size to query.
 */
class CspSetParamCommand : public SerialLinkedListAdapter<SerializeIF> {
public:

	CspSetParamCommand(uint16_t querySize_, uint16_t payloadlength_,
			uint16_t checksum_, uint16_t seq_, uint16_t total_, uint16_t addr_,
			const uint8_t* parameter_, uint8_t parameterCount_) :
			querySize(querySize_), payloadlength(
					payloadlength_), checksum(checksum_), seq(seq_), total(
					total_), addr(addr_), parameter(parameter_, parameterCount_) {
		setLinks();
	}

private:
	CspSetParamCommand(const CspSetParamCommand &command);
	void setLinks() {
		setStart(&cspPort);
		cspPort.setNext(&querySize);
		querySize.setNext(&action);
		action.setNext(&tableId);
		tableId.setNext(&payloadlength);
		payloadlength.setNext(&checksum);
		checksum.setNext(&seq);
		seq.setNext(&total);
		total.setNext(&addr);
		addr.setNext(&parameter);
	}
	SerializeElement<uint8_t> cspPort = GOMSPACE::PARAM_PORT;
	/* Only a parameter will be set. No data will be queried with this command */
	SerializeElement<uint16_t> querySize;
	SerializeElement<uint8_t> action = 0xFF; // param set
	/* We will never set a parameter in a table other than the configuration
	 * table */
	SerializeElement<uint8_t> tableId = 1;
	SerializeElement<uint16_t> payloadlength;
	SerializeElement<uint16_t> checksum;
	SerializeElement<uint16_t> seq;
	SerializeElement<uint16_t> total;
	SerializeElement<uint16_t> addr;
	SerializeElement<SerialBufferAdapter<uint8_t>> parameter;
};


/**
 * @brief	This class can be used to generate a get param command for the
 * 			gomspace devices which will be sent to the device communication
 * 			interface object.
 *
 * @note 	cspPort and querySize only serve as information for the CspComIF
 * 			and will not be transmitted physically to the target device.
 */
class CspGetParamCommand : public SerialLinkedListAdapter<SerializeIF> {
public:
	/* The size of the header of a gomspace CSP packet. */
	static const uint8_t GS_HDR_LENGTH = 12;

	CspGetParamCommand(uint16_t querySize_, uint8_t tableId_,
			uint16_t addresslength_, uint16_t checksum_, uint16_t seq_,
			uint16_t total_, uint16_t addr_) :
			querySize(querySize_), tableId(tableId_), addresslength(
					addresslength_), checksum(checksum_), seq(seq_), total(
					total_), addr(addr_) {
		fixedValuesInit();
		setLinks();
	}

private:
	CspGetParamCommand(const CspGetParamCommand &command);
	void setLinks() {
		setStart(&cspPort);
		cspPort.setNext(&querySize);
		querySize.setNext(&action);
		action.setNext(&tableId);
		tableId.setNext(&addresslength);
		addresslength.setNext(&checksum);
		checksum.setNext(&seq);
		seq.setNext(&total);
		total.setNext(&addr);
	}
	void fixedValuesInit(){
		cspPort.entry = GOMSPACE::PARAM_PORT;
	}
	SerializeElement<uint8_t> cspPort;
	SerializeElement<uint16_t> querySize; // size of bytes to query
	/* Following information will also be physically transmitted to the target
	 * device*/
	SerializeElement<uint8_t> action = 0x00; // get param
	SerializeElement<uint8_t> tableId;
	SerializeElement<uint16_t> addresslength;
	SerializeElement<uint16_t> checksum;
	SerializeElement<uint16_t> seq;
	SerializeElement<uint16_t> total;
	SerializeElement<uint16_t> addr;
};


/**
 * @brief   This class can be used to generate a get param command for the
 *          gomspace devices which will be sent to the device communication
 *          interface object.
 *
 * @note    cspPort and querySize only serve as information for the CspComIF
 *          and will not be transmitted physically to the target device.
 */
class RequestFullTableCommand : public SerialLinkedListAdapter<SerializeIF> {
public:

    RequestFullTableCommand(uint16_t querySize_, uint8_t tableId_) :
            querySize(querySize_), tableId(tableId_) {
        setLinks();
    }

private:
    RequestFullTableCommand(const RequestFullTableCommand &command);
    void setLinks() {
        setStart(&cspPort);
        cspPort.setNext(&querySize);
        querySize.setNext(&action);
        action.setNext(&tableId);
        tableId.setNext(&addresslength);
        addresslength.setNext(&checksum);
        checksum.setNext(&seq);
        seq.setNext(&total);
    }
    SerializeElement<uint8_t> cspPort = GOMSPACE::PARAM_PORT;
    /** Size of bytes to query (size of csp header + size of table) */
    SerializeElement<uint16_t> querySize;
    /* Following information will also be physically transmitted to the target
     * device*/
    SerializeElement<uint8_t> action = 0x00; // get param
    SerializeElement<uint8_t> tableId;
    /* Size of address. Set to 0 to get full table */
    SerializeElement<uint16_t> addresslength = 0;
    SerializeElement<uint16_t> checksum = GOMSPACE::IGNORE_CHECKSUM;
    SerializeElement<uint16_t> seq = 0;
    SerializeElement<uint16_t> total = 0;
};


/**
 * @brief	This class can be used to deserialize replies from gomspace devices
 * 			and extract the relevant data.
 */
class CspGetParamReply : public SerialLinkedListAdapter<SerializeIF> {
public:
	/**
	 * @brief	Constructor
	 *
	 * @param payloadBuffer	Pointer to a buffer to store the payload data of
	 * 						the CSP packet.
	 * @param payloadBufferSz	The size of the payload buffer where the payload
	 * 							data will be stored.
	 */
	CspGetParamReply(uint8_t* payloadBuffer_, uint8_t payloadBufferSz_) :
			payload(payloadBuffer_, payloadBufferSz_) {
		setLinks();
	}

	uint8_t getAction(){
		return action;
	}

	uint8_t getTableId(){
		return tableId;
	}

	uint16_t getLength(){
		return length;
	}

	uint16_t getAddress(){
		return addr;
	}

private:
	CspGetParamReply(const CspGetParamReply &reply);
	void setLinks() {
		setStart(&action);
		action.setNext(&tableId);
		tableId.setNext(&length);
		length.setNext(&checksum);
		checksum.setNext(&seq);
		seq.setNext(&total);
		total.setNext(&addr);
		addr.setNext(&payload);
	}

	SerializeElement<uint8_t> action;
	SerializeElement<uint8_t> tableId;
	SerializeElement<uint16_t> length; //length of address field + payload data
	SerializeElement<uint16_t> checksum;
	SerializeElement<uint16_t> seq;
	SerializeElement<uint16_t> total;
	SerializeElement<uint16_t> addr;
	SerializeElement<SerialBufferAdapter<uint8_t>> payload;
};


/**
 * @brief	This class generates telemetry packets containing data from
 * 			CSP get-parameter-replies.
 */
class ParamReply : public SerialLinkedListAdapter<SerializeIF> {
public:
	/**
	 * @brief	Constructor
	 *
	 * @param payloadBuffer	Pointer to a buffer to store the payload data of
	 * 						the CSP packet.
	 * @param payloadBufferSz	The size of the payload buffer where the payload
	 * 							data will be stored.
	 */
	ParamReply(uint8_t action_, uint8_t tableId_, uint16_t addr_,
			uint16_t payloadLength_, uint8_t* payloadBuffer_) :
			action(action_), tableId(tableId_), addr(addr_), payloadLength(
					payloadLength_), payload(payloadBuffer_, payloadLength) {
		setLinks();
	}

private:
	ParamReply(const CspGetParamReply &reply);
	void setLinks() {
		setStart(&action);
		action.setNext(&tableId);
		tableId.setNext(&addr);
		addr.setNext(&payloadLength);
		payloadLength.setNext(&payload);
	}
	SerializeElement<uint8_t> action;
	SerializeElement<uint8_t> tableId;
	SerializeElement<uint16_t> addr;
	SerializeElement<uint16_t> payloadLength;
	SerializeElement<SerialBufferAdapter<uint16_t>> payload;
};


/**
 * @brief	This class generates the reply containing data from a full housekeeping table request
 *          of the PDU2.
 */
class Pdu2FullTableReply : public SerialLinkedListAdapter<SerializeIF> {
public:
	/**
	 * @brief	Constructor
	 *
	 * @param action_	The command which triggered the full table request.
	 * @param tableId_	The id of the requested table.
	 * @param tableDataset_	The dataset holding the table data.
	 */
    Pdu2FullTableReply(uint8_t action_, uint8_t tableId_, SerializeIF* tableDataset_) :
			action(action_), tableId(tableId_), dataset(tableDataset_) {
		setLinks();
	}

private:
    Pdu2FullTableReply(const Pdu2FullTableReply &reply);
	void setLinks() {
		setStart(&action);
		action.setNext(&tableId);
		tableId.setNext(&dataset);
	}
	SerializeElement<uint8_t> action;
	SerializeElement<uint8_t> tableId;
	LinkedElement<SerializeIF> dataset;
};


/**
 * @brief	This class helps to unpack information from an action message
 * 			to set a parameter in gomspace devices. The action message can be
 * 			for example received from the PUS Service 8.
 */
class SetParamMessageUnpacker: public SerialLinkedListAdapter<SerializeIF> {
public:
	/* Largest parameter is a uint32_t */
	static const uint32_t MAX_SIZE = 4;

	SetParamMessageUnpacker() {
		setLinks();
	}

	uint16_t getAddress() {
		return address;
	}

	uint8_t*  getParameter() {
		return parameter->front();
	}

	uint8_t getParameterSize(){
		return parameter->size;
	}

private:
	void setLinks() {
		setStart(&address);
		address.setNext(&parameter);
	}
	SetParamMessageUnpacker(const SetParamMessageUnpacker &message);
	SerializeElement<uint16_t> address;
	SerializeElement<SerialFixedArrayListAdapter<uint8_t, MAX_SIZE, uint8_t>> parameter;
};


/**
 * @brief   This class generates a message which can be sent to the GomspaceDeviceHandler to
 *          command a parameter change.
 *
 * @details Structure of set parameter command:
 *          |   memory address  |   size of parameter value |   parameter value |
 */
class GomspaceSetParamMessage : public SerialLinkedListAdapter<SerializeIF> {
public:

    /* The size of the largest parameter */
    static const uint8_t MAX_SIZE = 4;

    /**
     * @brief   Constructor
     *
     * @param memoryAddress   The address of the parameter to change in the configuration table.
     * @param parameterValue  Pointer to the parameter value to set.
     * @param parameterSize The size of the parameter.
     *
     */
    GomspaceSetParamMessage(uint16_t memoryAddress, const uint8_t* parameterValue,
            uint8_t parameterSize) :
            memoryAddress(memoryAddress), parameterValueBuffer(parameterValue, parameterSize, true) {
        setLinks();
    }

private:
    GomspaceSetParamMessage(const GomspaceSetParamMessage &reply);
    void setLinks() {
        setStart(&memoryAddress);
        memoryAddress.setNext(&parameterValueBuffer);
    }
    SerializeElement<uint16_t> memoryAddress;
    /**
     * Parameter can be uint8_t, uint16_t or uint32_t. Thus max size of parameterValueBuffer is
     * four bytes.
     */
    SerializeElement<SerialBufferAdapter<uint8_t>> parameterValueBuffer;
};


/**
 * @brief	This class helps to unpack information from an action message
 * 			to get a parameter from gomspace devices. The action message can be
 * 			for example received from the PUS Service 8.
 */
class GetParamMessageUnpacker: public SerialLinkedListAdapter<SerializeIF> {
public:

	GetParamMessageUnpacker() {
		setLinks();
	}

	uint8_t getTableId() {
		return tableId;
	}

	uint16_t getAddress() {
		return address;
	}

	uint8_t getParameterSize(){
		return parameterSize;
	}


private:
	GetParamMessageUnpacker(const GetParamMessageUnpacker &message);
	void setLinks() {
		setStart(&tableId);
		tableId.setNext(&address);
		address.setNext(&parameterSize);
	}
	SerializeElement<uint8_t> tableId;
	SerializeElement<uint16_t> address; //The memory address offset within the table
	/* The size of the requested value (e.g. temperature is a uint16_t value) */
	SerializeElement<uint8_t> parameterSize;
};

#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_GOMSPACEPACKETS_H_ */