/*
 * platformapi.h
 *
 *  Created on: 21 May 2020
 *      Author: Nick Pelling
 */

#ifndef PLATFORM_API_H_
#define PLATFORM_API_H_

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "default-param.h"

// Allow up to five plugins to be launched and run by the platform
#define PLUGINSLOT_1			0
#define PLUGINSLOT_2			1
#define PLUGINSLOT_3			2
#define PLUGINSLOT_4			3
#define PLUGINSLOT_5			4
#define PLUGINS_MAX				5

#ifndef NULL
#define NULL ((void *)0U)
#endif

#define CRLF	"\r\n"

#define NUM_ELEMENTS(N)							\
	(sizeof(N) / sizeof((N)[0]))

#define FIELD_OFFSET(STRUCTTYPE,STRUCTFIELD)	\
	((size_t)(&((STRUCTTYPE *)NULL)->STRUCTFIELD))

#define dMAKE_VERSION(major, minor, bugfix) (((major) << 16) | ((minor) << 8) | (bugfix))

/*
 * Magic number placed in plugin header
 */
#define PLUGIN_MAGIC_NUMBER		0x1234ABCDU

#define PLATFORM_GPIO1	0
#define PLATFORM_GPIO2	1
#define PLATFORM_GPIO3	2

// Convert a port/pin pair into a single unified pin index
#define PLATFORM_MAKE_PIN(PORT,PORTPIN)		((PORT) * 32 + (PORTPIN))

#define PLATFORM_GPIO_AD_B0(N)		PLATFORM_MAKE_PIN(PLATFORM_GPIO1,(N))
#define PLATFORM_GPIO_AD_B1(N)		PLATFORM_MAKE_PIN(PLATFORM_GPIO1,16+(N))
#define PLATFORM_GPIO_B0(N)			PLATFORM_MAKE_PIN(PLATFORM_GPIO2,(N))
#define PLATFORM_GPIO_B1(N)			PLATFORM_MAKE_PIN(PLATFORM_GPIO2,16+(N))
#define PLATFORM_GPIO_SD_B1(N)		PLATFORM_MAKE_PIN(PLATFORM_GPIO3,(N))
#define PLATFORM_GPIO_SD_B0(N)		PLATFORM_MAKE_PIN(PLATFORM_GPIO3,16+(N))

#define PLATFORM_SAMPLER_MAX_CHANNELS	8

// ---------------------------------------------------------------------------

/*
 * These mask values allow plugins to tell the platform what h/w is attached.
 */
typedef enum
{
	ePLATFORM_HasMask_IsValid			= 1ULL << 0,
	ePLATFORM_HasMask_SDRAM				= 1ULL << 1,
	ePLATFORM_HasMask_LPSPI3_ADS114S08	= 1ULL << 2,
	ePLATFORM_HasMask_LPSPI3_ADS8688	= 1ULL << 3,
	ePLATFORM_HasMask_WebServer			= 1ULL << 4,

	ePLATFORM_HasMask_NONE				= 0ULL
} ePLATFORM_HasMask_t;

// ---------------------------------------------------------------------------

/*
 * Note: values are the same one used for the ADS8688 range registers
 * These are listed on p.51 of the ADS8688 datasheet.
 */
typedef enum
{
	ePLATFORM_SamplerConfig_PlusMinus2pt5_VREF		= 0,	// ADS8688 => 0000
	ePLATFORM_SamplerConfig_PlusMinus1pt25_VREF		= 1,	// ADS8688 => 0001
	ePLATFORM_SamplerConfig_PlusMinus0pt625_VREF	= 2,	// ADS8688 => 0010
	ePLATFORM_SamplerConfig_ZeroTo2pt5_VREF			= 5,	// ADS8688 => 0101
	ePLATFORM_SamplerConfig_ZeroTo1pt25_VREF		= 6,	// ADS8688 => 0110

	ePLATFORM_SamplerConfig_NOCHANNEL				= 255
} ePLATFORM_SamplerConfig_t;

// ---------------------------------------------------------------------------
// Status returned when platform attempts to start plugin
// ---------------------------------------------------------------------------

typedef enum
{
	ePLUGIN_Status_OK						= 0,
	ePLUGIN_Status_CouldntStartTask_1		= 1,
	ePLUGIN_Status_CouldntStartTask_2		= 2,
	ePLUGIN_Status_CouldntStartTask_3		= 3,
	ePLUGIN_Status_CouldntStartTask_4		= 4,
	ePLUGIN_Status_CouldntStartTask_5		= 5,
	ePLUGIN_Status_CouldntStartTask_6		= 6,
	ePLUGIN_Status_CouldntStartTask_7		= 7,
	ePLUGIN_Status_CouldntStartTask_8		= 8,
	ePLUGIN_Status_UnrecognisedDevice		= 9,
	ePLUGIN_Status_NoMemoryOnHeap			= 10,
	ePLUGIN_Status_NoLaunchVector			= 11,

	ePLUGIN_Status_CoAPInitFailed			= 20,
	ePLUGIN_Status_CoAPResourceSetupFailed	= 21,

	ePLUGIN_Status_UnknownFailure			= 99
} ePLUGIN_Status_t;

// ---------------------------------------------------------------------------
// Each plugin can start up to 8 separate tasks using PLATFORM_TaskCreate()
// Each task has to be started in a unique task "slot".
// ---------------------------------------------------------------------------

