//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
//
// Purpose: 
//
//=============================================================================

#ifndef IDATAMODEL_H
#define IDATAMODEL_H
#ifdef _WIN32
#pragma once
#endif

#include "tier1/interface.h"
#include "tier1/utlvector.h"
#include "tier1/utlsymbol.h"
#include "appframework/IAppSystem.h"
#include "datamodel/dmattributetypes.h"


//-----------------------------------------------------------------------------
// Forward declarations: 
//-----------------------------------------------------------------------------
class CDmAttribute;
class CDmElement;
class IDmeOperator;
class IElementForKeyValueCallback;

struct DmValueBase_t;
class CUtlBuffer;
class KeyValues;
class CUtlSymbolTable;
class CUtlCharConversion;


//-----------------------------------------------------------------------------
// data file format info
//-----------------------------------------------------------------------------
#define DMX_LEGACY_VERSION_STARTING_TOKEN "<!-- DMXVersion"
#define DMX_LEGACY_VERSION_ENDING_TOKEN "-->"

#define DMX_VERSION_STARTING_TOKEN "<!-- dmx"
#define DMX_VERSION_ENDING_TOKEN "-->"

#define GENERIC_DMX_FORMAT "dmx"


enum
{
	DMX_MAX_FORMAT_NAME_MAX_LENGTH = 64,
	DMX_MAX_HEADER_LENGTH = 40 + 2 * DMX_MAX_FORMAT_NAME_MAX_LENGTH,
};

struct DmxHeader_t
{
	char encodingName[ DMX_MAX_FORMAT_NAME_MAX_LENGTH ];
	int nEncodingVersion;
	char formatName[ DMX_MAX_FORMAT_NAME_MAX_LENGTH ];
	int nFormatVersion;

	DmxHeader_t() : nEncodingVersion( -1 ), nFormatVersion( -1 )
	{
		encodingName[ 0 ] = formatName[ 0 ] = '\0';
	}
};


//-----------------------------------------------------------------------------
// element framework phases
//-----------------------------------------------------------------------------
enum DmPhase_t
{
	PH_EDIT,
	PH_EDIT_APPLY,
	PH_EDIT_RESOLVE,
	PH_DEPENDENCY,
	PH_OPERATE,
	PH_OPERATE_RESOLVE,
	PH_OUTPUT,
};


//-----------------------------------------------------------------------------
// file id - also used to refer to elements that don't have file associations
//-----------------------------------------------------------------------------
enum DmFileId_t
{
	DMFILEID_INVALID = 0xffffffff
};

//-----------------------------------------------------------------------------
// Handle to an CDmAttribute
//-----------------------------------------------------------------------------
enum DmAttributeHandle_t
{  
	DMATTRIBUTE_HANDLE_INVALID = 0xffffffff
};

//-----------------------------------------------------------------------------
// Handle to an DmAttributeList_t
//-----------------------------------------------------------------------------
enum DmAttributeReferenceIterator_t
{  
	DMATTRIBUTE_REFERENCE_ITERATOR_INVALID = 0
};

//-----------------------------------------------------------------------------
// element framework interface
//-----------------------------------------------------------------------------
abstract_class IDmElementFramework : public IAppSystem
{
public:
	// Methods of IAppSystem
	virtual bool Connect( CreateInterfaceFn factory ) = 0;
	virtual void Disconnect() = 0;
	virtual void *QueryInterface( const char *pInterfaceName ) = 0;
	virtual InitReturnVal_t Init() = 0;
	virtual void Shutdown() = 0;

	virtual DmPhase_t GetPhase() = 0;

	virtual void SetOperators( const CUtlVector< IDmeOperator* > &operators ) = 0;

	virtual void BeginEdit() = 0; // ends in edit phase, forces apply/resolve if from edit phase
	virtual void Operate( bool bResolve ) = 0; // ends in output phase
	virtual void Resolve() = 0;
};


//-----------------------------------------------------------------------------
// Used only by aplpications to hook in the element framework
//-----------------------------------------------------------------------------
#define VDMELEMENTFRAMEWORK_VERSION	"VDmElementFrameworkVersion001"


//-----------------------------------------------------------------------------
// Main interface
//-----------------------------------------------------------------------------
extern IDmElementFramework *g_pDmElementFramework;


