#pragma once

#include "data/config/SensorSpecificSettings.h"

namespace inertia
{

struct IOCommonDescriptor;
struct IOModeDescriptor;
struct IOConfigurationDescriptor;
struct IOCapabilityDescriptor;
typedef std::shared_ptr<IOCommonDescriptor> IOCommonDescriptorPtr; ///< Pointer to an IOCommonDescriptor.
typedef std::shared_ptr<IOModeDescriptor> IOModeDescriptorPtr; ///< Pointer to an IOModeDescriptor.
typedef std::shared_ptr<IOConfigurationDescriptor> IOConfigurationDescriptorPtr; ///< Pointer to an IOConfigurationDescriptor.
typedef std::shared_ptr<IOCapabilityDescriptor> IOCapabilityDescriptorPtr; ///< Pointer to an IOCapabilityDescriptor.


/** IOModeID */
enum IOModeID {
	IO_MODE_ADC = 0,
	IO_MODE_DAC = 1,
	IO_MODE_SYNC = 2,
	IO_MODE_TRIGGER_INPUT = 3,
	IO_MODE_TRIGGER_OUTPUT = 4,
	IO_MODE_INPUT = 5,
	IO_MODE_OUTPUT = 6,
	IO_MODE_LAST = 7
};

/** IOMessageID */
enum IOMessageID {
	IO_MESSAGE_TRIGGER_INPUT_EVENT = 1,
	IO_MESSAGE_TRIGGER_OUTPUT_FORCE = 0,
	IO_MESSAGE_TRIGGER_OUTPUT_EVENT = 1,
	IO_MESSAGE_INPUT_READ = 0,
	IO_MESSAGE_OUTPUT_WRITE = 0,
	IO_MESSAGE_LAST = 255
};

/** IOCommonModeID */
enum IOCommonModeID {
	IO_COMMON_MODE_SYNC = 0,
	IO_COMMON_MODE_LAST = 1
};

/** IOCommonMessageID */
enum IOCommonMessageID {
	IO_COMMON_MESSAGE_LAST = 255
};

/** IOModeTriggerOutputType */
enum IOModeTriggerOutputType {
	IO_MODE_TRIGGER_OUTPUT_NONE = -1,
	IO_MODE_TRIGGER_OUTPUT_PULSE = 0,
	IO_MODE_TRIGGER_OUTPUT_LEVEL = 1,
	IO_MODE_TRIGGRE_OUTPUT_TOGGLE = 2
};

/** IOModeTriggerInputType */
enum IOModeTriggerInputType {
	IO_MODE_TRIGGER_INPUT_NONE = -1,
	IO_MODE_TRIGGER_INPUT_EDGE = 0,
	IO_MODE_TRIGGER_INPUT_LEVEL = 1
};

/** IOConfigurationDescriptorType */
enum IOConfigurationDescriptorType {
	IO_CONFIG_DESCRIPTOR_SPECIFIC = 0,
	IO_CONFIG_DESCRIPTOR_COMMON = 1,
	IO_CONFIG_DESCRIPTOR_LAST = 2
};


struct INERTIA_API IOCommonDescriptor
{
private:
	IOCommonModeID commonID;
	unsigned descrLength; // not part of specification

	void SetDescrLength(IOCommonModeID _modeID);

public:
	IOCommonDescriptor(IOCommonModeID _commonID);
	IOCommonDescriptor(uint8_t const* in, const unsigned _ioLength);
	IOCommonDescriptor(std::wstring const& argString);

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

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