typedef enum
{
	ePLATFORM_TaskSlot_1,	// 0
	ePLATFORM_TaskSlot_2,	// 1
	ePLATFORM_TaskSlot_3,	// 2
	ePLATFORM_TaskSlot_4,	// 3
	ePLATFORM_TaskSlot_5,	// 4
	ePLATFORM_TaskSlot_6,	// 5
	ePLATFORM_TaskSlot_7,	// 6
	ePLATFORM_TaskSlot_8,	// 7
	ePLATFORM_TaskSlot_MAX	// 8
} ePLATFORM_TaskSlot_t;

// ---------------------------------------------------------------------------
// Each plugin task gets given a task priority, as per the following enum:
// ---------------------------------------------------------------------------

typedef enum
{
	ePLATFORM_TaskPriority_Low,			// 0
	ePLATFORM_TaskPriority_Medium,		// 1
	ePLATFORM_TaskPriority_High,		// 2
	ePLATFORM_TaskPriority_RealTime,	// 3
	ePLATFORM_TaskPriority_MAX			// 4
} ePLATFORM_TaskPriority_t;

// ---------------------------------------------------------------------------
// The platform itself knows nothing about the contents of any CoAP packet.
// Hence when a plugin registers a data structure, it passes down:
//		the text string corresponding to that structure's CoAP name
//		the pointer to the data structure (in memory)
//		a pointer to a tree of CoAP nodes
// Each tree functions as a 'view' onto the data structure, & contains:
//		the name of the node
//		the offset of the field (relative to the start of the data structure)
//		the datatype of the field (each has its own accessor function prototype)
//		a read accessor function
//		a write accessor function
// ---------------------------------------------------------------------------

typedef enum
{
	eDataType_UnsignedInteger,
	eDataType_Integer,
	eDataType_Real,
	eDataType_String,
	eDataType_Boolean,
	eDataType_Enum,
	eDataType_Struct,

	eDataType_MAX
} eDataType_t;

// This enum is returned by the read/write accessor functions, to allow them
// to report back either a successful read/write (eAccess_OK) or a failure code.
typedef enum
{
	eAccess_OK,						// Access operation completed as per normal
	eAccess_NullPointerEncountered,	// The value of the DataType was not expected
	eAccess_ValueNotNumeric,		// Value not numeric
	eAccess_ValueNotAnInteger,		// Value not an integer
	eAccess_ValueNotFloatingPoint,	// Value not a floating point number
	eAccess_ValueTooHigh,			// Value not in valid range for type (too high)
	eAccess_ValueTooLow,			// Value not in valid range for type (too low)
	eAccess_ValueNotRecognised,		// Value not recognized (e.g. unknown enum)
	eAccess_StringTruncated,		// String too long to fit in field without truncation
	eAccess_StringTooShort,			// String is shorter than required by the pattern
	eAccess_StringTooLong,			// String is longer  than required by the pattern
	eAccess_StringBadPattern,		// String does not match a required pattern for this type
	eAccess_UnexpectedDataType,		// The value of the DataType was not expected
	eAccess_NestedArraysNotAllowed,	// Currently only single-dimensional arrays are handled
	eAccess_UnrecognizedFieldName,	// Field name was not recognised
	eAccess_UnspecifiedError,		// Unspecified error
	eAccess_ParsingError,			// Parsing error
	eAccess_OutOfMemory				// Memory allocation failure
} eAccess_t;

// GPIO pin direction
typedef enum
{
    ePLATFORM_DigitalInput,			// 0 = Pin is digital input
    ePLATFORM_DigitalOutput,		// 1 = Pin is digital output

    ePLATFORM_Digital_MAX			// 2
} ePLATFORM_pin_direction_t;

// GPIO port interrupt mode
typedef enum
{
    ePLATFORM_NoIntmode,				// 0 = Set pin to have general IO functionality.
    ePLATFORM_IntLowLevel,				// 1 = Set pin interrupt to be low-level sensitive.
    ePLATFORM_IntHighLevel,				// 2 = Set pin interrupt to be high-level sensitive.
    ePLATFORM_IntRisingEdge,			// 3 = Set pin interrupt to be rising-edge sensitive.
    ePLATFORM_IntFallingEdge,			// 4 = Set pin interrupt to be falling-edge sensitive.
    ePLATFORM_IntRisingOrFallingEdge,	// 5 = Enable edge select bit to override the ICR register's configuration.

	ePLATFORM_Int_MAX					// 6
} ePLATFORM_interrupt_mode_t;

/*
 * Block of channel samples captured at a given timestamp (e.g. from an ADS8688 on LPSPI3)
 */
typedef struct
{
	uint32_t uiTimestamp;				// copied from a processor profiling timestamp register
	uint16_t au16Samples[PLATFORM_SAMPLER_MAX_CHANNELS];
} stPLATFORM_SampleGrab_t;

// Forward reference (to allow Types to point to Fields containing Types)
struct tag_stPLUGIN_Field_t;

typedef struct
{
	uint64_t uiMin;					// minimum inclusive value
	uint64_t uiMax;					// maximum inclusive value
	const char * pachUnitName;		// META: unit description (e.g. "cm", "msec")
} stPLUGIN_RangeUint_t;

typedef struct
{
	int64_t iMin;						// minimum inclusive value
	int64_t iMax;						// maximum inclusive value
	const char * pachUnitName;		// META: unit description (e.g. "cm", "msec")
} stPLUGIN_RangeInt_t;

typedef struct
{
	double dMin;					// minimum inclusive value
	double dMax;					// maximum inclusive value
	const char * pachUnitName;		// META: unit description (e.g. "cm", "msec")
} stPLUGIN_RangeReal_t;