//-----------------------------------------------------------------------------
// datamodel operator interface - for all elements that need to be sorted in the operator dependency graph
//-----------------------------------------------------------------------------
abstract_class IDmeOperator
{
public:
	virtual bool IsDirty() = 0; // ie needs to operate
	virtual void Operate() = 0;

	virtual void GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs ) = 0;
	virtual void GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs ) = 0;
};

//-----------------------------------------------------------------------------
// Class factory methods: 
//-----------------------------------------------------------------------------
class IDmElementFactory
{
public:
	// Creation, destruction
	virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id ) = 0;
	virtual void Destroy( DmElementHandle_t hElement ) = 0;
};


//-----------------------------------------------------------------------------
// Various serialization methods can be installed into the data model factory 
//-----------------------------------------------------------------------------
enum DmConflictResolution_t
{
	CR_DELETE_NEW,
	CR_DELETE_OLD,
	CR_COPY_NEW,
	CR_FORCE_COPY,
};

// convert files to elements and back
// current file encodings supported: binary, xml, xml_flat, keyvalues2, keyvalues2_flat, keyvalues (vmf/vmt/actbusy), text? (qc/obj)
class IDmSerializer
{
public:
	virtual const char *GetName() const = 0;
	virtual const char *GetDescription() const = 0;
	virtual bool IsBinaryFormat() const = 0;
	virtual bool StoresVersionInFile() const = 0;
	virtual int GetCurrentVersion() const = 0;

	// Write into the UtlBuffer, return true if successful
	// if we decide to implement non-identity conversions between formats on write, then the source and dest format will need to be passed in here
	virtual bool Serialize( CUtlBuffer &buf, CDmElement *pRoot ) = 0;

	// Read from the UtlBuffer, return true if successful, and return the read-in root in ppRoot.
	virtual bool Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
							  const char *pSourceFormatName, int nSourceFormatVersion,
							  DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot ) = 0;
};

// convert legacy elements to non-legacy elements
// legacy formats include: binary_vN, keyvalues2_v1, keyvalues2_flat_v1, xml, xml_flat
//   where N is a version number 
class IDmLegacyUpdater
{
public:
	virtual const char *GetName() const = 0;
	virtual bool IsLatestVersion() const = 0;

	// Updates ppRoot to first non-legacy generic dmx format, returns false if the conversion fails
	virtual bool Update( CDmElement **ppRoot ) = 0;
};

// converts old elements to new elements
// current formats include: animset presets, particle definitions, exported maya character, etc.
class IDmFormatUpdater
{
public:
	virtual const char *GetName() const = 0;
	virtual const char *GetDescription() const = 0;
	virtual const char *GetExtension() const = 0;
	virtual int GetCurrentVersion() const = 0;
	virtual const char *GetDefaultEncoding() const = 0;

	// Converts pSourceRoot from nSourceVersion to the current version, returns false if the conversion fails
	virtual bool Update( CDmElement **pRoot, int nSourceVersion ) = 0;
};

//-----------------------------------------------------------------------------
// Interface for callbacks to supply element types for specific keys inside keyvalues files
//-----------------------------------------------------------------------------
class IElementForKeyValueCallback
{
public:
	virtual const char *GetElementForKeyValue( const char *pszKeyName, int iNestingLevel ) = 0;
};

//-----------------------------------------------------------------------------
// Purpose: Optional helper passed in with clipboard data which is called when it's time to clean up the clipboard data in case the application
//  had some dynamically allocated data attached to a KV SetPtr object...
//-----------------------------------------------------------------------------
abstract_class IClipboardCleanup
{
public:
	virtual void ReleaseClipboardData( CUtlVector< KeyValues * >& list ) = 0;
};


//-----------------------------------------------------------------------------
// Purpose: Can be installed to be called back when data changes
//-----------------------------------------------------------------------------
enum DmNotifySource_t
{
	// Sources
	NOTIFY_SOURCE_APPLICATION = 0,
	NOTIFY_SOURCE_UNDO,
	NOTIFY_SOURCE_FIRST_DME_CONTROL_SOURCE = 4,	// Sources from dme_controls starts here
	NOTIFY_SOURCE_FIRST_APPLICATION_SOURCE = 8, // Sources from applications starts here
};

enum DmNotifyFlags_t
{
	// Does this dirty the document?
	NOTIFY_SOURCE_BITS					= 8,
	NOTIFY_SETDIRTYFLAG					= (1<<NOTIFY_SOURCE_BITS),

