#pragma once

namespace inertia
{

/** ServiceVersion */
enum ServiceVersion : unsigned {
	SACP_V0 = 0,
	SACP_V1 = 1
};

/** ServiceV0ID */
enum ServiceV0ID : unsigned {
	SERVICE_0_ECHO_REQUEST = 10,
	SERVICE_0_ECHO_RESPONSE = 11,
	SERVICE_0_ECHO_ERROR = 12,
	SERVICE_0_ADDRESS_ASSIGN = 20,
	SERVICE_0_ADDRESS_ANNOUNCE = 21,
	SERVICE_0_ADDRESS_SERIAL = 26,
	SERVICE_0_ADDRESS_VERSION = 27,
	SERVICE_0_LIST_INTERFACE = 30,
	SERVICE_0_LIST_ARP = 34,
	SERVICE_0_ADD_ARP = 35,
	SERVICE_0_REMOVE_ARP = 36,
	SERVICE_0_CLEAR_ARP = 37,
	SERVICE_0_LIST_FORWARDERS = 40,
	SERVICE_0_ADD_FORWARDER = 41,
	SERVICE_0_REMOVE_FORWARDER = 42,
	SERVICE_0_LIST_CONNECTORS = 44,
	SERVICE_0_ADD_CONNECTOR = 45,
	SERVICE_0_REMOVE_CONNECTOR = 46,
	SERVICE_0_MAC_SLOT_SET = 48,
	SERVICE_0_MAC_SLOT_GET = 49,
	SERVICE_0_RADIO_CHANNEL_SET = 50,
	SERVICE_0_RADIO_CHANNEL_GET = 51,
	SERVICE_0_MAC_SLOT_COUNT_SET = 52,
	SERVICE_0_MAC_SLOT_COUNT_GET = 53,
	SERVICE_0_MAC_SLOT_FREQ_SET = 54,
	SERVICE_0_MAC_SLOT_FREQ_GET = 55,
	SERVICE_0_RADIO_CONFIG_SET = 56,
	SERVICE_0_RADIO_CONFIG_GET = 57,
	SERVICE_0_RADIO_ENABLE_SET = 58,
	SERVICE_0_RADIO_ENABLE_GET = 59
};

/** ServiceGroup */
enum ServiceGroup : unsigned {
	SERVICE_GROUP_GENERAL = 0,
	SERVICE_GROUP_DEVICE = 1,
	SERVICE_GROUP_INTERFACE = 2,
	SERVICE_GROUP_ARP = 3,
	SERVICE_GROUP_FORWARDER = 4,
	SERVICE_GROUP_CONNECTOR = 5,
	SERVICE_GROUP_RADIO = 16,
	SERVICE_GROUP_FASTMAC = 17,
	SERVICE_GROUP_RRP = 999
};

/** ServiceGeneral */
enum ServiceGeneral : uint8_t {
	SERVICE_GENERAL_ECHO_REQUEST = 0,
	SERVICE_GENERAL_RESPONSE = 1,
	SERVICE_GENERAL_ERROR = 2,
	SERVICE_GENERAL_NETINFO = 3
};

/** ServiceDevice */
enum ServiceDevice : uint8_t {
	SERVICE_DEVICE_ADDRESS_ASSIGN = 0,
	SERVICE_DEVICE_ADDRESS_ANNOUNCE = 1,
	SERVICE_DEVICE_SERIAL_GET = 2,
	SERVICE_DEVICE_VERSION_INFO_GET = 3,
	SERVICE_DEVICE_PROCESS_STATS_GET = 4
};

/** ServiceInterface */
enum ServiceInterface : uint8_t {
	SERVICE_INTERFACE_LIST = 0
};

/** ServiceARP */
enum ServiceARP : uint8_t {
	SERVICE_ARP_LIST = 0,
	SERVICE_ARP_ADD = 1,
	SERVICE_ARP_REMOVE = 2,
	SERVICE_ARP_CLEAR = 3
};

/** ServiceForwarder */
enum ServiceForwarder : uint8_t {
	SERVICE_FORWARDER_LIST = 0,
	SERVICE_FORWARDER_ADD = 1,
	SERVICE_FORWARDER_REMOVE = 2
};

/** ServiceConnector */
enum ServiceConnector : uint8_t {
	SERVICE_CONNECTOR_LIST = 0,
	SERVICE_CONNECTOR_ADD = 1,
	SERVICE_CONNECTOR_REMOVE = 2
};

/** ServiceRadio */
enum ServiceRadio : uint8_t {
	SERVICE_RADIO_CONF_SET = 0,
	SERVICE_RADIO_CONF_GET = 1,
	SERVICE_RADIO_ENABLE_SET = 2,
	SERVICE_RADIO_ENABLE_GET = 3
};

/** ServiceFastMac */
enum ServiceFastMac : uint8_t {
	SERVICE_FASTMAC_SLOT_SET = 0,
	SERVICE_FASTMAC_SLOT_GET = 1,
	SERVICE_FASTMAC_CHANNEL_SET = 2,
	SERVICE_FASTMAC_CHANNEL_GET = 3,
	SERVICE_FASTMAC_SLOT_COUNT_SET = 4,
	SERVICE_FASTMAC_SLOT_COUNT_GET = 5,
	SERVICE_FASTMAC_FREQ_SET = 6,
	SERVICE_FASTMAC_FREQ_GET = 7
};

/** ServiceRRP */
enum ServiceRRP : uint8_t {
	SERVICE_RRP_RESPONSE = 0,
	SERVICE_RRP_PROGRESS = 1
};

/** ServiceHardwareTypeID */
enum ServiceHardwareTypeID : uint32_t {
	HW_NONE =                 0x00,
	HW_PROMOVE_GW =           0x04,
	HW_PROMOVE_3D =           0x06,
	HW_IDLER_NODE =           0x08,
	HW_IDLER_GW =             0x09,
	HW_VMON4000_NODE =        0x0A,
	HW_VMON4000_GW =          0x2A,
	HW_PROMOVE_MINI_IRM =     0x0B,
	HW_INERTIA_GW_ADV =       0x0C,
	HW_INERTIA_GW_BSC =       0x0D,
	HW_PROMOVE_MINI_BT =      0x0E,
	HW_PROMOVE_MINI_IRM_GPS = 0x0F,
	HW_PROMOVE_MINI_BT_GPS =  0x10,
	HW_PROMOVE_MINI_BTLE =    0x11,
	HW_NEXODAQ =              0x20,
	HW_EQUI_PRO_GW_BSC =      0x30,
	HW_EQUI_PRO_GW_ADC =      0x31,
	HW_EQUI_PRO_GW_STICK =    0x32,
	HW_PROMOVE_V_HG_BARO_IRM =      0x40,
	HW_PROMOVE_V_HG_BARO_IRM_GNSS = 0x41,
	HW_PROMOVE_V_HG_IRM =           0x42,
	HW_PROMOVE_V_HG_IRM_GNSS =      0x43,
	HW_PROMOVE_V_HG_BARO_BLE =      0x50,
	HW_PROMOVE_V_HG_BARO_BLE_GNSS = 0x51,
	HW_PROMOVE_V_HG_BLE =           0x52,
	HW_PROMOVE_V_HG_BLE_GNSS =      0x53,
	HW_INERTIA_GW_STICK =           0x60
};

/** ServiceFirmwareTypeID */
enum ServiceFirmwareTypeID : uint8_t {
	FW_PM_MINI_BT  = 0x02,
	FW_PM_MINI_GPS = 0xF0,
	FW_VMON4000_GW = 0x06
};

/** ServiceRevisionTypeID */
enum ServiceRevisionTypeID : uint8_t {
	REVISION_NONE = 0,
	REVISION_A = 1,
	REVISION_B = 2,
	REVISION_C = 3,
	REVISION_D = 4,
	REVISION_E = 5,
	REVISION_F = 6,
	REVISION_G = 7
};

/** ServiceFirmwareEntryType */
enum ServiceFirmwareEntryType : unsigned {
	FW_MAIN_APPLICATION = 0,
	FW_MODULE_PACKAGE = 1,
	FW_EXTERNAL_TOOL = 2,
	FW_SUB_PCA = 3,
	FW_SUB_PCA_FW = 4
};


struct INERTIA_API ServiceResponseGetVersionExtended
{
	ServiceFirmwareEntryType entryType;
	uint8_t fwMajor;
	uint8_t fwMinor;
	uint8_t fwHotfix;
	std::wstring nameSuffix;
	std::wstring versionSuffix;
	std::wstring optionSuffix;

