// This interface should normally be used to access properties, rather than 
// accessing the class memebers directly.  
// The only time the members should be accessed directly is when initialising the property.

// #pragma once doesn't work if PropertiesInterface is in both the \h and the local directory
#ifndef PROPERTIES_INTERFACE_H
#define PROPERTIES_INTERFACE_H

#include <typeinfo.h>

#include <vector>

#ifdef SCO_EXPORTS
	#define SCO_API __declspec(dllexport)
#else
	#define SCO_API __declspec(dllimport)
#endif
 
#ifdef STDCALL
	#define CALL_CONV __stdcall
#else
	#define CALL_CONV
#endif



namespace NDTS
{
namespace SCO
{
	class Property;

	enum ChangeTypeFlags
	{
		CTValue = 1,
		CTBounds = 2,  // The bounds of the property is the min or max 
		CTPropertyFlags = 4, 
	} ;


	struct PropertyChangeItem
	{
		Property *pPropertyHandle;
		ChangeTypeFlags changeTypeFlags;
	} ;

	class Property
	{
	public:
		// All property and enum names are statically allocated in the SDK specific DLL.
		virtual const char *getShortName()=0;
		virtual const char *getLongName()=0;
		virtual int getFlags()=0;

		// The back pointer allows an application to store a handle or 
		// pointer to its own setting object so that it is easier to locate
		// the setting following a change event.
		virtual void setBackPointer(void *pBackPointer)=0;
		virtual void *getBackPointer()=0;
	};

	class IntProperty : public virtual Property
	{
	public:
		virtual int getValue()=0;
		virtual void setValue(int value)=0;
		virtual int getMin()=0;
		virtual int getMax()=0;
	};


	class EnumProperty : public virtual IntProperty
	{
	public:
		virtual const char* getEnumStringByIndex(int index)=0;
		virtual int getNumberOfEnumStrings()=0;

	};

	namespace SI
	{
		// Not a complete list of SI units but rather units that are useful for the SDK

		enum BaseUnit
		{
			Metres, 
			Seconds, 		// Hertz are treated as inverse Seconds internally but it is useful for applications to have both Hertz and Seconds 
			Hertz,
			dB,				// dB should strictly speaking be in Bels but it never gets converted and is more practical in dB
			NumberOfBaseUnits
		};
		// Complete list from Wikipedia
		enum Prefix
		{
			Yotta, Zetta, Exa, Peta, Tera, Giga, Mega, Kilo, Hecto, Deca, 
			NoPrefix,  Deci, Centi, Milli, Micro, Nano, Pico, Femto, Atto, Zepto,  Yocto,
			NumberOfPrefixes
		};
	}

	class SIUnit
	{
	public:	
		virtual SI::BaseUnit getBaseUnit()=0;
		virtual SI::Prefix getPrefix()=0;
		virtual double baseToPrefix(double value)=0;
		virtual double prefixToBase(double value)=0;
		virtual const char *getName()=0;
	};

	class SIProperty : public virtual Property
	{
	public:
		virtual double getValue()=0;
		virtual void setValue(double value)=0;
		virtual double getMin()=0;
		virtual double getMax()=0;
		virtual SIUnit *getSIUnit()=0;
		// The default behaviour is for get / set to work on the value that is scaled by the prefix of the SI unit
		// The prefix is set by the SDK specific .dll on a property by property basis to a "recommended" value.
		// If setBaseUnitOnly is called, the SIUnit will change to the base unit and all get /set will work on the 
		// base SI unit, including Hz being returned as seconds.
		virtual void setBaseUnitOnly()=0; 

	};

	// Do an event such as resetting an encoder or taking a snapshot
	class EventProperty : public virtual Property
	{
	public:
		virtual void doEvent()=0;
	};
 
	class GroupProperty : public virtual Property
	{
	protected :
		virtual Property *getFlatPropertyLocal(const char *pTypeName, int localIndex)=0;
	