	// Type of change (note NOTIFY_CHANGE_TOPOLOGICAL/NOTIFY_CHANGE_ATTRIBUTE_VALUE/NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE
	// are set by the Datamodel itself)
	NOTIFY_CHANGE_TOPOLOGICAL			= (1<<(NOTIFY_SOURCE_BITS+4)),	// Element created, destroyed, element attribute or element array attribute value changed
	NOTIFY_CHANGE_ATTRIBUTE_VALUE		= (1<<(NOTIFY_SOURCE_BITS+5)),	// Non-element attribute value changed
	NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE	= (1<<(NOTIFY_SOURCE_BITS+6)),	// Non-element array attribute added or removed
	NOTIFY_CHANGE_OTHER					= (1<<(NOTIFY_SOURCE_BITS+7)),	// Non attribute related change (a change in UI, for example)

	NOTIFY_CHANGE_MASK = ( NOTIFY_CHANGE_TOPOLOGICAL | NOTIFY_CHANGE_ATTRIBUTE_VALUE | NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE | NOTIFY_CHANGE_OTHER ),

};

abstract_class IDmNotify
{
public:
	// See DmNotifySource_t and DmNotifyFlags_t
	virtual void NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags ) = 0;
};


//-----------------------------------------------------------------------------
// Purpose: Helper for debugging undo system
//-----------------------------------------------------------------------------
struct UndoInfo_t
{
	bool		terminator;
	const char	*desc;
	const char	*undo;
	const char	*redo;
	int			numoperations;
};


//-----------------------------------------------------------------------------
// Interface for undo
//-----------------------------------------------------------------------------
abstract_class IUndoElement
{
public:
	virtual void		Undo() = 0;
	virtual void		Redo() = 0;

	virtual const char *UndoDesc() const = 0;
	virtual const char *RedoDesc() const = 0;
	virtual const char *GetDesc() const = 0;
	virtual void		Release() = 0;

protected:
	virtual bool IsEndOfStream() const = 0;
	virtual void SetEndOfStream( bool end ) = 0;
	virtual	~IUndoElement() { }

	friend class CUndoManager;
};

//-----------------------------------------------------------------------------
// traversal depth for copy, search, and other element/attribute traversals
//-----------------------------------------------------------------------------
enum TraversalDepth_t
{
	TD_ALL,		// traverse all attributes
	TD_DEEP,	// traverse attributes with FATTRIB_NEVERCOPY clear
	TD_SHALLOW,	// traverse attributes with FATTRIB_MUSTCOPY set
	TD_NONE,	// don't traverse any attributes
};


//-----------------------------------------------------------------------------
// Main interface for creation of all IDmeElements: 
//-----------------------------------------------------------------------------
class IDataModel : public IAppSystem
{
public:	
	// Installs factories used to instance elements
	virtual void			AddElementFactory( const char *pElementTypeName, IDmElementFactory *pFactory ) = 0;

	// This factory will be used to instance all elements whose type name isn't found.
	virtual void			SetDefaultElementFactory( IDmElementFactory *pFactory ) = 0;

	virtual int				GetFirstFactory() const = 0;
	virtual int				GetNextFactory( int index ) const = 0;
	virtual bool			IsValidFactory( int index ) const = 0;
	virtual const char		*GetFactoryName( int index ) const = 0;

	// create/destroy element methods - proxies to installed element factories
	virtual DmElementHandle_t	CreateElement( UtlSymId_t typeSymbol, const char *pElementName, DmFileId_t fileid = DMFILEID_INVALID, const DmObjectId_t *pObjectID = NULL ) = 0;
	virtual DmElementHandle_t	CreateElement( const char *pTypeName, const char *pElementName, DmFileId_t fileid = DMFILEID_INVALID, const DmObjectId_t *pObjectID = NULL ) = 0;
	virtual void				DestroyElement( DmElementHandle_t hElement ) = 0;

	// element handle related methods
	virtual	CDmElement*			GetElement			( DmElementHandle_t hElement ) const = 0;
	virtual UtlSymId_t			GetElementType	    ( DmElementHandle_t hElement ) const = 0;
	virtual const char*			GetElementName	    ( DmElementHandle_t hElement ) const = 0;
	virtual const DmObjectId_t&	GetElementId	    ( DmElementHandle_t hElement ) const = 0;