typedef struct
{
	const char * pachFalseString;	// META: name to override "False" with
	const char * pachTrueString;	// META: name to override "True"  with
} stPLUGIN_BooleanNames_t;

typedef struct
{
	const char * pachEnumName;
	int32_t iEnumValue;
} stPLUGIN_Dictionary_t;

#define DICTIONARY_TERMINATOR()	{ NULL, 0 }

typedef struct
{
	const char * pachTypeName;

	eDataType_t eDataType;

	uint32_t uiSizeof;

	union	// Read accessor function union
	{
		eAccess_t (*pfnRead_UnsignedInteger)(const void * pvField, uint64_t * piValue, const stPLUGIN_RangeUint_t * pstRange);
		eAccess_t (*pfnRead_Integer)(const void * pvField, int64_t * piValue, const stPLUGIN_RangeInt_t * pstRange);
		eAccess_t (*pfnRead_Real)(   const void * pvField, double * prValue, const stPLUGIN_RangeReal_t * pstRange);
		eAccess_t (*pfnRead_String)( const void * pvField, const char * * ppchResult);
		eAccess_t (*pfnRead_Enum)(   const void * pvField, const char * * ppchResult, const stPLUGIN_Dictionary_t * pastDictionary);
		eAccess_t (*pfnRead_Boolean)(const void * pvField, bool * bValue);
		const struct tag_stPLUGIN_Field_t * pastFieldPtr;	// if field is a struct, this points to its table of fields
	} uReadAccessor;

	union	// Write accessor function union
	{
		eAccess_t (*pfnWrite_UnsignedInteger)(void * pvField, uint64_t iValue, const stPLUGIN_RangeUint_t * pstRange);
		eAccess_t (*pfnWrite_Integer)(void * pvField, int64_t iValue, const stPLUGIN_RangeInt_t * pstRange);
		eAccess_t (*pfnWrite_Real)(   void * pvField, double rValue, const stPLUGIN_RangeReal_t * pstRange);
		eAccess_t (*pfnWrite_String)( void * pvField, const char * pachString, uint32_t uiSrcStringLen, uint32_t uiDstStringMax);
		eAccess_t (*pfnWrite_Enum)(   const void * pvField, const char * pchString, uint32_t uiStringLen, const stPLUGIN_Dictionary_t * pastDictionary);
		eAccess_t (*pfnWrite_Boolean)(void * pvField, bool bValue);
		uint32_t uiNumFields;		// if field is a struct, this says how many elements the table of fields holds
	} uWriteAccessor;

	union
	{
		const stPLUGIN_RangeUint_t * pstRange_UnsignedInteger;	// range (if type is an unsigned integer)
		const stPLUGIN_RangeInt_t  * pstRange_Integer;			// range (if type is an integer)
		const stPLUGIN_RangeReal_t * pstRange_Real; 			// range (if type is a real number)
		const stPLUGIN_BooleanNames_t * pstBooleanNames;		// boolean names (to override "True" / "False")
		const stPLUGIN_Dictionary_t * pastDictionary;			// dictionary (if type is an enum)
		uint32_t uiStringMax;										// length of string (if type is a string)
	} uHelper;
} stPLUGIN_Type_t;

// --------------------------------------------------------------------------

#define PLUGIN_FieldFlags_ReadBitPos	2
#define PLUGIN_FieldFlags_WriteBitPos	5
#define PLUGIN_FieldFlags_AccessBitMask	7

#define PLUGIN_FieldFlags_IsAnObject	(1U << 0)	// set if field has an object pointer
#define PLUGIN_FieldFlags_IsAnArray		(1U << 1)	// set if field is an array (i.e. not a single unit)
#define PLUGIN_FieldFlags_ReadFact		(1U << 2)	// set if field is readable in Factory mode
#define PLUGIN_FieldFlags_ReadComm		(1U << 3)	// set if field is readable in Commissioning mode
#define PLUGIN_FieldFlags_ReadLive		(1U << 4)	// set if field is readable in Live mode
#define PLUGIN_FieldFlags_WriteFact		(1U << 5)	// set if field is writable in Factory mode
#define PLUGIN_FieldFlags_WriteComm		(1U << 6)	// set if field is writable in Commissioning mode
#define PLUGIN_FieldFlags_WriteLive		(1U << 7)	// set if field is writable in Live mode
#define PLUGIN_FieldFlags_GetVisibility	(1U << 8)	// set if field's arraysize / visibility is dynamic
#define PLUGIN_FieldFlags_IsPersistent	(1U << 9)	// set if field is persistent
#define PLUGIN_FieldFlags_IsManaged		(1U << 10)	// set if field's persistency is managed by platform
#define PLUGIN_FieldFlags_IsLive		(1U << 11)	// set if field contains live data
#define PLUGIN_FieldFlags_NeedsReboot	(1U << 12)	// set if field only becomes active after a reboot
#define PLUGIN_FieldFlags_IsButton		(1U << 13)	// set if field is a one-shot that should be rendered as a button
#define PLUGIN_FieldFlags_RadioButtons	(1U << 14)	// set if field should be rendered as radio buttons
#define PLUGIN_FieldFlags_IsHex			(1U << 15)	// set if field number should be rendered as hex (e.g. MAC addresses)
#define PLUGIN_FieldFlags_IsPrivate		(1U << 16)	// set if field is private (e.g. username/password)


#define PLUGIN_FieldFlags_ReadAny		\
	(PLUGIN_FieldFlags_ReadFact | PLUGIN_FieldFlags_ReadComm | PLUGIN_FieldFlags_ReadLive)

