#pragma once

#include "PropertiesInterface.h"
#include "PropertyInternal.h"

using namespace std; 
namespace NDTS
{
namespace SCO
{
	class IntPropertyInternal;
	class EventPropertyInternal;


	// Interface to a hooks object that is SDK specific
	// provides a way for the SDK specific code to run during certain property operations
	class GroupHooks
	{
	public:
		// Called during an IntPropertyInternal setValue before the value is sent to the SDK 
		// return false if it is not currently OK to set the property, e.g. if scanning
		virtual bool isOkToSet(PropertyInternal *pProperty)=0;
		// Called once the property has been set in the SDK.  E.g. if the rep rate has been changed,
		// update the max of the range.
		virtual void postSetUpdate(IntPropertyInternal *pProp)=0;
		virtual void postEventUpdate(EventPropertyInternal *pProp)=0;
		// Called after the app has called commitGroup() but before any properties changed in the commit have been applied
		// used to set certain values so that the commit won't fail, e.g. set the A Scan range to a small value.
		virtual void preCommit()=0;
	};


	// See explanation in IntPropertyInernal.h
	#pragma warning (disable :4250)

	class SCO_API GroupPropertyInternal :	public PropertyInternal, public virtual GroupProperty
	{

	private:
		// The states that are used on commitGroupChange()
		enum GroupChangeState
		{
			GSTClearSIGroupChange,
			GSTSIShadowProperty,
			GSTClearIntGroupChange,
			GSTRunHook,
			GSTCommitRemainingInts,
			NumGroupChangeStates
		};

	protected :
		Property *getFlatPropertyLocal(const char *pTypeName, int localIndex) ;

	public:
		GroupPropertyInternal(char *pShortName, char *pLongName, int flags);
		virtual ~GroupPropertyInternal(void);

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

		// Return the property at this index in the current group. Uses the list of visible properties.
		virtual Property *getPropertyByNumber(int index);
		// Searches this group and sub groups for the named property.
		virtual Property *getPropertyByName(const char *pShortName);

		virtual void beginGroupChange();
		virtual void commitGroupChange();

		virtual bool getIsArray();

		virtual bool isInIntGroupChange();
		virtual bool isInSIGroupChange();

		// Each group maintains its own property change list to help with de-duplication of entries.
		// addChangeItem should not be called from the application
		virtual void addChangeItemToGroup(Property *pProperty, ChangeTypeFlags changeTypeFlag);
		virtual PropertyChangeItem *getChangeItemByIndex(int index);
		virtual void clearChangeItemLog();
		virtual int getNumberOfChangeListEntries();

		// These functions should only be called by the associated SDK library and never by the application
		// Initialisation
		void addProperty(PropertyInternal *pProperty);
		void initComplete();
		// Hooks
		void setGroupHooks(GroupHooks *pGroupHooksObject);

		// These functions should only be called from internal functions
		// isOkToSet is called from a child IntProp or EventProp before it writes the value to the SDK.
		// We return true if we are currently in the right state to set pProp (e.g. not scanning)
		// If there is a GroupHooks, call  IGroupHooks->isOkToSet(), otherwise call parent's isOkToSet
		bool isOkToSet(PropertyInternal *pProp);

		// postSetUpdate is called after the SDK has been called with an IntProp change.  
		// Hook or parent is called in a similar way to isOkToSet
		void postSetUpdate(IntPropertyInternal *pProp);
		// Called after an event has been sucesfully applied
		void postEventUpdate(EventPropertyInternal *pProp);
		bool isArray;

		virtual int getFlatNumberOfIntProperties();
		virtual int getFlatNumberOfEnumProperties();
		virtual int getFlatNumberOfSIProperties();
		virtual int getFlatNumberOfEventProperties();
        
		/*
		virtual IntProperty *getFlatIntPropertyByNumber(int index);
		virtual int getFlatNumberOfIntProperties();

		virtual EnumProperty *getFlatEnumPropertyByNumber(int index);
		virtual int getFlatNumberOfEnumProperties();
        
		virtual SIProperty *getFlatSIPropertyByNumber(int index);
		virtual int getFlatNumberOfSIProperties();

		virtual EventProperty *getFlatEventPropertyByNumber(int index);
		virtual int getFlatNumberOfEventProperties();
		*/

	private:

#pragma warning(push)
#pragma warning(disable : 4251)			// Disable warning warning C4251: 'Sco::GroupPropertyInternal::propertyVector' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'NdtsSco::GroupPropertyInternal
		vector<PropertyInternal *> allPropertiesVector;  // All the properties in this group
		vector<PropertyInternal *> visiblePropertiesVector; // All the properties visible to the outside world - i.e. not including Int that are shadowed by SI
		bool inIntGroupChange;
		bool inSIGroupChange;
		vector<GroupPropertyInternal *> subGroups;
		vector<PropertyChangeItem> changeList;

		vector<IntProperty *> flatIntPropertyVector;
		vector<EnumProperty *> flatEnumPropertyVector;
		vector<SIProperty *> flatSIPropertyVector;
		vector<EventProperty *> flatEventPropertyVector;

		bool isFlatPropertyListConstructed;

		GroupHooks *pGroupHooksObject;
		void setCommitGroupChangeState(enum GroupChangeState state);
		int getNumberOfUncommittedChanges();
		Property *getPropertyByNameInternal(const char *pShortName);
        
		void buildFlatPropertyByNumber();
		void addGroupToFlatPropertyByNumber(GroupProperty *pGroup);

#pragma warning(pop)		
	};
}}