	virtual const char*			GetAttributeNameForType( DmAttributeType_t attType ) const = 0;
	virtual DmAttributeType_t	GetAttributeTypeForName( const char *name ) const = 0;

	// Adds various serializers and updaters
	virtual void			AddSerializer( IDmSerializer *pSerializer ) = 0;
	virtual void			AddLegacyUpdater( IDmLegacyUpdater *pUpdater ) = 0;
	virtual void			AddFormatUpdater( IDmFormatUpdater *pUpdater ) = 0;

	// file format methods
	virtual const char*		GetFormatExtension( const char *pFormatName ) = 0;
	virtual const char*		GetFormatDescription( const char *pFormatName ) = 0;
	virtual int				GetFormatCount() const = 0;
	virtual const char *	GetFormatName( int i ) const = 0;
	virtual const char *	GetDefaultEncoding( const char *pFormatName ) = 0;

	// file encoding methods
	virtual int				GetEncodingCount() const = 0;
	virtual const char *	GetEncodingName( int i ) const = 0;
	virtual bool			IsEncodingBinary( const char *pEncodingName ) const = 0;
	virtual bool			DoesEncodingStoreVersionInFile( const char *pEncodingName ) const = 0;

	// For serialization, set the delimiter rules
	// These methods are meant to be used by importer/exporters
	virtual void			SetSerializationDelimiter( CUtlCharConversion *pConv ) = 0;
	virtual void			SetSerializationArrayDelimiter( const char *pDelimiter ) = 0;

	// used to skip auto-creation of child elements during unserialization
	virtual bool			IsUnserializing() = 0;

	// Serialization of a element tree into a utlbuffer
	virtual bool			Serialize( CUtlBuffer &outBuf, const char *pEncodingName, const char *pFormatName, DmElementHandle_t hRoot ) = 0;

	// Unserializes, returns the root of the unserialized tree in hRoot 
	// The file name passed in is simply for error messages and fileid creation
	virtual bool			Unserialize( CUtlBuffer &inBuf, const char *pEncodingName, const char *pSourceFormatName, const char *pFormatHint,
										 const char *pFileName, DmConflictResolution_t idConflictResolution, DmElementHandle_t &hRoot ) = 0;

	// converts from elements from old file formats to elements for the current file format
	virtual bool			UpdateUnserializedElements( const char *pSourceFormatName, int nSourceFormatVersion,
														DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot ) = 0;

	// force creation of untyped elements, ignoring type
	virtual void			OnlyCreateUntypedElements( bool bEnable ) = 0;

	// Finds a serializer by name
	virtual IDmSerializer*		FindSerializer( const char *pEncodingName ) const = 0;
	virtual IDmLegacyUpdater*	FindLegacyUpdater( const char *pLegacyFormatName ) const = 0;
	virtual IDmFormatUpdater*	FindFormatUpdater( const char *pFormatName ) const = 0;

	// saves element tree to a file
	virtual bool				SaveToFile( const char *pFileName, const char *pPathID, const char *pEncodingName, const char *pFormatName, CDmElement *pRoot ) = 0;

	// restores file into an element tree
	// NOTE: Format name is only used here for those formats which don't store
	// the format name in the file. Use NULL for those formats which store the 
	// format name in the file.
	virtual DmFileId_t			RestoreFromFile( const char *pFileName, const char *pPathID, const char *pFormatHint, CDmElement **ppRoot, DmConflictResolution_t idConflictResolution = CR_DELETE_NEW, DmxHeader_t *pHeaderOut = NULL ) = 0;

	// Sets the name of the DME element to create in keyvalues serialization
	virtual void			SetKeyValuesElementCallback( IElementForKeyValueCallback *pCallbackInterface ) = 0;
	virtual const char		*GetKeyValuesElementName( const char *pszKeyName, int iNestingLevel ) = 0;

	// Global symbol table for the datamodel system
	virtual UtlSymId_t		GetSymbol( const char *pString ) = 0;
	virtual const char *	GetString( UtlSymId_t sym ) const = 0;

	// Returns the total number of elements allocated at the moment
	virtual int				GetMaxNumberOfElements() = 0;
	virtual int				GetElementsAllocatedSoFar() = 0;
	virtual int				GetAllocatedAttributeCount() = 0;
	virtual int				GetAllocatedElementCount() = 0;
	virtual DmElementHandle_t	FirstAllocatedElement() = 0;
	virtual DmElementHandle_t	NextAllocatedElement( DmElementHandle_t it ) = 0;