#define PLUGIN_FieldFlags_WriteAny		\
	(PLUGIN_FieldFlags_WriteFact | PLUGIN_FieldFlags_WriteComm | PLUGIN_FieldFlags_WriteLive)

#define PLUGIN_FieldFlags_ReadWriteAny	\
	(PLUGIN_FieldFlags_ReadAny | PLUGIN_FieldFlags_WriteAny)

// Assumes that factory (eeprom) settings are persistent but not handled by the platform
#define PLUGIN_FieldFlags_Factory		\
	(		PLUGIN_FieldFlags_ReadAny | PLUGIN_FieldFlags_WriteFact		\
		|	PLUGIN_FieldFlags_IsPersistent )

#define PLUGIN_FieldFlags_Config		\
	(		PLUGIN_FieldFlags_ReadAny | PLUGIN_FieldFlags_WriteFact | PLUGIN_FieldFlags_WriteComm	\
		|	PLUGIN_FieldFlags_IsPersistent | PLUGIN_FieldFlags_IsManaged	)

#define PLUGIN_FieldFlags_Request		\
	(		PLUGIN_FieldFlags_ReadAny | PLUGIN_FieldFlags_WriteFact | PLUGIN_FieldFlags_WriteComm	\
		|	PLUGIN_FieldFlags_WriteLive	)

#define PLUGIN_FieldFlags_Status		\
	(PLUGIN_FieldFlags_ReadAny | PLUGIN_FieldFlags_IsLive)

typedef struct tag_stPLUGIN_Field_t
{
	const char * pachFieldName;
	union
	{
		size_t uiFieldOffset;
		void * pvObject;
	} uLocation;
	const stPLUGIN_Type_t * pstType;
	union
	{
		uint32_t uiNumArrayElements;
		bool (*pfn_DetermineUnitVisibility)(void *pvObject);
		bool (*pfn_DetermineArrayVisibility)(void *pvObject, uint32_t *puiNumArrayElements);
	} uVisibility;
	union
	{
		uint32_t uiEnum;				// Initial enumeration value (cast to uint32_t)
		uint32_t uiUnsignedInteger;
		uint32_t iInteger;
		float    fReal;
		char     * pachString;
		bool	 bBool;
	} uInit;
	uint32_t uiFieldFlags;				// As per the PLUGIN_FieldFlags defines above
} stPLUGIN_Field_t;

#define FIELD(NAME,LOCATION,TYPEPTR,VISIBILITY,INIT,FLAGS)	\
	{					\
		NAME,			\
		LOCATION,		\
		TYPEPTR,		\
		VISIBILITY,		\
		INIT,			\
		FLAGS			\
	}

#define FIELD__OFFSET(STRUCT,SUBFIELD)		{ .uiFieldOffset = FIELD_OFFSET(STRUCT,SUBFIELD) }
#define FIELD__OBJECT(OBJECTPTR)			{ .pvObject = OBJECTPTR }

#define FIELD__ARRAYSIZE(NUMELEMENTS)		{ .uiNumArrayElements = NUMELEMENTS }
#define FIELD__UNITVISIBILITY(CALLBACK)		{ .pfn_DetermineUnitVisibility = &CALLBACK }
#define FIELD__ARRAYVISIBILITY(CALLBACK)	{ .pfn_DetermineArrayVisibility = &CALLBACK }

#define INIT_ENUM(N)						{ .uiEnum = (uint32_t) N	}
#define INIT_UINT(N)						{ .uiUnsignedInteger = N	}
#define INIT_INT(N)							{ .iInteger = N				}
#define INIT_REAL(D)						{ .fReal = (float) D		}
#define INIT_STRING(S)						{ .pachString = S			}
#define INIT_BOOL(F)						{ .bBool = F				}
#define INIT_NOP							{ .iInteger = 0				}

// If invoked with an empty parameter, replace it with PLUGIN_FieldFlags_ReadWriteAny
#define DEFAULT_FLAG(...)	\
	DEFAULT_PARAMETER(PLUGIN_FieldFlags_ReadWriteAny, GET_ARG1(__VA_ARGS__))