	ServiceResponseGetVersionExtended();
	const std::wstring ToString() const;
	const std::wstring GetEntryTypeString() const;
};


struct INERTIA_API ServiceResponseProcessStats
{
	std::wstring name;
	uint8_t state;
	uint16_t flags;
	uint8_t stack;

	ServiceResponseProcessStats();
	const std::wstring ToString() const;
};


struct INERTIA_API ServiceARPentry
{
	uint8_t interfaceID;
	uint8_t flags;
	uint16_t address;
	int8_t age;
	uint8_t linkType;
	uint8_t linkAddress;

	ServiceARPentry();
	const std::wstring ToString() const;
};


struct INERTIA_API ServiceForwarderEntry
{
	uint8_t interfaceID;
	uint8_t protocolID;
	int8_t minLevel;
	int8_t maxLevel;
	std::vector<std::pair<uint8_t, uint8_t> > destinations;

	ServiceForwarderEntry();
	const std::wstring ToString() const;
};


struct INERTIA_API ServiceConnectorEntry
{
	uint8_t protocolID;
	int8_t minLevel;
	int8_t maxLevel;

	ServiceConnectorEntry();
	const std::wstring ToString() const;
};


struct INERTIA_API ServiceMessage
{
private:
	ServiceGroup msgGroup;
	uint8_t msgType;

public:
	ServiceMessage(ServiceGroup const& group, uint8_t const& type);
	virtual ~ServiceMessage();
	ServiceMessage(ServiceMessage const&) = default;
	ServiceMessage& operator = (ServiceMessage const&) = default;