	// estimate memory usage
	virtual int				EstimateMemoryUsage( DmElementHandle_t hElement, TraversalDepth_t depth ) = 0;

	// Undo/Redo support
	virtual void			SetUndoEnabled( bool enable ) = 0;
	virtual bool			IsUndoEnabled() const = 0;
	virtual bool			UndoEnabledForElement( const CDmElement *pElement ) const = 0;
	virtual bool			IsDirty() const = 0;
	virtual bool			CanUndo() const = 0;
	virtual bool			CanRedo() const = 0;
	// If chaining ID is != 0 and the next StartUndo uses the same ID, then the operations will be chained together into a single undo operation
	virtual void			StartUndo( char const *undodesc, char const *redodesc, int nChainingID = 0 ) = 0;
	virtual void			FinishUndo() = 0;
	virtual void			AbortUndoableOperation() = 0; // called instead of FinishUndo, essentially performs and Undo() and WipeRedo() if any undo items have been added to the stack
	virtual void			ClearRedo() = 0;
	virtual const char		*GetUndoDesc() = 0;
	virtual const char		*GetRedoDesc() = 0;
	// From the UI, perform the Undo operation
	virtual void			Undo() = 0;
	virtual void			Redo() = 0;
	virtual void			TraceUndo( bool state ) = 0; // if true, undo records spew as they are added

	// Wipes out all Undo data
	virtual void			ClearUndo() = 0;

	virtual void			GetUndoInfo( CUtlVector< UndoInfo_t >& list ) = 0;

	virtual void			AddUndoElement( IUndoElement *pElement ) = 0;
	virtual UtlSymId_t		GetUndoDescInternal( const char *context ) = 0;
	virtual UtlSymId_t		GetRedoDescInternal( const char *context ) = 0;

	virtual void			EmptyClipboard() = 0;
	virtual void			SetClipboardData( CUtlVector< KeyValues * >& data, IClipboardCleanup *pfnOptionalCleanuFunction = 0 ) = 0;
	virtual void			AddToClipboardData( KeyValues *add ) = 0;
	virtual void			GetClipboardData( CUtlVector< KeyValues * >& data ) = 0;
	virtual bool			HasClipboardData() const = 0;

	// Handles to attributes
	virtual CDmAttribute *	GetAttribute( DmAttributeHandle_t h ) = 0;
	virtual bool			IsAttributeHandleValid( DmAttributeHandle_t h ) const = 0;

	// file id reference methods
	virtual int					NumFileIds() = 0;
	virtual DmFileId_t			GetFileId( int i ) = 0;
	virtual DmFileId_t			FindOrCreateFileId( const char *pFilename ) = 0;
	virtual void				RemoveFileId( DmFileId_t fileid ) = 0;
	virtual DmFileId_t			GetFileId( const char *pFilename ) = 0;
	virtual const char *		GetFileName( DmFileId_t fileid ) = 0;
	virtual void				SetFileName( DmFileId_t fileid, const char *pFileName ) = 0;
	virtual const char *		GetFileFormat( DmFileId_t fileid ) = 0;
	virtual void				SetFileFormat( DmFileId_t fileid, const char *pFormat ) = 0;
	virtual DmElementHandle_t	GetFileRoot( DmFileId_t fileid ) = 0;
	virtual void				SetFileRoot( DmFileId_t fileid, DmElementHandle_t hRoot ) = 0;
	virtual bool				IsFileLoaded( DmFileId_t fileid ) = 0;
	virtual void				MarkFileLoaded( DmFileId_t fileid ) = 0;
	virtual void				UnloadFile( DmFileId_t fileid ) = 0;
	virtual int					NumElementsInFile( DmFileId_t fileid ) = 0;

	virtual void				DontAutoDelete( DmElementHandle_t hElement ) = 0;

	// handle validity methods - these shouldn't really be here, but the undo system needs them...
	virtual void			MarkHandleInvalid( DmElementHandle_t hElement ) = 0;
	virtual void			MarkHandleValid( DmElementHandle_t hElement ) = 0;

	virtual DmElementHandle_t	FindElement( const DmObjectId_t &id ) = 0;