#define FIELD_UNIT(NAME,STRUCT,SUBFIELD,TYPE,INIT,...)	\
	FIELD(NAME, FIELD__OFFSET(STRUCT,SUBFIELD), &stType_ ## TYPE, FIELD__ARRAYSIZE(1U), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__))

#define FIELD_UNIT_VISIBLE(NAME,STRUCT,SUBFIELD,TYPE,INIT,PFNVISIBLE,...)	\
	FIELD(NAME, FIELD__OFFSET(STRUCT,SUBFIELD), &stType_ ## TYPE, FIELD__UNITVISIBILITY(PFNVISIBLE), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_GetVisibility)

#define FIELD_ARRAY(NAME,STRUCT,SUBFIELD,TYPE,INIT,NUMELEMENTS,...)	\
	FIELD(NAME, FIELD__OFFSET(STRUCT,SUBFIELD), &stType_ ## TYPE, FIELD__ARRAYSIZE(NUMELEMENTS), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnArray)

#define FIELD_ARRAY_VISIBLE(NAME,STRUCT,SUBFIELD,TYPE,INIT,PFNVISIBLE,...)	\
	FIELD(NAME, FIELD__OFFSET(STRUCT,SUBFIELD), &stType_ ## TYPE, FIELD__ARRAYVISIBILITY(PFNVISIBLE), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnArray | PLUGIN_FieldFlags_GetVisibility)

#define FIELD_OBJECT(NAME,TYPEPTR,INIT,...)	\
	FIELD(NAME, FIELD__OBJECT(NULL), TYPEPTR, FIELD__ARRAYSIZE(1U), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnObject)

#define FIELD_OBJECT_VISIBLE(NAME,TYPEPTR,INIT,PFNVISIBLE,...)	\
	FIELD(NAME, FIELD__OBJECT(NULL), TYPEPTR, FIELD__UNITVISIBILITY(PFNVISIBLE), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnObject | PLUGIN_FieldFlags_GetVisibility)

#define FIELD_OBJECT_ARRAY(NAME,TYPEPTR,INIT,NUMELEMENTS,...)	\
	FIELD(NAME, FIELD__OBJECT(NULL), TYPEPTR, FIELD__ARRAYSIZE(NUMELEMENTS), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnObject | PLUGIN_FieldFlags_IsAnArray)

#define FIELD_OBJECT_ARRAY_VISIBLE(NAME,TYPEPTR,INIT,PFNVISIBLE,...)	\
	FIELD(NAME, FIELD__OBJECT(NULL), TYPEPTR, FIELD__ARRAYVISIBILITY(PFNVISIBLE), INIT_ ## INIT, \
			DEFAULT_FLAG(__VA_ARGS__) | PLUGIN_FieldFlags_IsAnObject | PLUGIN_FieldFlags_IsAnArray | PLUGIN_FieldFlags_GetVisibility)

#define FIELD_TERMINATOR()		\
	FIELD(NULL, FIELD__OBJECT(NULL), NULL, FIELD__ARRAYSIZE(0), INIT_NOP, 0U )

// --------------------------------------------------------------------------

#define DEFAULT_STRUCTSIZEOF(...)	\
	DEFAULT_PARAMETER(PLUGIN_FieldFlags_ReadWriteAny, GET_ARG1(__VA_ARGS__))

#define PLUGIN_STRUCT(TYPENAME,...)		\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_Struct,			\
		DEFAULT_STRUCTSIZEOF(sizeof(TYPENAME), __VA_ARGS__),	\
		{ .pastFieldPtr = astFields_ ## TYPENAME },		\
		{ NULL },					\
		{ NULL }					\
	}

#define PLUGIN_UNSIGNEDINTEGER(TYPENAME,READFUNC,WRITEFUNC,RANGEPTR)	\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_UnsignedInteger,	\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_UnsignedInteger = &READFUNC },		\
		{ .pfnWrite_UnsignedInteger = &WRITEFUNC },		\
		{ .pstRange_UnsignedInteger = RANGEPTR }		\
	}

#define PLUGIN_INTEGER(TYPENAME,READFUNC,WRITEFUNC,RANGEPTR)	\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_Integer,			\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_Integer = &READFUNC },		\
		{ .pfnWrite_Integer = &WRITEFUNC },		\
		{ .pstRange_Integer = RANGEPTR }		\
	}

#define PLUGIN_REAL(TYPENAME,READFUNC,WRITEFUNC,RANGEPTR)	\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_Real,				\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_Real = &READFUNC },		\
		{ .pfnWrite_Real = &WRITEFUNC },		\
		{ .pstRange_Real = RANGEPTR }		\
	}

#define PLUGIN_BOOLEAN(TYPENAME,READFUNC,WRITEFUNC,OVERRIDE)	\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_Boolean,			\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_Boolean = &READFUNC },	\
		{ .pfnWrite_Boolean = &WRITEFUNC },	\
		{ .pstBooleanNames = OVERRIDE }		\
	}

#define PLUGIN_STRING(TYPENAME,READFUNC,WRITEFUNC,MAXLEN)		\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_String,			\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_String = &READFUNC },	\
		{ .pfnWrite_String = &WRITEFUNC },	\
		{ .uiStringMax = MAXLEN }		\
	}

#define PLUGIN_ENUM(TYPENAME,READFUNC,WRITEFUNC,DICTIONARYPTR)			\
	static const stPLUGIN_Type_t stType_ ## TYPENAME =	\
	{								\
		#TYPENAME,					\
		eDataType_Enum,				\
		(uint32_t)sizeof(TYPENAME),	\
		{ .pfnRead_Enum = READFUNC },		\
		{ .pfnWrite_Enum = WRITEFUNC },		\
		{ .pastDictionary = DICTIONARYPTR }	\
	}

// ---------------------------------------------------------------------------

/*! @brief GPIO Init structure definition. */
typedef struct
{
    ePLATFORM_pin_direction_t direction; /*!< Specifies the pin direction. */
    uint8_t outputLogic;            /*!< Set a default output logic, which has no use in input */
    ePLATFORM_interrupt_mode_t interruptMode; /*!< Specifies the pin interrupt mode, a value of @ref gpio_interrupt_mode_t. */
} stPLATFORM_pin_config_t;

typedef struct
{
	uint32_t uiNanosecs;		// 32-bit nanosecond value
	int64_t  uiSecs;			// 64-bit to prevent Unix 2038 time overflow!
} stPLATFORM_Timestamp_t;

struct stPLATFORM_ObjectData_s;

typedef bool (*pfn_CoAPObjectEdited_t)( const struct stPLATFORM_ObjectData_s * pstObjectData );