	public:
		// Get a pointer to a property in the group
		virtual Property *getPropertyByNumber(int index)=0;

		// Searches this group and sub groups for the named property.
		virtual Property *getPropertyByName(const char *pShortName)=0;

		// Return the number of properties in the group
		virtual int getNumberOfProperties()=0;
		// If any of the getProperty.. functions are passed an invalid parameter, they 
		// log an error and return the first property in the group.

		template <class T>
		T *getProperty(int globalIndex)
		{
			return dynamic_cast<T *>(getPropertyByNumber(globalIndex)) ;
		}

		template <class T>
		T *getProperty(const char *pShortName)
		{
			return dynamic_cast<T *>(getPropertyByName(pShortName)) ;
		}

		// beginGroupChange() and commitGroupChange() are used when setting several properties at once
		// e.g. when reading from a file.  No range checking or writing to the SDK is done until 
		// commitGroupChange() is called.  This prevents errors caused by for example an existing
		// A Scan range of 10 samples and rep rate of 100 samples and a new A Scan range of 1000 samples and rep
		// rate of 2000 samples.  Without begin / end group, setting the A Scan range to 1000 would fail because
		// the range is greater than the rep rate
		virtual void beginGroupChange()=0;
		virtual void commitGroupChange()=0;


		// The property change list contains a list of properties whose values, min, max or flags have changed.
		// Each group maintains its own property change list.  
		// There is at most one entry in the change list for each property.  This entry may have several flags masked together,
		// e.g. if the value and the max of the property has changed.
		// The list is reset by calling clearChangeItemLog().
		// The list then grows as properties are set with setValue() calls.
		// If a setValue() call sets the value to the value requested, a change for the value is not added to the list.
		// A change is added to the list if the value is clamped or the set fails completely, in which case a change is added
		// to indicate that the value is still the previous value.  A change is also added to the list if the min or max of 
		// a property is updated.  For example setting range to 100 may produce a change in the min of the rep rate.
		virtual PropertyChangeItem *getChangeItemByIndex(int index)=0;
		virtual void clearChangeItemLog()=0;
		virtual int getNumberOfChangeListEntries()=0;

		// Returns True if this property is an array.  
		// An array group is used for e.g. the array of cards in an NDTS2100
		// Calling getPropertyByNumber(index) gets a group (or property) that contains the contents of the array at "index"
		// Each group that is retuned by getPropertyByNumber(index) has the same name and contains items with the same name.
		virtual bool getIsArray()=0;

		template <class T>
		T *getFlatPropertyGlobal(int globalIndex)
		{
			return dynamic_cast<T *>(getFlatPropertyByNumber(index));
		}
        
		template <class T>
		T *getFlatPropertyLocal(int localIndex)
		{
			return dynamic_cast<T *>(getFlatPropertyLocal(typeid(T).name(), localIndex));
		}

		virtual int getFlatNumberOfIntProperties() = 0;
		virtual int getFlatNumberOfEnumProperties() = 0;
		virtual int getFlatNumberOfSIProperties() = 0;
		virtual int getFlatNumberOfEventProperties() = 0;
         
		/*
		//interface for IntProperty between SCOManagedWrapper and SCO
		virtual IntProperty *getFlatIntPropertyByNumber(int localIndex) = 0;
		virtual int getFlatNumberOfIntProperties() = 0;

		virtual EnumProperty *getFlatEnumPropertyByNumber(int localIndex) = 0;
		virtual int getFlatNumberOfEnumProperties() = 0;

		virtual SIProperty *getFlatSIPropertyByNumber(int localIndex) = 0;
		virtual int getFlatNumberOfSIProperties() = 0;

		virtual EventProperty *getFlatEventPropertyByNumber(int localIndex) = 0;
		virtual int getFlatNumberOfEventProperties() = 0;
		*/
	};

}} // namespaces
#endif