	virtual	DmAttributeReferenceIterator_t	FirstAttributeReferencingElement( DmElementHandle_t hElement ) = 0;
	virtual DmAttributeReferenceIterator_t	NextAttributeReferencingElement( DmAttributeReferenceIterator_t hAttrIter ) = 0;
	virtual CDmAttribute *					GetAttribute( DmAttributeReferenceIterator_t hAttrIter ) = 0;

	// Install, remove notify callbacks associated w/ undo contexts
	virtual bool InstallNotificationCallback( IDmNotify *pNotify ) = 0;
	virtual void RemoveNotificationCallback( IDmNotify *pNotify ) = 0;
	virtual bool IsSuppressingNotify( ) const = 0;
	virtual void SetSuppressingNotify( bool bSuppress ) = 0;
	virtual void PushNotificationScope( const char *pReason, int nNotifySource, int nNotifyFlags ) = 0;
	virtual void PopNotificationScope( bool bAbort = false ) = 0;
	virtual const char *GetUndoString( UtlSymId_t sym )	= 0;

	virtual bool HasElementFactory( const char *pElementType ) const = 0;

	// Call before you make any undo records
	virtual void SetUndoDepth( int nSize ) = 0;

	// Displats memory stats to the console
	virtual void DisplayMemoryStats() = 0;
};


//-----------------------------------------------------------------------------
// Used only by applications to hook in the data model
//-----------------------------------------------------------------------------
#define VDATAMODEL_INTERFACE_VERSION	"VDataModelVersion001"


//-----------------------------------------------------------------------------
// Main interface accessor
//-----------------------------------------------------------------------------
extern IDataModel *g_pDataModel;


//-----------------------------------------------------------------------------
// Allows clients to implement customized undo elements
//-----------------------------------------------------------------------------
class CUndoElement : public IUndoElement
{
public:
	CUndoElement( const char *pDesc )
	{
		m_UndoDesc = g_pDataModel->GetUndoDescInternal( pDesc );
		m_RedoDesc = g_pDataModel->GetRedoDescInternal( pDesc );
		m_pDesc = pDesc;
		m_bEndOfStream = false;
	}

	virtual void Release()
	{
		delete this;
	}

	virtual const char *UndoDesc() const
	{
		return g_pDataModel->GetUndoString( m_UndoDesc );
	}

	virtual const char *RedoDesc() const
	{
		return g_pDataModel->GetUndoString( m_RedoDesc );
	}

	virtual const char *GetDesc() const
	{
		return m_pDesc;
	}

protected:
	virtual bool IsEndOfStream() const 
	{ 
		return m_bEndOfStream; 
	}

	virtual void SetEndOfStream( bool end )
	{ 
		m_bEndOfStream = end; 
	}

	const char *m_pDesc;
	CUtlSymbol	m_UndoDesc;
	CUtlSymbol	m_RedoDesc;
	bool m_bEndOfStream;

private:
	friend class CUndoManager;
};


//-----------------------------------------------------------------------------
// Purpose: Simple helper class
//-----------------------------------------------------------------------------
class CUndoScopeGuard
{
public:
	explicit CUndoScopeGuard( const char *udesc, const char *rdesc = NULL )
	{
		m_bReleased = false;
		m_bNotify = false;
		m_pNotify = NULL;
		g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc );
	}

	explicit CUndoScopeGuard( int nChainingID, char const *udesc )
	{
		m_bReleased = false;
		m_bNotify = false;
		m_pNotify = NULL;
		g_pDataModel->StartUndo( udesc, udesc, nChainingID );
	}

	explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 )
	{
		m_bReleased = false;
		m_bNotify = true;
		m_pNotify = NULL;
		g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID );
		g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags );
	}

	explicit CUndoScopeGuard( int nNotifySource, int nNotifyFlags, IDmNotify *pNotify, const char *udesc, const char *rdesc = NULL, int nChainingID = 0 )
	{
		m_bReleased = false;
		m_bNotify = true;
		m_pNotify = NULL;
		g_pDataModel->StartUndo( udesc, rdesc ? rdesc : udesc, nChainingID );
		if ( pNotify )
		{
			if ( g_pDataModel->InstallNotificationCallback( pNotify ) )
			{
				m_pNotify = pNotify;
			}
		}
		g_pDataModel->PushNotificationScope( udesc, nNotifySource, nNotifyFlags );
	}

	~CUndoScopeGuard()
	{
		Release();
	}

	void Release()
	{
		if ( !m_bReleased )
		{
			g_pDataModel->FinishUndo();
			if ( m_bNotify )
			{
				g_pDataModel->PopNotificationScope( );
				m_bNotify = false;
			}
			if ( m_pNotify )
			{
				g_pDataModel->RemoveNotificationCallback( m_pNotify );
				m_pNotify = NULL;
			}
			m_bReleased = true;
		}
	}

	void Abort()
	{
		if ( !m_bReleased )
		{
			g_pDataModel->AbortUndoableOperation();
			if ( m_bNotify )
			{
				g_pDataModel->PopNotificationScope( true );
				m_bNotify = false;
			}
			if ( m_pNotify )
			{
				g_pDataModel->RemoveNotificationCallback( m_pNotify );
				m_pNotify = NULL;
			}
			m_bReleased = true;
		}
	}