typedef struct stPLATFORM_ObjectData_s
{
	const char * pachResourceStr;			// Name of this entity
	const char * pachResourceType;			// Resource Type string
	const char * pachInterfaceDescription;	// Interface Description string
	void * pvObject;						// The object itself
	const stPLUGIN_Type_t * pstType;		// The type of the object
	uint32_t uiFieldFlags;					// The default field permissions
	void * pvCoAPResource;					// blind pointer to CoAP resource
	pfn_CoAPObjectEdited_t pfn_CoAPObjectEdited; // Func called when object is edited over CoAP
	uint32_t uiObjectOwnerIndex;			// 0 => platform, 1 => plugin slot #1, etc
	int iMaxAge;							// -1 = forever, etc
	uint16_t ui16MediaType;					// 50 = JSON, etc
} stPLATFORM_ObjectData_t;

#define OBJECTOWNERINDEX_PLATFORM		0U
#define OBJECTOWNERINDEX_PLUGIN(N)		(1U + (N))

#define PLATFORM_MEDIATYPE_JSON			50	// Note: same as CoAP content type for JSON
#define PLATFORM_MEDIATYPE_CBOR			60	// Note: same as CoAP content type for CBOR

// ---------------------------------------------------------------------------

typedef enum
{
	eWebFlags_HTML			= 1 << 0,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n"
	eWebFlags_JavaScript	= 1 << 1,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: text/javascript\r\n\r\n"
	eWebFlags_CSS			= 1 << 2,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: text/css\r\n\r\n"
	eWebFlags_GIF			= 1 << 3,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: image/gif\r\n\r\n"
	eWebFlags_JPG			= 1 << 4,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: image/jpeg\r\n\r\n"
	eWebFlags_PNG			= 1 << 5,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: image/png\r\n\r\n"
	eWebFlags_WEBP			= 1 << 6,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: image/webp\r\n\r\n"
	eWebFlags_PDF			= 1 << 7,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: application/pdf\r\n\r\n"
	eWebFlags_JSON			= 1 << 8,		// e.g. "HTTP/1.1 200 OK\r\nContent-type: application/json\r\n\r\n"

	eWebFlags_KeepAlive		= 1 << 16

} eWebFlags_t;

typedef const char *(*pfnCgiCallback_t)(int iIndex, int iNumParams, const char *pcParam[], const char *pcValue[]);

typedef struct
{
	// PLATFORM/PLUGIN API V0.1 *** START ***
	// Do not add, edit or remove any functions from this section!

	// Platform: Task functions
	bool     (*pfn_TaskCreate)(
					uint32_t uiPluginIndex,					// Which plugin is calling this
					ePLATFORM_TaskSlot_t eTaskSlot,			// Which platform task slot to use
					void (*pvTaskFunc)(void *),				// Pointer to task code
					const char * const pcTaskName,			// Task name
					void * pvParams,						// Parameter to pass to created task
					uint32_t uiStackSizeInBytes,			// Task stack size
					ePLATFORM_TaskPriority_t ePriority );	// Task stack priority
	void	  (*pfn_TaskSleep)( uint32_t uiMsec );
	volatile uint32_t (*pfn_TaskGetTickCount)( void );		// Return 1ms tick count

	// Platform: GPIO functions
	void     (*pfn_GPIO_PinInit)( uint32_t uiPin, const stPLATFORM_pin_config_t *pstConfig );
	uint8_t  (*pfn_GPIO_PinReadPadStatus)( uint32_t uiPin );
	void     (*pfn_GPIO_PinSetInterruptConfig)( uint32_t uiPin, ePLATFORM_interrupt_mode_t pinInterruptMode );
	uint32_t (*pfn_GPIO_PinRead)( uint32_t uiPin );
	uint32_t (*pfn_GPIO_PinsRead)( uint32_t uiPin, uint32_t uiNumPins );
	void	 (*pfn_GPIO_PinWrite)( uint32_t uiPin, uint32_t uiOutput );
	void	 (*pfn_GPIO_PinsWrite)( uint32_t uiPin, uint32_t uiNumPins, uint32_t uiOutput );

	// Platform: CoAP functions
	bool	  (*pfn_CoAPCreateResource)(	 stPLATFORM_ObjectData_t * pstObjectData);
	void	  (*pfn_CoAPObjectUpdated)(const stPLATFORM_ObjectData_t * pstObjectData);

	// Platform: Other functions
	void *	  (*pfn_Malloc)( uint32_t uiSize );
	void	  (*pfn_Free)( void * pvPtr );
	bool	  (*pfn_GetTimestamp)( stPLATFORM_Timestamp_t * pstTimestamp );
	int		  (*pfn_Printf)( const char *format, ... );
	int		  (*pfn_SafeSNPrintf)( char * pchBuffer, uint32_t uiBufferSize, const char *format, ... );
	void	  (*pfn_Assert)(const char * pachBuffer, const char * pachFilename, int iLineNumber );

	// Platform: Sampling services
	bool	 (*pfn_Sampler_Setup)(	const ePLATFORM_SamplerConfig_t *paeSamplerConfig,
									uint32_t uiNumChannels,
									volatile stPLATFORM_SampleGrab_t * pastCircularBuffer,
									uint32_t uiBufferElements );
	bool	 (*pfn_Sampler_Start)(uint32_t uiPeriod_Nsec);
	bool	 (*pfn_Sampler_Adjust)(uint32_t uiDelay_Nsec, uint32_t uiPeriod_Nsec);
	bool	 (*pfn_Sampler_Stop)(void);
	uint32_t (*pfn_Sampler_GetTimer)(void);
	uint32_t (*pfn_Sampler_Grab)(uint32_t uiEarliestTimer, volatile stPLATFORM_SampleGrab_t ** papstGrabArray, uint32_t uiMaxBlocksToGrab);

	// Platform: more basic functions
	void *	 (*pfn_MemCpy) ( void * destination, const void * source, uint32_t num );
	void *	 (*pfn_MemCmp) ( const void * ptr1, const void * ptr2, uint32_t num );
	void * 	 (*pfn_MemSet) ( void * ptr, int value, uint32_t num );
	char *	 (*pfn_StrCpy) ( char * destination, const char * source );
	int 	 (*pfn_StrCmp) ( const char * str1, const char * str2 );
	int 	 (*pfn_StrLen) ( const char * str );

	// Platform: webserver functions
	bool	 (*pfn_FileAdd)( const char * pachFileName, eWebFlags_t eWebFlags, const char * pau8Filedata, uint32_t uiFilesize );
	bool	 (*pfn_CgiAdd) ( const char * pachCgiName,  pfnCgiCallback_t pfnCgiCallback );

	// Platform: Discovery service
	bool	 (*pfn_DNS_SD_Discovery)(void (*pfnResponseCallback)(const char *pachResponse, uint32_t uiResponseLen));

	// Do not add, edit or remove any functions from this section!
	// PLATFORM/PLUGIN API V0.1 *** END ***

} stPlatformCalls_t;