	static ServiceV0ID ServiceV1toV0(ServiceGroup, uint8_t msg);
	static const std::pair<ServiceGroup, uint8_t> ServiceV0toV1(ServiceV0ID msg);
	virtual const std::wstring ToString() const;

	virtual ServiceGroup GetGroup() const;
	virtual uint8_t GetType() const;
};


struct INERTIA_API ServiceResponse
	: ServiceMessage
{
	ServiceResponse(ServiceGroup const& group, uint8_t const& type);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseEcho
	: ServiceResponse
{
	ServiceVersion version;
	uint32_t mtuLength;

	ServiceResponseEcho(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseNetInfo
	: ServiceResponse
{
	uint16_t netPrefix;
	uint8_t netMask;

	ServiceResponseNetInfo(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseGetVersion
	: ServiceResponse
{
private:
	std::vector<uint8_t> rawData;

public:
	ServiceHardwareTypeID hardwareID;
	ServiceRevisionTypeID hardwareRevisionID;
	uint8_t hardwareOptionCode;
	uint32_t hardwarePCANumber;

	uint8_t firmwareID;
	uint8_t fwMajor;
	uint8_t fwMinor;
	uint8_t fwHotfix;
	std::vector<ServiceResponseGetVersionExtended> extendedInfo;

	ServiceResponseGetVersion();
	ServiceResponseGetVersion(const ServiceHardwareTypeID _hardwareID, const ServiceRevisionTypeID _revisionID);
	ServiceResponseGetVersion(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
	const std::wstring GetHardwareString() const;
	const std::wstring GetRevisionString() const;
	const std::wstring GetVersionString() const;
	static const std::wstring ToHardwareString(ServiceHardwareTypeID _hardwareID);

	std::vector<uint8_t> GetRawData() const;
};


struct INERTIA_API ServiceResponseGetSerialNr
	: ServiceResponse
{
private:
	std::vector<uint8_t> rawData;

public:
	uint64_t serialNr;

	ServiceResponseGetSerialNr();
	ServiceResponseGetSerialNr(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring GetHexString() const;
	const std::wstring ToString() const override;

	std::vector<uint8_t> GetRawData() const;
};


struct INERTIA_API ServiceResponseGetProcessStats
	: ServiceResponse
{
	std::vector<ServiceResponseProcessStats> processInfo;

	ServiceResponseGetProcessStats(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseListInterfaces
	: ServiceResponse
{
	std::vector<uint8_t> interfaceIDs;

	ServiceResponseListInterfaces(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseListARPs
	: ServiceResponse
{
	std::vector<ServiceARPentry> ARPentries;

	ServiceResponseListARPs(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseListForwarders
	: ServiceResponse
{
	std::vector<ServiceForwarderEntry> forwarderEntries;

	ServiceResponseListForwarders(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseListConnectors
	: ServiceResponse
{
	std::vector<ServiceConnectorEntry> connectorEntries;

	ServiceResponseListConnectors(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceResponseError
	: ServiceResponse
{
	uint8_t errorCode;

	ServiceResponseError(const uint8_t* in, const unsigned _offset, const unsigned _length);
	const std::wstring ToString() const override;
};


struct INERTIA_API ServiceRequest
	: ServiceMessage
{
	ServiceVersion version;
	std::vector<uint8_t> payload;

	ServiceRequest(ServiceGroup const& _msgGroup, uint8_t const& _msgType, ServiceVersion const& _version, std::vector<uint8_t> const& payload = std::vector<uint8_t>());
	const std::wstring ToString() const override;
};


struct INERTIA_API RRPResponse
	: ServiceResponse
{
	bool success;
	int responseCode;

	RRPResponse(const bool _success, int _responseCode = -1);
	const std::wstring ToString() const override;
};


/** RRPInfoType */
enum RRPInfoType : unsigned
{
	RRP_INFO_START = 0,
	RRP_INFO_BUSY,
	RRP_INFO_FWU_VERSION,
	RRP_INFO_FWU_FACTORY_DEFAULTS,
	RRP_INFO_DEVICE_HW,
	RRP_INFO_DEVICE_FW,
	RRP_INFO_DEVICE_REV,
	RRP_INFO_DEVICE_VERSION,
	RRP_INFO_FILE_HW,
	RRP_INFO_FILE_FW,
	RRP_INFO_FILE_REV,
	RRP_INFO_FILE_VERSION,
	RRP_INFO_COMPONENTS,
	RRP_INFO_VALIDATE,
	RRP_INFO_UPLOAD,
	RRP_INFO_REPROGRAM,
	RRP_INFO_TIMEOUT,
	RRP_INFO_RESET_DEFAULTS
};


struct INERTIA_API RRPProgress
	: ServiceResponse
{
	bool success;
	RRPInfoType type;
	std::wstring message;

	RRPProgress(const bool _success, const RRPInfoType type, std::wstring const& message);
	const std::wstring ToString() const override;
};

}