private:
	IDmNotify *m_pNotify;
	bool m_bReleased;
	bool m_bNotify;
};


//-----------------------------------------------------------------------------
// Purpose: Simple helper class to disable Undo/Redo operations when in scope
//-----------------------------------------------------------------------------
class CChangeUndoScopeGuard
{
public:
	CChangeUndoScopeGuard( bool bNewState )
	{
		m_bReleased = false;
		m_bNotify = false;
		m_pNotify = NULL;
		m_bOldValue = g_pDataModel->IsUndoEnabled();
		g_pDataModel->SetUndoEnabled( bNewState );
	};

	CChangeUndoScopeGuard( bool bNewState, const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL )
	{
		m_bReleased = false;
		m_bOldValue = g_pDataModel->IsUndoEnabled();
		g_pDataModel->SetUndoEnabled( bNewState );

		m_bNotify = true;
		m_pNotify = NULL;
		if ( pNotify )
		{
			if ( g_pDataModel->InstallNotificationCallback( pNotify ) )
			{
				m_pNotify = pNotify;
			}
		}
		g_pDataModel->PushNotificationScope( pDesc, nNotifySource, nNotifyFlags );
	};

	~CChangeUndoScopeGuard()
	{
		Release();
	}

	void Release()
	{
		// Releases the guard...
		if ( !m_bReleased )
		{
			g_pDataModel->SetUndoEnabled( m_bOldValue );
			m_bReleased = true;
			if ( m_bNotify )
			{
				g_pDataModel->PopNotificationScope( );
				m_bNotify = false;
			}
			if ( m_pNotify )
			{
				g_pDataModel->RemoveNotificationCallback( m_pNotify );
				m_pNotify = NULL;
			}
		}
	}

private:
	IDmNotify *m_pNotify;
	bool m_bOldValue;
	bool m_bReleased;
	bool m_bNotify;
};

class CDisableUndoScopeGuard : public CChangeUndoScopeGuard
{
	typedef CChangeUndoScopeGuard BaseClass;

public:
	CDisableUndoScopeGuard() : BaseClass( false ) { }
	CDisableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) :
		BaseClass( false, pDesc, nNotifySource, nNotifyFlags, pNotify ) {}
};

class CEnableUndoScopeGuard : public CChangeUndoScopeGuard
{
	typedef CChangeUndoScopeGuard BaseClass;

public:
	CEnableUndoScopeGuard( ) : BaseClass( true ) { }
	CEnableUndoScopeGuard( const char *pDesc, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL ) :
		BaseClass( true, pDesc, nNotifySource, nNotifyFlags, pNotify ) {}
};


#define DEFINE_SOURCE_UNDO_SCOPE_GUARD( _classnameprefix, _source )	\
	class C ## _classnameprefix ## UndoScopeGuard : public CUndoScopeGuard	\
	{															\
		typedef CUndoScopeGuard BaseClass;						\
																\
	public:														\
		C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \
			BaseClass( _source, nNotifyFlags, pUndoDesc, pRedoDesc, nChainingID ) \
		{														\
		}														\
		C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, IDmNotify *pNotify, const char *pUndoDesc, const char *pRedoDesc = NULL, int nChainingID = 0 ) : \
			BaseClass( _source, nNotifyFlags, pNotify, pUndoDesc, pRedoDesc, nChainingID ) \
		{														\
		}														\
		C ## _classnameprefix ## UndoScopeGuard( int nNotifyFlags, const char *pUndoDesc, int nChainingID ) : \
			BaseClass( _source, nNotifyFlags, pUndoDesc, pUndoDesc, nChainingID ) \
		{														\
		}														\
	};															\
	class C ## _classnameprefix ## DisableUndoScopeGuard : public CDisableUndoScopeGuard	\
	{															\
		typedef CDisableUndoScopeGuard BaseClass;				\
																\
	public:														\
		C ## _classnameprefix ## DisableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \
			BaseClass( pDesc, _source, nNotifyFlags, pNotify )	\
		{														\
		}														\
	};															\
	class C ## _classnameprefix ## EnableUndoScopeGuard : public CEnableUndoScopeGuard	\
	{															\
		typedef CEnableUndoScopeGuard BaseClass;				\
																\
	public:														\
		C ## _classnameprefix ## EnableUndoScopeGuard( const char *pDesc, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \
			BaseClass( pDesc, _source, nNotifyFlags, pNotify )	\
		{														\
		}														\
	}