// Note that the following helper macros USE BUT DO NOT DEFINE the DO_CALL() macro.
// The DO_CALL() macro is left to be defined inside the plugin code itself.

	// Platform: Task functions
#define PLATFORM_TaskCreate(PLUGININDEX,TASKSLOT,TASKFUNC,TASKNAME,PARAMPTR,STACKSIZE,PRIORITY)	\
	DO_CALL(pfn_TaskCreate)(PLUGININDEX,TASKSLOT,TASKFUNC,TASKNAME,PARAMPTR,STACKSIZE,PRIORITY)
#define PLATFORM_TaskSleep(MSEC)						DO_CALL(pfn_TaskSleep)(MSEC)
#define PLATFORM_TaskGetTickCount()						DO_CALL(pfn_TaskGetTickCount)()

	// Platform: GPIO functions
#define PLATFORM_GPIO_PinInit(PIN,CONFIG)				DO_CALL(pfn_GPIO_PinInit)(PIN,CONFIG)
#define PLATFORM_GPIO_PinReadPadStatus(PIN)				DO_CALL(pfn_GPIO_PinReadPadStatus)(PIN)
#define PLATFORM_GPIO_PinSetInterruptConfig(PIN,MODE)   DO_CALL(pfn_GPIO_PinSetInterruptConfig)(PIN,MODE)
#define PLATFORM_GPIO_PinRead(PIN)                      DO_CALL(pfn_GPIO_PinRead)(PIN)
#define PLATFORM_GPIO_PinsRead(PIN,NUMPINS)             DO_CALL(pfn_GPIO_PinsRead)(PIN,NUMPINS)
#define PLATFORM_GPIO_PinWrite(PIN,OUTPUT)              DO_CALL(pfn_GPIO_PinWrite)(PIN,OUTPUT)
#define PLATFORM_GPIO_PinsWrite(PIN,NUMPINS,OUTPUT)     DO_CALL(pfn_GPIO_PinsWrite)(PIN,NUMPINS,OUTPUT)

	// Platform: CoAP functions
#define PLATFORM_CoAPCreateResource(OBJECTDATA)			DO_CALL(pfn_CoAPCreateResource)(OBJECTDATA)
#define PLATFORM_CoAPObjectUpdated(OBJECTDATA)			DO_CALL(pfn_CoAPObjectUpdated)(OBJECTDATA)

	// Platform: Test API
#define PLATFORM_TestHardware(HASMASK)					DO_CALL(pfn_TestHardware)(HASMASK)

	// Platform: Other functions
#define PLATFORM_Malloc(SIZE)                           DO_CALL(pfn_Malloc)(SIZE)
#define PLATFORM_Free(PTR)                              DO_CALL(pfn_Free)(PTR)
#define PLATFORM_GetTimestamp(PTR)						DO_CALL(pfn_GetTimestamp)(PTR)
#define PLATFORM_Printf(FORMAT,...)        				DO_CALL(pfn_Printf)(FORMAT,__VA_ARGS__)
#define PLATFORM_SafeSNPrintf(BUFFPTR,BUFFSIZE,FORMAT,...)        	\
	DO_CALL(pfn_SafeSNPrintf)(BUFFPTR,BUFFSIZE,FORMAT,__VA_ARGS__)
#define PLATFORM_Assert(FAILURETEXT,FILENAME,LINE)		DO_CALL(pfn_Assert)(FAILURETEXT,FILENAME,LINE)

// Platform: Sampling service
#define PLATFORM_Sampler_Setup(CONFIGARRAY,NUMCHANNELS,BUFFERBASE,BUFFERELEMENTS)	\
	DO_CALL(pfn_Sampler_Setup)(CONFIGARRAY,NUMCHANNELS,BUFFERBASE,BUFFERELEMENTS)
#define PLATFORM_Sampler_Start(PERIOD)	\
	DO_CALL(pfn_Sampler_Start)(PERIOD)
#define PLATFORM_Sampler_Adjust(DELAY,NEWPERIOD)	\
	DO_CALL(pfn_Sampler_Adjust)(DELAY,NEWPERIOD)
#define PLATFORM_Sampler_Stop()	\
	DO_CALL(pfn_Sampler_Stop)()
#define PLATFORM_Sampler_GetTimer() \
	DO_CALL(pfn_Sampler_GetTimer)()
