#pragma once

#include "data/command/InertiaCommand.h"
#include "data/config/NexoDAQSettings.h"

namespace inertia
{
enum ConfigType : unsigned;
enum NexoDAQDescriptorID : uint8_t;

enum GCCCommandID : uint8_t {
	GCC_COMMAND_NEXO_WAKEUP_GET_TRIGGER_TIME = 0,
	GCC_COMMAND_NEXO_WAKEUP_LOG_TRIGGER_TIME = 1,
	GCC_COMMAND_NEXO_WAKEUP_LOG_TRIGGER_TIME_EVENT = 2,
	GCC_COMMAND_NEXO_GPIO_GET = 0,
	GCC_COMMAND_NEXO_GPIO_SET = 1,
	GCC_COMMAND_NEXO_GPIO_TOGGLE = 2,
	GCC_COMMAND_NEXO_GPIO_GET_TRIGGER_TIME = 3,
	GCC_COMMAND_NEXO_USER_CALIBRATION_GET_ALL = 0,
	GCC_COMMAND_NEXO_USER_CALIBRATION_GET_CURRENT = 1,
	GCC_COMMAND_NEXO_USER_CALIBRATION_ADD = 2,
	GCC_COMMAND_NEXO_USER_CALIBRATION_REMOVE = 3,
	GCC_COMMAND_NEXO_USER_CALIBRATION_CLEAR_ALL = 4,
	GCC_COMMAND_NEXO_TEDS_SET_MODE = 0,
	GCC_COMMAND_NEXO_TEDS_WRITE = 1,
	GCC_COMMAND_NEXO_TEDS_READ = 2,
};


struct INERTIA_API GCCCommand
	: InertiaCommand
{
	uint8_t gccDescriptorTypeID;
	ConfigType gccConfigTypeID;

protected:
	GCCCommand(uint8_t const& _gccDescriptorTypeID, ConfigType const& _gccConfigTypeID);
	GCCCommand(const uint8_t* packet, const unsigned max_size);
	GCCCommand(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

public:
	InertiaCommandGroups GetGroup() const override;
	uint8_t GetType() const override;

	static InertiaCommandPtr Parse(const uint8_t* in, unsigned const packetSize);
	static InertiaCommandPtr Parse(std::wstring const& argString);
};


struct INERTIA_API GCCCommandNexo
	: GCCCommand
{
	uint8_t channelMask;
	uint8_t channelDescriptorID;
	uint8_t commandID;

protected:
	GCCCommandNexo(uint8_t const& _channelMask, uint8_t const& _channelDescriptorID, uint8_t const& _commandID);
	GCCCommandNexo(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexo(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

public:
#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif
};


struct INERTIA_API GCCCommandNexoWakeupGetTriggerTimeRequest
	: GCCCommandNexo
{
	GCCCommandNexoWakeupGetTriggerTimeRequest(uint8_t const& channelMask);
	GCCCommandNexoWakeupGetTriggerTimeRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoWakeupGetTriggerTimeRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoWakeupGetTriggerTimeResponse
	: GCCCommandNexo
{
	uint32_t sampleCounter;

	GCCCommandNexoWakeupGetTriggerTimeResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoWakeupGetTriggerTimeResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoWakeupLogTriggerTimeRequest
	: GCCCommandNexo
{
	GCCCommandNexoWakeupLogTriggerTimeRequest(uint8_t const& channelMask);
	GCCCommandNexoWakeupLogTriggerTimeRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoWakeupLogTriggerTimeRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoWakeupLogTriggerTimeResponse
	: GCCCommandNexo
	, BoolValue
{

	GCCCommandNexoWakeupLogTriggerTimeResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoWakeupLogTriggerTimeResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoWakeupLogTriggerTimeEvent
	: GCCCommandNexo
{
	uint32_t sampleCounter;

	GCCCommandNexoWakeupLogTriggerTimeEvent(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoWakeupLogTriggerTimeEvent(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOGetRequest
	: GCCCommandNexo
{
	GCCCommandNexoGPIOGetRequest(uint8_t const& channelMask);
	GCCCommandNexoGPIOGetRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOGetRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOGetResponse
	: GCCCommandNexo
{
	uint16_t value;

	GCCCommandNexoGPIOGetResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOGetResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOSetRequest
	: GCCCommandNexo
{
	uint16_t value;

	GCCCommandNexoGPIOSetRequest(uint8_t const& channelMask, uint16_t const& _value);
	GCCCommandNexoGPIOSetRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOSetRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOSetResponse
	: GCCCommandNexo
	, BoolValue
{

	GCCCommandNexoGPIOSetResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOSetResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOToggleRequest
	: GCCCommandNexo
{
	GCCCommandNexoGPIOToggleRequest(uint8_t const& channelMask);
	GCCCommandNexoGPIOToggleRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOToggleRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOToggleResponse
	: GCCCommandNexo
	, BoolValue
{

	GCCCommandNexoGPIOToggleResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOToggleResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOGetTriggerTimeRequest
	: GCCCommandNexo
{
	GCCCommandNexoGPIOGetTriggerTimeRequest(uint8_t const& channelMask);
	GCCCommandNexoGPIOGetTriggerTimeRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOGetTriggerTimeRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoGPIOGetTriggerTimeResponse
	: GCCCommandNexo
{
	uint32_t sampleCounter;

	GCCCommandNexoGPIOGetTriggerTimeResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoGPIOGetTriggerTimeResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationEntry
{
	float offset;
	float gain;
	std::vector<NexoDAQDescriptorID> descriptors;

	GCCCommandNexoUserCalibrationEntry();
	GCCCommandNexoUserCalibrationEntry(float const& _offset, float const& _gain, std::vector<NexoDAQDescriptorID> const& _descriptors);
	GCCCommandNexoUserCalibrationEntry(std::wstring const& argString);

	const std::wstring ToString() const;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif
};


struct INERTIA_API GCCCommandNexoUserCalibrationGetAllRequest
	: GCCCommandNexo
{
	GCCCommandNexoUserCalibrationGetAllRequest(uint8_t const& channelMask);
	GCCCommandNexoUserCalibrationGetAllRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationGetAllRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationGetAllResponse
	: GCCCommandNexo
{
	std::vector<uint8_t> rawdata;
	std::vector<GCCCommandNexoUserCalibrationEntry> entries;

	GCCCommandNexoUserCalibrationGetAllResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationGetAllResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationGetCurrentRequest
	: GCCCommandNexo
{
	GCCCommandNexoUserCalibrationGetCurrentRequest(uint8_t const& channelMask);
	GCCCommandNexoUserCalibrationGetCurrentRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationGetCurrentRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationGetCurrentResponse
	: GCCCommandNexo
{
	uint8_t tableIndex;
	uint8_t rssDistance;

	GCCCommandNexoUserCalibrationGetCurrentResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationGetCurrentResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationAddRequest
	: GCCCommandNexo
{
	float offset;
	float gain;

	GCCCommandNexoUserCalibrationAddRequest(uint8_t const& channelMask, float const& _offset, float const& _gain);
	GCCCommandNexoUserCalibrationAddRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationAddRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationAddResponse
	: GCCCommandNexo
	, BoolValue
{
	GCCCommandNexoUserCalibrationAddResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationAddResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationRemoveRequest
	: GCCCommandNexo
{
	uint8_t tableIndex;

	GCCCommandNexoUserCalibrationRemoveRequest(uint8_t const& channelMask, uint8_t const& _tableIndex);
	GCCCommandNexoUserCalibrationRemoveRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationRemoveRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationRemoveResponse
	: GCCCommandNexo
	, BoolValue
{
	GCCCommandNexoUserCalibrationRemoveResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationRemoveResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationClearAllRequest
	: GCCCommandNexo
{
	GCCCommandNexoUserCalibrationClearAllRequest(uint8_t const& channelMask);
	GCCCommandNexoUserCalibrationClearAllRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationClearAllRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoUserCalibrationClearAllResponse
	: GCCCommandNexo
	, BoolValue
{
	GCCCommandNexoUserCalibrationClearAllResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoUserCalibrationClearAllResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSSetModeRequest
	: GCCCommandNexo
{
	NexoDAQTEDSMode mode;

	GCCCommandNexoTEDSSetModeRequest(uint8_t const& channelMask, NexoDAQTEDSMode const& _mode);
	GCCCommandNexoTEDSSetModeRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSSetModeRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSSetModeResponse
	: GCCCommandNexo
	, BoolValue
{
	GCCCommandNexoTEDSSetModeResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSSetModeResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSWriteRequest
	: GCCCommandNexo
{
	std::vector<uint8_t> inbound;

	GCCCommandNexoTEDSWriteRequest(uint8_t const& channelMask, const std::vector<uint8_t>& _inbound);
	GCCCommandNexoTEDSWriteRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSWriteRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSWriteResponse
	: GCCCommandNexo
	, BoolValue
{
	GCCCommandNexoTEDSWriteResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSWriteResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSReadRequest
	: GCCCommandNexo
{
	GCCCommandNexoTEDSReadRequest(uint8_t const& channelMask);
	GCCCommandNexoTEDSReadRequest(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSReadRequest(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};


struct INERTIA_API GCCCommandNexoTEDSReadResponse
	: GCCCommandNexo
{
	std::vector<uint8_t> outbound;

	GCCCommandNexoTEDSReadResponse(const uint8_t* packet, const unsigned max_size);
	GCCCommandNexoTEDSReadResponse(std::wstring const& argString);

	void GetBytes(std::vector<uint8_t>& out) const override;
	const std::wstring ToString() const override;

#ifdef WITH_BOOST_PROGRAM_OPTIONS
	static const po::options_description GetOptionDescription();
#endif

	bool IsResponse() const override;
};

}