//-----------------------------------------------------------------------------
// Purpose: Simple helper class to disable NotifyDataChanged from current scope
//-----------------------------------------------------------------------------
class CNotifyScopeGuard
{
public:
	CNotifyScopeGuard( const char *pReason, int nNotifySource, int nNotifyFlags, IDmNotify *pNotify = NULL )
	{
		m_bReleased = false;
		m_pNotify = NULL;
		g_pDataModel->PushNotificationScope( pReason, nNotifySource, nNotifyFlags );
		if ( pNotify )
		{
			if ( g_pDataModel->InstallNotificationCallback( pNotify ) )
			{
				m_pNotify = pNotify;
			}
		}
	};

	~CNotifyScopeGuard()
	{
		Release();
	}

	void Release()
	{
		// Releases the guard...
		if ( !m_bReleased )
		{
			g_pDataModel->PopNotificationScope( );
			if ( m_pNotify )
			{
				g_pDataModel->RemoveNotificationCallback( m_pNotify );
				m_pNotify = NULL;
			}
			m_bReleased = true;
		}
	}

private:
	CNotifyScopeGuard( const CNotifyScopeGuard& g );

private:
	IDmNotify *m_pNotify;
	bool m_bReleased;
};


#define DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( _classnameprefix, _source )	\
	class C ## _classnameprefix ## NotifyScopeGuard : public CNotifyScopeGuard	\
	{															\
		typedef CNotifyScopeGuard BaseClass;					\
																\
	public:														\
		C ## _classnameprefix ## NotifyScopeGuard( const char *pReason, int nNotifyFlags, IDmNotify *pNotify = NULL ) : \
			BaseClass( pReason, _source, nNotifyFlags, pNotify )\
		{														\
		}														\
	}


//-----------------------------------------------------------------------------
// Purpose: Simple helper class to disable notifications when in scope
//-----------------------------------------------------------------------------
class CChangeNotifyScopeGuard
{
public:
	CChangeNotifyScopeGuard( bool bNewState )
	{
		m_bReleased = false;
		m_bOldValue = g_pDataModel->IsSuppressingNotify();
		g_pDataModel->SetSuppressingNotify( bNewState );
	};

	~CChangeNotifyScopeGuard()
	{
		Release();
	}

	void Release()
	{
		// Releases the guard...
		if ( !m_bReleased )
		{
			g_pDataModel->SetSuppressingNotify( m_bOldValue );
			m_bReleased = true;
		}
	}

private:
	bool m_bOldValue;
	bool m_bReleased;
};

class CDisableNotifyScopeGuard : public CChangeNotifyScopeGuard
{
	typedef CChangeNotifyScopeGuard BaseClass;

public:
	CDisableNotifyScopeGuard() : BaseClass( true ) { }

private:
	CDisableNotifyScopeGuard( const CDisableNotifyScopeGuard& g );
};

class CEnableNotifyScopeGuard : public CChangeNotifyScopeGuard
{
	typedef CChangeNotifyScopeGuard BaseClass;

public:
	CEnableNotifyScopeGuard( ) : BaseClass( false ) { }

private:
	CEnableNotifyScopeGuard( const CEnableNotifyScopeGuard& g );
};


//-----------------------------------------------------------------------------
// Standard undo/notify guards for the application
//-----------------------------------------------------------------------------
DEFINE_SOURCE_UNDO_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION );
DEFINE_SOURCE_NOTIFY_SCOPE_GUARD( App, NOTIFY_SOURCE_APPLICATION );



#endif // IDATAMODEL_H