#define PLATFORM_Sampler_Grab(TIMESTAMP,GRABARRAY,GRABARRAYNUM)	\
	DO_CALL(pfn_Sampler_Grab)(TIMESTAMP,GRABARRAY,GRABARRAYNUM)

#define PLATFORM_MemCpy(DST,SRC,NUM)	\
	DO_CALL(pfn_MemCpy)(DST,SRC,NUM)
#define PLATFORM_MemCmp(PTR1,PTR2,NUM)	\
	DO_CALL(pfn_MemCmp)(PTR1,PTR2,NUM)
#define PLATFORM_MemSet(PTR,VALUE,NUM)	\
	DO_CALL(pfn_MemSet)(PTR,VALUE,NUM)
#define PLATFORM_StrCpy(DST,SRC)		\
	DO_CALL(pfn_StrCpy)(DST,SRC)
#define PLATFORM_StrCmp(STR1,STR2)		\
	DO_CALL(pfn_StrCmp)(STR1,STR2)
#define PLATFORM_StrLen(STR)			\
	DO_CALL(pfn_StrLen)(STR)

#define PLATFORM_FileAdd(FILENAME,WEBFLAGS,FILEDATA,FILESIZE)	\
	DO_CALL(pfn_FileAdd)(FILENAME,WEBFLAGS,FILEDATA,FILESIZE)
#define PLATFORM_CgiAdd(FILENAME,CALLBACK)				\
	DO_CALL(pfn_CgiAdd)(FILENAME,CALLBACK)

#define PLATFORM_DNS_SD_Discovery(CALLBACK)		\
	DO_CALL(pfn_DNS_SD_Discovery)(CALLBACK)

	// Special printf wrapper for messages that you only want emitted in debug builds
#ifdef DEBUG
#define PLATFORM_PRINTF(FORMAT,...)        				PLATFORM_Printf(FORMAT,__VA_ARGS__)
#else
#define PLATFORM_PRINTF(FORMAT,...)						((void)FORMAT)
#endif

	// Special debug assert wrapper for platform assert function
#ifdef DEBUG
#define PLATFORM_ASSERT(X)	do { if (!(X)) { PLATFORM_Assert(#X,__FILE__,__LINE__); } } while (0)
#else
#define PLATFORM_ASSERT(X)	do { } while (0)
#endif

typedef struct
{
	// PLATFORM/PLUGIN API V0.1 *** START ***
	// Do not add, edit or remove any functions from this section!

	ePLUGIN_Status_t (*pfn_Startup)(	uint32_t uiPluginIndex,		// which plugin slot is this loaded into
										uint32_t uiDeviceIndex );	// what device is this loaded on?

	// Do not add, edit or remove any functions from this section!
	// PLATFORM/PLUGIN API V0.1 *** END ***

} stPluginCalls_t;

/*
 * Describes a set of plugin calls plus the size of the structure
 * This is needed so that different versions of the API can be
 * in use in the platform and test plugins etc.
 */
typedef struct
{
	const stPluginCalls_t *	pstPluginCalls;
	uint32_t		  	uiPluginCallsSize_Bytes;
} stPluginCallBlock_t;

/*
 * Describes a set of platform calls plus the size of the structure
 * This is needed so that different versions of the API can be
 * in use in the platform and test plugins etc.
 */
typedef struct
{
	stPlatformCalls_t *	pstPlatformCalls;
	uint32_t		  	uiPlatformCallsSize_Bytes;
} stPlatformCallBlock_t;

/**
 * Callback function passed down at early startup to register a mock (test) API
 * for a given target plugin slot.
 * Note that a test plugin might target multiple slots (e.g. serial plugin + protocol plugin)
 *
 * @param[in] uiTargetPluginIndex - slot index of the target plugin
 * @param[in] pstMockAPI - mock API that the test plugin wants the target plugin to use
 * @param[in] ppstTargetPluginCallBlock - where to (eventually) write the target plugin's call block
 * @retval false Problem encountered with request
 * @retval true  Request registered (but not necessarily acted upon, that happens later)
 */
typedef bool (*pfnTestAPI_t)(	uint32_t uiTargetPluginIndex,
								const stPlatformCallBlock_t * pstMockAPI,
								stPluginCallBlock_t **ppstTargetPluginCallBlock );

typedef struct
{
	// PLATFORM/PLUGIN API V0.1 *** START ***
	// Do not add, edit or remove any functions from this section!

	uint32_t			uiBinaryLength;		// Length of the .bin image
	uint32_t			uiMagicNumber;		// Set this to PLUGIN_MAGIC_NUMBER

	ePLATFORM_HasMask_t	(*pfnDoStartup)( uint32_t uiDeviceIndex, pfnTestAPI_t pfnSetupMock );
	const char *		pachPluginName;

	uint32_t			uiPluginVersion;
	uint32_t			uiRequiredPlatformVersion;

	uint32_t			uiSigningValue;		// I'm not yet sure how this will work :-(
	uint32_t			uiChecksumValue;	// THIS MUST BE AT OFFSET +0x1C FOR IT TO WORK!!!!!!!

	stPluginCallBlock_t		stPluginCallBlock;

	stPlatformCallBlock_t	stPlatformCallBlock;

	uint32_t			uiPluginSlotIndex;	// Plugin slot index  this was compiled for
	void *				pvFlashBase;		// Flash base address this was compiled for
	void *				pvRamBase;			// Ram base address   this was compiled for

	// Do not add, edit or remove any functions from this section!
	// PLATFORM/PLUGIN API V0.1 *** END ***

} stPluginHeader_t;

#endif /* PLATFORM_API_H_ */