	IOCommonModeID GetCommonModeID() const;
	unsigned GetDescrLength() const;
};


struct INERTIA_API IOCommonDescriptorAdvGWSync
	: IOCommonDescriptor
{
	uint8_t commonSyncRevision;
	uint8_t commonSyncReserved;

	//bitfield options
	bool select6v6Over5v;

	IOCommonDescriptorAdvGWSync();
	IOCommonDescriptorAdvGWSync(uint8_t const* in, const unsigned _descrLength);
	IOCommonDescriptorAdvGWSync(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptor
{
private:
	IOModeID modeID;
	unsigned descrLength; // not part of specification

	void SetDescrLength(IOModeID _modeID);

public:
	IOModeDescriptor(IOModeID _modeID);
	IOModeDescriptor(uint8_t const* in, const unsigned _ioLength);
	IOModeDescriptor(std::wstring const& argString);

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

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

	IOModeID GetModeID() const;
	unsigned GetDescrLength() const;
};


struct INERTIA_API IOModeDescriptorADC
	: IOModeDescriptor
{
	IOModeDescriptorADC();
	IOModeDescriptorADC(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorADC(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorDAC
	: IOModeDescriptor
{
	IOModeDescriptorDAC();
	IOModeDescriptorDAC(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorDAC(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorSync
	: IOModeDescriptor
{
	float syncFreq;
	float phaseShift;

	IOModeDescriptorSync();
	IOModeDescriptorSync(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorSync(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorTriggerInput
	: IOModeDescriptor
{
	IOModeTriggerInputType triggerType;
	float holdOffTime;

	// bitfield options
	bool startLogging;
	bool stopLogging;
	bool resetFrameCounter;
	bool sendTimestampMessage;

	IOModeDescriptorTriggerInput();
	IOModeDescriptorTriggerInput(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorTriggerInput(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorTriggerOutput
	: IOModeDescriptor
{
	IOModeTriggerOutputType triggerType;
	float pulseDuration;

	// bitfield options
	bool startLogging;
	bool stopLogging;
	bool resetFrameCounter;

	bool levelResetStopLogging;
	bool levelResetGPIOReset;

	IOModeDescriptorTriggerOutput();
	IOModeDescriptorTriggerOutput(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorTriggerOutput(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorInput
	: IOModeDescriptor
{
	int16_t lowLevelThreshold;
	int16_t highLevelThreshold;

	// bitfield options
	bool invertPolarity;
	bool setLowLevelThreshold;
	bool setHighLevelThreshold;
	bool enable50OhmImp;

	IOModeDescriptorInput();
	IOModeDescriptorInput(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorInput(std::wstring const& argString);

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

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


struct INERTIA_API IOModeDescriptorOutput
	: IOModeDescriptor
{
	int16_t lowLevelVoltage;
	int16_t highlevelVoltage;

	// bitfield options
	bool invertPolarity;
	bool setLowLevelVoltage;
	bool setHighLevelVoltage;
	bool enable50OhmImp;

	IOModeDescriptorOutput();
	IOModeDescriptorOutput(uint8_t const* in, const unsigned _descrLength);
	IOModeDescriptorOutput(std::wstring const& argString);

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

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


struct INERTIA_API IOConfigurationDescriptor
{
private:
	IOConfigurationDescriptorType confDescriptorType;
	uint8_t ioLength;

public:
	IOConfigurationDescriptor(IOConfigurationDescriptorType _confDescriptorType);
	IOConfigurationDescriptor(uint8_t const* in, const unsigned _ioLength);
	IOConfigurationDescriptor(std::wstring const& argString);

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

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

	IOConfigurationDescriptorType GetConfDescriptorType() const;
	uint8_t GetIOLength() const;
};


struct INERTIA_API IOConfigurationDescriptorCommon
	: IOConfigurationDescriptor
{
	IOCommonDescriptorPtr ioCommonDescriptor;

	IOConfigurationDescriptorCommon();
	IOConfigurationDescriptorCommon(uint8_t const* in, const unsigned _ioLength);
	IOConfigurationDescriptorCommon(std::wstring const& argString);

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

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


struct INERTIA_API IOConfigurationDescriptorSpecific
	: IOConfigurationDescriptor
{
	uint8_t ioID;
	std::vector<IOModeDescriptorPtr> ioModeDescriptors;

	IOConfigurationDescriptorSpecific(uint8_t _ioID);
	IOConfigurationDescriptorSpecific(uint8_t const* in, const unsigned _ioLength);
	IOConfigurationDescriptorSpecific(std::wstring const& argString);

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

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


struct INERTIA_API IOCapabilityDescriptor
{
	uint8_t ioLength;
	uint8_t ioID;

	// bitfield options
	bool capADC;
	bool capDAC;
	bool capOutput10v;
	bool capOutput6v6;
	bool capOutput5v;
	bool capOutput5v50imp;
	bool capOutput3v3;
	bool capOutput3v350imp;
	bool capOutputNeg5v50imp;
	bool capOutputNeg10v;
	bool capOutputPolarityInverse;

	bool capInputLowThreshold;
	bool capInputHighThreshold;
	bool capInputPolarityInverse;
	bool capInput50imp;
	bool capSyncInput;
	bool capSyncOutput;

	bool capTriggerInputStartLog;
	bool capTriggerInputStopLog;
	bool capTriggerInputResetFrameCounter;
	bool capTriggerInputSendTimestamp;
	bool capTriggerInputEdge;
	bool capTriggerInputLevel;
	bool capTriggerInputHoldOff;
	bool capTriggerOutputPulse;
	bool capTriggerOutputLevel;
	bool capTriggerOutputToggle;
	bool capTriggerOutputStartLog;
	bool capTriggerOutputStopLog;
	bool capTriggerOutputResetFrameCounter;

	IOCapabilityDescriptor();
	IOCapabilityDescriptor(uint8_t const* in, const unsigned _ioLength);
	IOCapabilityDescriptor(std::wstring const& argString);

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

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


struct INERTIA_API IOConfiguration
	: SpecificSensorSettings
{
	std::vector<IOConfigurationDescriptorPtr> ioConfigs;

	IOConfiguration();
	IOConfiguration(uint8_t const* in, const unsigned offset, const unsigned _length);
	IOConfiguration(std::wstring const& argString);

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

	void GetBytes(std::vector<uint8_t>& out, const size_t bitfieldOffset) const override;
	const std::wstring ToString() const override;
	const std::wstring ToSimpleString() const;
};


struct INERTIA_API IOCapability
	: SpecificSensorSettings
{
	std::vector<IOCapabilityDescriptorPtr> ioCapabilities;

	IOCapability();
	IOCapability(uint8_t const* in, const unsigned offset, const unsigned _length);
	IOCapability(std::wstring const& argString);

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

	void GetBytes(std::vector<uint8_t>& out, const size_t bitfieldOffset) const override;
	const std::wstring ToString() const override;
	const std::wstring ToSimpleString() const;
};

}
