//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

#include "dmattributeinternal.h"
#include "datamodel/dmelement.h"
#include "dmelementdictionary.h"
#include "datamodel/idatamodel.h"
#include "datamodel.h"
#include "tier1/uniqueid.h"
#include "Color.h"
#include "mathlib/vector.h"
#include "tier1/utlstring.h"
#include "tier1/utlbuffer.h"
#include "tier1/KeyValues.h"
#include "tier1/mempool.h"
#include "mathlib/vmatrix.h"
#include "datamodel/dmattributevar.h"
#include <ctype.h>

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// Tests equality
//-----------------------------------------------------------------------------
template< class T >
bool IsAttributeEqual( const T& src1, const T& src2 )
{
	return src1 == src2;
}

template< class T >
bool IsAttributeEqual( const CUtlVector<T> &src1, const CUtlVector<T> &src2 )
{
	if ( src1.Count() != src2.Count() )
		return false;
		
	for ( int i=0; i < src1.Count(); i++ )
	{
		if ( !( src1[i] == src2[i] ) )
			return false;
	}
	
	return true;
}


//-----------------------------------------------------------------------------
// Typesafety check for element handles
//-----------------------------------------------------------------------------
static inline bool IsA( DmElementHandle_t hElement, UtlSymId_t type )
{
	// treat NULL, deleted, and unloaded elements as being of any type - 
	// when set, undeleted or loaded, this should be checked again
	CDmElement *pElement = g_pDataModel->GetElement( hElement );
	return pElement ? pElement->IsA( type ) : true;
}


//-----------------------------------------------------------------------------
// Element attributes are never directly unserialized
//-----------------------------------------------------------------------------
static bool Serialize( CUtlBuffer &buf, DmElementHandle_t src )
{
	Assert( 0 );
	return false;
}

static bool Unserialize( CUtlBuffer &buf, DmElementHandle_t &dest )
{
	Assert( 0 );
	return false;
}

static bool Serialize( CUtlBuffer &buf, const DmUnknownAttribute_t& src )
{
	Assert( 0 );
	return false;
}

static bool Unserialize( CUtlBuffer &buf, DmUnknownAttribute_t &dest )
{
	Assert( 0 );
	return false;
}

#include "tier1/utlbufferutil.h"

//-----------------------------------------------------------------------------
// Internal interface for dealing with generic attribute operations
//-----------------------------------------------------------------------------
abstract_class IDmAttributeOp
{
public:
	virtual void* CreateAttributeData() = 0;
	virtual void DestroyAttributeData( void *pData ) = 0;
	virtual void SetDefaultValue( void *pData ) = 0;
	virtual int DataSize() = 0;
	virtual int ValueSize() = 0;
	virtual bool SerializesOnMultipleLines() = 0;
	virtual bool SkipUnserialize( CUtlBuffer& buf ) = 0;
	virtual const char *AttributeTypeName() = 0;

	virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue ) = 0;
	virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue ) = 0;
	virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue ) = 0;
	virtual void SetToDefaultValue( CDmAttribute *pAttribute ) = 0;
	virtual bool Serialize( const CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
	virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
	virtual bool SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) = 0;
	virtual bool UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
	virtual bool UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) = 0;
	virtual void OnUnserializationFinished( CDmAttribute *pAttribute ) = 0;
};


//-----------------------------------------------------------------------------
// Global table of generic attribute operations looked up by type
//-----------------------------------------------------------------------------
static IDmAttributeOp* s_pAttrInfo[ AT_TYPE_COUNT ];


//-----------------------------------------------------------------------------
//
// Implementation of IDmAttributeOp for single-valued attributes
//
//-----------------------------------------------------------------------------
template< class T >
class CDmAttributeOp : public IDmAttributeOp
{
public:
	virtual void* CreateAttributeData();
	virtual void DestroyAttributeData( void *pData );
	virtual void SetDefaultValue( void *pData );
	virtual int DataSize();
	virtual int ValueSize();
	virtual bool SerializesOnMultipleLines();
	virtual bool SkipUnserialize( CUtlBuffer& buf );
	virtual const char *AttributeTypeName();

	virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue );
	virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue );
	virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue );
	virtual void SetToDefaultValue( CDmAttribute *pAttribute );
	virtual bool Serialize( const CDmAttribute *pData, CUtlBuffer &buf );
	virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf );
	virtual bool SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf );
	virtual bool UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf );
	virtual bool UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf );
	virtual void OnUnserializationFinished( CDmAttribute *pAttribute );
};


//-----------------------------------------------------------------------------
// Memory pool useds for CDmAttribute data
// Over 8 bytes, use the small-block allocator (it aligns to 16 bytes)
//-----------------------------------------------------------------------------
CUtlMemoryPool g_DataAlloc4( sizeof( CDmAttribute ), 4, CUtlMemoryPool::GROW_SLOW, "4-byte data pool" );
CUtlMemoryPool g_DataAlloc8( sizeof( CDmAttribute ), 8, CUtlMemoryPool::GROW_SLOW, "8-byte data pool" );

template< class T > void* NewData()
{
	return new typename CDmAttributeInfo< T >::StorageType_t;
}

template< class T > void DeleteData( void *pData )
{
	delete reinterpret_cast< typename CDmAttributeInfo< T >::StorageType_t * >( pData );
}

#define USE_SPECIAL_ALLOCATOR( _className, _allocator )		\
	template<> void* NewData< _className >()				\
	{														\
		void* pData = _allocator.Alloc( sizeof( CDmAttributeInfo< _className >::StorageType_t ) );	\
		return ::new( pData ) CDmAttributeInfo< _className >::StorageType_t(); \
	}														\
	template<> void DeleteData<_className>( void *pData )	\
	{														\
		typedef CDmAttributeInfo< _className >::StorageType_t D;	\
		( ( D * )pData )->~D();								\
		_allocator.Free( pData );							\
	}

// make sure that the attribute data type sizes are what we think they are to choose the right allocator
struct CSizeTest
{
	CSizeTest()
	{
		// test internal value attribute sizes
		COMPILE_TIME_ASSERT( sizeof( int )			== 4 );
		COMPILE_TIME_ASSERT( sizeof( float )		== 4 );
		COMPILE_TIME_ASSERT( sizeof( bool )			<= 4 );
		COMPILE_TIME_ASSERT( sizeof( Color )		== 4 );
		COMPILE_TIME_ASSERT( sizeof( DmElementAttribute_t ) <= 8 );
		COMPILE_TIME_ASSERT( sizeof( Vector2D )		== 8 );
	}
};
static CSizeTest g_sizeTest;

// turn memdbg off temporarily so we can get at placement new
#include "tier0/memdbgoff.h"

USE_SPECIAL_ALLOCATOR( bool, g_DataAlloc4 )
USE_SPECIAL_ALLOCATOR( int, g_DataAlloc4 )
USE_SPECIAL_ALLOCATOR( float, g_DataAlloc4 )
USE_SPECIAL_ALLOCATOR( DmElementHandle_t, g_DataAlloc4 )
USE_SPECIAL_ALLOCATOR( Color, g_DataAlloc4 )
USE_SPECIAL_ALLOCATOR( Vector2D, g_DataAlloc8 )

#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// Create, destroy attribute data
//-----------------------------------------------------------------------------
template< class T >
void* CDmAttributeOp<T>::CreateAttributeData()
{
	void *pData = NewData< T >();
	CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast<T*>( pData ) );
	return pData;
}

template<> void* CDmAttributeOp< DmUnknownAttribute_t >::CreateAttributeData()
{
	// Fail if someone tries to create an AT_UNKNOWN attribute
	Assert(0);
	return NULL;
}

template< class T >
void CDmAttributeOp<T>::DestroyAttributeData( void *pData )
{
	DeleteData< T >( pData );
}


//-----------------------------------------------------------------------------
// Sets the data to a default value, no undo (used for construction)
//-----------------------------------------------------------------------------
template< class T >
void CDmAttributeOp<T>::SetDefaultValue( void *pData )
{
	CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast<T*>( pData ) );
}


//-----------------------------------------------------------------------------
// Attribute type name, data size, value size
//-----------------------------------------------------------------------------
template< class T >
const char *CDmAttributeOp<T>::AttributeTypeName()
{
	return CDmAttributeInfo<T>::AttributeTypeName();
}

template< class T >
int CDmAttributeOp<T>::DataSize()
{
	return sizeof( typename CDmAttributeInfo< T >::StorageType_t );
}

template< class T >
int CDmAttributeOp<T>::ValueSize()
{
	return sizeof( T );
}


//-----------------------------------------------------------------------------
// Value-setting methods
//-----------------------------------------------------------------------------
template< class T >
void CDmAttributeOp<T>::SetToDefaultValue( CDmAttribute *pAttribute )
{
	T newValue;
	CDmAttributeInfo< T >::SetDefaultValue( newValue );
	pAttribute->SetValue( newValue );
}

template< class T >
void CDmAttributeOp<T>::SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue )
{
	Assert(0);
}

template< class T >
void CDmAttributeOp<T>::Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue )
{
	Assert(0);
}

template< class T >
void CDmAttributeOp<T>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	Assert( pAttribute->GetType() == valueType );
	if ( pAttribute->GetType() == valueType )
	{
		pAttribute->SetValue( *reinterpret_cast< const T* >( pValue ) );
	}
}

#define SET_VALUE_TYPE( _srcType )												\
	case CDmAttributeInfo< _srcType >::ATTRIBUTE_TYPE:							\
	pAttribute->SetValue( *reinterpret_cast< const _srcType* >( pValue ) );	\
	break;

template<>
void CDmAttributeOp<int>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	switch( valueType )
	{
	SET_VALUE_TYPE( int );
	SET_VALUE_TYPE( float );
	SET_VALUE_TYPE( bool );

	default:
		Assert(0);
		break;
	}
}

template<>
void CDmAttributeOp<float>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	switch( valueType )
	{
	SET_VALUE_TYPE( int );
	SET_VALUE_TYPE( float );
	SET_VALUE_TYPE( bool );

	default:
		Assert(0);
		break;
	}
}

template<>
void CDmAttributeOp<bool>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	switch( valueType )
	{
	SET_VALUE_TYPE( int );
	SET_VALUE_TYPE( float );
	SET_VALUE_TYPE( bool );

	default:
		Assert(0);
		break;			 			  
	}
}


template<>
void CDmAttributeOp<QAngle>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	switch( valueType )
	{
		SET_VALUE_TYPE( QAngle );
		SET_VALUE_TYPE( Quaternion );

	default:
		Assert(0);
		break;
	}
}

template<>
void CDmAttributeOp<Quaternion>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	switch( valueType )
	{
		SET_VALUE_TYPE( QAngle );
		SET_VALUE_TYPE( Quaternion );

	default:
		Assert(0);
		break;
	}
}


//-----------------------------------------------------------------------------
// Methods related to serialization
//-----------------------------------------------------------------------------
template< class T >
bool CDmAttributeOp<T>::SerializesOnMultipleLines()
{
	return ::SerializesOnMultipleLines< T >();
}

template< class T >
bool CDmAttributeOp<T>::SkipUnserialize( CUtlBuffer& buf )
{
	T dummy;
	::Unserialize( buf, dummy );
	return buf.IsValid();
}

template< class T >
bool CDmAttributeOp<T>::Serialize( const CDmAttribute *pAttribute, CUtlBuffer &buf )
{
	// NOTE: For this to work, the class must have a function defined of type
	// bool Serialize( CUtlBuffer &buf, T &src )
	return ::Serialize( buf, pAttribute->GetValue<T>() );
}

template< class T >
bool CDmAttributeOp<T>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
{
	// NOTE: For this to work, the class must have a function defined of type
	// bool Unserialize( CUtlBuffer &buf, T &src )

	T tempVal;
	bool bRet = ::Unserialize( buf, tempVal );

	// Don't need undo hook since this goes through SetValue route
	pAttribute->SetValue( tempVal );

	return bRet;
}

template< class T >
bool CDmAttributeOp<T>::SerializeElement( const CDmAttribute *pData, int nElement, CUtlBuffer &buf )
{
	Assert( 0 );
	return false;
}

template< class T >
bool CDmAttributeOp<T>::UnserializeElement( CDmAttribute *pData, CUtlBuffer &buf )
{
	Assert( 0 );
	return false;
}

template< class T >
bool CDmAttributeOp<T>::UnserializeElement( CDmAttribute *pData, int nElement, CUtlBuffer &buf )
{
	Assert( 0 );
	return false;
}

template< class T >
void CDmAttributeOp<T>::OnUnserializationFinished( CDmAttribute *pAttribute )
{
	CDmAttributeAccessor::OnChanged( pAttribute, false, true );
}



//-----------------------------------------------------------------------------
//
// Implementation of IDmAttributeOp for array attributes
//
//-----------------------------------------------------------------------------
template< class T >
class CDmArrayAttributeOp : public CDmAttributeOp< CUtlVector< T > >
{
	typedef typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t D;

public:
	// Inherited from IDmAttributeOp
	virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue );
	virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue );
	virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue );
	virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf );
	virtual bool SerializeElement( const CDmAttribute *pData, int nElement, CUtlBuffer &buf );
	virtual bool UnserializeElement( CDmAttribute *pData, CUtlBuffer &buf );
	virtual bool UnserializeElement( CDmAttribute *pData, int nElement, CUtlBuffer &buf );
	virtual void OnUnserializationFinished( CDmAttribute *pAttribute );

	// Other methods used by CDmaArrayBase
	CDmArrayAttributeOp() : m_pAttribute( NULL ), m_pData( NULL ) {}
	CDmArrayAttributeOp( CDmAttribute *pAttribute ) : m_pAttribute( pAttribute ), m_pData( (D*)m_pAttribute->GetAttributeData() ) {}

	// Count
	int		Count() const;

	// Insertion
	int		AddToTail( const T& src );
	int		InsertBefore( int elem, const T& src );
	int		InsertMultipleBefore( int elem, int num );

	// Removal
	void	FastRemove( int elem );
	void	Remove( int elem );
	void	RemoveAll();
	void	RemoveMultiple( int elem, int num );
	void	Purge();

	// Element Modification
	void	Set( int i, const T& value );
	void	SetMultiple( int i, int nCount, const T* pValue );
	void	Swap( int i, int j );

	// Copy related methods
	void	CopyArray( const T *pArray, int size );
	void	SwapArray( CUtlVector< T >& src );	// Performs a pointer swap

	void	OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences = true );
	void	OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem );

private:
	bool	ShouldInsertElement( const T& src );
	bool	ShouldInsert( const T& src );
	void	PerformCopyArray( const T *pArray, int nCount );
	D& Data() { return *m_pData; }
	const D& Data() const { return *m_pData; }

	CDmAttribute *m_pAttribute;
	D* m_pData;
};


//-----------------------------------------------------------------------------
//
// Undo-related classes
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Undo attribute name change
//-----------------------------------------------------------------------------
class CUndoAttributeRenameElement : public CUndoElement
{
	typedef CUndoElement BaseClass;

public:
	CUndoAttributeRenameElement( CDmAttribute *pAttribute, const char *newName )
		: BaseClass( "CUndoAttributeRenameElement" )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
		m_hOwner = pAttribute->GetOwner()->GetHandle();
		m_symAttributeOld = pAttribute->GetName();
		m_symAttributeNew = newName;
	}

	virtual void Undo()
	{
		CDmElement *pOwner = GetOwner();
		if ( pOwner )
		{
			pOwner->RenameAttribute( m_symAttributeNew.String(), m_symAttributeOld.String() );
		}
	}

	virtual void Redo()
	{
		CDmElement *pOwner = GetOwner();
		if ( pOwner )
		{
			pOwner->RenameAttribute( m_symAttributeOld.String(), m_symAttributeNew.String() );
		}
	}

	virtual const char	*GetDesc()
	{
		static char buf[ 128 ];

		const char *base = BaseClass::GetDesc();
		Q_snprintf( buf, sizeof( buf ), "%s (%s -> %s)", base, m_symAttributeOld.String(), m_symAttributeNew.String() );
		return buf;
	}

private:
	CDmElement *GetOwner()
	{
		return g_pDataModel->GetElement( m_hOwner );
	}

	CUtlSymbol				m_symAttributeOld;
	CUtlSymbol				m_symAttributeNew;
	DmElementHandle_t		m_hOwner;
};

//-----------------------------------------------------------------------------
// Undo single-valued attribute value changed
//-----------------------------------------------------------------------------
template< class T >
class CUndoAttributeSetValueElement : public CUndoElement
{
	typedef CUndoElement BaseClass;
public:
	CUndoAttributeSetValueElement( CDmAttribute *pAttribute, const T &newValue )
		: BaseClass( "CUndoAttributeSetValueElement" )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
		m_hOwner = pAttribute->GetOwner()->GetHandle();
		m_OldValue = pAttribute->GetValue<T>();
		m_Value = newValue;
		m_symAttribute = pAttribute->GetNameSymbol( );
	}

	CDmElement *GetOwner()
	{
		return g_pDataModel->GetElement( m_hOwner );
	}

	virtual void Undo()
	{
		CDmAttribute *pAttribute = GetAttribute();
		if ( pAttribute && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
		{
			pAttribute->SetValue<T>( m_OldValue );
		}
	}
	virtual void Redo()
	{
		CDmAttribute *pAttribute = GetAttribute();
		if ( pAttribute && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
		{
			pAttribute->SetValue<T>( m_Value );
		}
	}

	virtual const char	*GetDesc()
	{
		static char buf[ 128 ];

		const char *base = BaseClass::GetDesc();
		CDmAttribute *pAtt = GetAttribute();
		CUtlBuffer serialized( 0, 0, CUtlBuffer::TEXT_BUFFER );
		if ( pAtt && pAtt->GetType() != AT_ELEMENT )
		{
			::Serialize( serialized, m_Value );
		}
		Q_snprintf( buf, sizeof( buf ), "%s(%s) = %s", base, g_pDataModel->GetString( m_symAttribute ), serialized.Base() ? (const char*)serialized.Base() : "\"\"" );
		return buf;
	}

private:
	CDmAttribute *GetAttribute()
	{
		CDmElement *pOwner = GetOwner();
		if ( pOwner )
		{
			const char *pAttributeName = g_pDataModel->GetString( m_symAttribute );
			return pOwner->GetAttribute( pAttributeName );
		}
		return NULL;
	}

	typedef T StorageType_t;

	CUtlSymbol			m_symAttribute;
	DmElementHandle_t	m_hOwner;
	StorageType_t		m_OldValue;
	StorageType_t		m_Value;
};



//-----------------------------------------------------------------------------
// Base undo for array attributes
//-----------------------------------------------------------------------------
template< class T >
class CUndoAttributeArrayBase : public CUndoElement
{
	typedef CUndoElement BaseClass;

public:
	CUndoAttributeArrayBase( CDmAttribute *pAttribute, const char *pUndoName ) : BaseClass( pUndoName )
	{
		m_hOwner = pAttribute->GetOwner()->GetHandle();
		m_symAttribute = pAttribute->GetNameSymbol( );
	}

protected:
	typedef typename CDmAttributeUndoStorageType< T >::UndoStorageType StorageType_t;

	CDmElement *GetOwner()
	{
		return g_pDataModel->GetElement( m_hOwner );
	}

	const char *GetAttributeName()
	{
		return g_pDataModel->GetString( m_symAttribute );
	}

	CDmAttribute *GetAttribute()
	{
		const char *pAttributeName = GetAttributeName();
		CDmElement *pOwner = GetOwner();
		if ( pOwner )
			return pOwner->GetAttribute( pAttributeName );
		Assert( 0 );
		return NULL;
	}

private:
	CUtlSymbol				m_symAttribute;
	DmElementHandle_t		m_hOwner;
};


//-----------------------------------------------------------------------------
// Undo for setting a single element
//-----------------------------------------------------------------------------
template< class T >
class CUndoArrayAttributeSetValueElement : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;

public:
	CUndoArrayAttributeSetValueElement( CDmAttribute *pAttribute, int slot, const T &newValue ) : 
		BaseClass( pAttribute, "CUndoArrayAttributeSetValueElement" ),
		m_nSlot( slot )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );

		CDmrArray<T> array( pAttribute );
		m_OldValue = array[ slot ];
		m_Value = newValue;
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.Set( m_nSlot, m_OldValue );
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.Set( m_nSlot, m_Value );
		}
	}

private:
	int				m_nSlot;
	typename CUndoAttributeArrayBase<T>::StorageType_t	m_OldValue;
	typename CUndoAttributeArrayBase<T>::StorageType_t	m_Value;
};


//-----------------------------------------------------------------------------
// Undo for setting a multiple elements
//-----------------------------------------------------------------------------
template< class T >
class CUndoArrayAttributeSetMultipleValueElement : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;

public:
	CUndoArrayAttributeSetMultipleValueElement( CDmAttribute *pAttribute, int nSlot, int nCount, const T *pNewValue ) : 
		BaseClass( pAttribute, "CUndoArrayAttributeSetMultipleValueElement" ), 
		m_nSlot( nSlot ), m_nCount( nCount )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
		m_pOldValue = new typename CUndoAttributeArrayBase<T>::StorageType_t[nCount];
		m_pValue    = new typename CUndoAttributeArrayBase<T>::StorageType_t[nCount];

		CDmrArray<T> array( pAttribute );
		for ( int i = 0; i < nCount; ++i )
		{
			m_pOldValue[i] = array[ nSlot+i ];
			m_pValue[i] = pNewValue[ i ];
		}
	}

	~CUndoArrayAttributeSetMultipleValueElement()
	{
		// this is a hack necessitated by MSVC's lack of partially specialized member template support
		// (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
		// anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
		if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
		{
			DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
			for ( int i = 0; i < m_nCount; ++i )
			{
				m_pOldValue[ i ] = m_pValue[ i ] = *( T* )&value;
			}
		}

		delete[] m_pOldValue;
		delete[] m_pValue;
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			for ( int i = 0; i < m_nCount; ++i )
			{
				array.Set( m_nSlot+i, m_pOldValue[i] );
			}
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			for ( int i = 0; i < m_nCount; ++i )
			{
				array.Set( m_nSlot+i, m_pValue[i] );
			}
		}
	}

private:
	int				m_nSlot;
	int				m_nCount;
	typename CUndoAttributeArrayBase<T>::StorageType_t	*m_pOldValue;
	typename CUndoAttributeArrayBase<T>::StorageType_t	*m_pValue;
};


//-----------------------------------------------------------------------------
//
// Implementation Undo for CDmAttributeTyped
//
//-----------------------------------------------------------------------------
template< class T >
class CUndoAttributeArrayInsertBefore : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;
public:
	CUndoAttributeArrayInsertBefore( CDmAttribute *pAttribute, int slot, int count = 1 ) : 
		BaseClass( pAttribute, "CUndoAttributeArrayInsertBefore" ),
		m_nIndex( slot ), m_nCount( count )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.RemoveMultiple( m_nIndex, m_nCount );
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			T defaultVal;
			CDmAttributeInfo<T>::SetDefaultValue( defaultVal );

			array.InsertMultipleBefore( m_nIndex, m_nCount );
			for( int i = 0; i < m_nCount; ++i )
			{
				array.Set( m_nIndex + i, defaultVal );
			}
		}
	}

private:
	int	m_nIndex;
	int m_nCount;
};


//-----------------------------------------------------------------------------
//
// Implementation Undo for inserting a copy
//
//-----------------------------------------------------------------------------
template< class T >
class CUndoAttributeArrayInsertCopyBefore : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;

public:
	CUndoAttributeArrayInsertCopyBefore( CDmAttribute *pAttribute, int slot, const T& newValue ) : 
		BaseClass( pAttribute, "CUndoAttributeArrayInsertCopyBefore" ),
		m_nIndex( slot ),
		m_newValue( newValue )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.Remove( m_nIndex );
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.InsertBefore( m_nIndex, m_newValue );
		}
	}

private:
	int				m_nIndex;
	typename CUndoAttributeArrayBase<T>::StorageType_t	m_newValue;
};


//-----------------------------------------------------------------------------
//
// Implementation Undo for remove
//
//-----------------------------------------------------------------------------
template< class T >
class CUndoAttributeArrayRemoveElement : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;

public:
	CUndoAttributeArrayRemoveElement( CDmAttribute *pAttribute, bool fastRemove, int elem, int count ) : 
		BaseClass( pAttribute, "CUndoAttributeArrayRemoveElement" ),
		m_bFastRemove( fastRemove ), m_nIndex( elem ), m_nCount( count )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
		Assert( m_nCount >= 1 );
		// If it's fastremove, count must == 1
		Assert( !m_bFastRemove || m_nCount == 1 );
		CDmrArray< T > array( pAttribute );
		Assert( array.IsValid() );
		for ( int i = 0 ; i < m_nCount; ++i )
		{
			m_OldValues.AddToTail( array[ elem + i ] );
		}
	}

	~CUndoAttributeArrayRemoveElement()
	{
		// this is a hack necessitated by MSVC's lack of partially specialized member template support
		// (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
		// anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
		if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
		{
			DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
			for ( int i = 0; i < m_nCount; ++i )
			{
				m_OldValues[ i ] = *( T* )&value;
			}
			m_OldValues.RemoveAll();
		}
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			if ( m_bFastRemove )
			{
				Assert( m_nCount == 1 );
				Assert( m_OldValues.Count() == 1 );

				if ( array.Count() > m_nIndex )
				{
					// Get value at previous index (it was moved down from the "end" before
					T m_EndValue = array.Get( m_nIndex );

					// Restore previous value
					array.Set( m_nIndex, m_OldValues[ 0 ] );

					// Put old value back to end of array
					array.AddToTail( m_EndValue );
				}
				else
				{
					Assert( array.Count() == m_nIndex );
					array.AddToTail( m_OldValues[ 0 ] );
				}
			}
			else
			{
				int insertPos = m_nIndex;
				for ( int i = 0; i < m_nCount; ++i )
				{
					array.InsertBefore( insertPos++, m_OldValues[ i ] );
				}
			}
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			if ( m_bFastRemove )
			{
				Assert( m_nCount == 1 );
				Assert( m_OldValues.Count() == 1 );

				array.FastRemove( m_nIndex );
			}
			else
			{
				array.RemoveMultiple( m_nIndex, m_nCount );
			}
		}
	}

	virtual const char *GetDesc()
	{
		static char buf[ 128 ];

		const char *base = BaseClass::GetDesc();
		Q_snprintf( buf, sizeof( buf ), "%s (%s) = remove( pos %i, count %i )", base, this->GetAttributeName(), m_nIndex, m_nCount );
		return buf;
	}

private:	
	bool						m_bFastRemove;
	int							m_nIndex;
	int							m_nCount;
	CUtlVector< typename CUndoAttributeArrayBase<T>::StorageType_t >	m_OldValues;
};


template< class T >
class CUndoAttributeArrayCopyAllElement : public CUndoAttributeArrayBase<T>
{
	typedef CUndoAttributeArrayBase<T> BaseClass;
public:
	CUndoAttributeArrayCopyAllElement( CDmAttribute *pAttribute, const T *pNewValues, int nNewSize, bool purgeOnRemove = false )
		: BaseClass( pAttribute, "CUndoAttributeArrayCopyAllElement" ),
		m_bPurge( purgeOnRemove )
	{
		Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
		CDmrArray< T > att( pAttribute );
		Assert( att.IsValid() );

		if ( pNewValues != NULL && nNewSize > 0 )
		{
			m_pNewValues = new typename CUndoAttributeArrayBase<T>::StorageType_t[ nNewSize ];
			for ( int i = 0; i < nNewSize; ++i )
			{
				m_pNewValues[ i ] = pNewValues[ i ];
			}
			m_nNewSize = nNewSize;
		}
		else
		{
			m_pNewValues = NULL;
			m_nNewSize = 0;
		}

		int nOldSize = att.Count();
		const T *pOldValues = att.Base();
		if ( pOldValues != NULL && nOldSize > 0 )
		{
			m_pOldValues = new typename CUndoAttributeArrayBase<T>::StorageType_t[ nOldSize ];
			for ( int i = 0; i < nOldSize; ++i )
			{
				m_pOldValues[ i ] = pOldValues[ i ];
			}
			m_nOldSize = nOldSize;
		}
		else
		{
			m_pOldValues = NULL;
			m_nOldSize = 0;
		}
	}

	~CUndoAttributeArrayCopyAllElement()
	{
		// this is a hack necessitated by MSVC's lack of partially specialized member template support
		// (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
		// anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
		if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
		{
			DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
			for ( int i = 0; i < m_nOldSize; ++i )
			{
				m_pOldValues[ i ] = *( T* )&value;
			}
			for ( int i = 0; i < m_nNewSize; ++i )
			{
				m_pNewValues[ i ] = *( T* )&value;
			}
		}

		delete[] m_pOldValues;
		delete[] m_pNewValues;
	}

	virtual void Undo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.RemoveAll();
			for ( int i = 0; i < m_nOldSize; ++i )
			{
				array.AddToTail( m_pOldValues[ i ] );
			}
		}
	}

	virtual void Redo()
	{
		CDmrArray<T> array( this->GetAttribute() );
		if ( array.IsValid() )
		{
			array.RemoveAll();
			for ( int i = 0; i < m_nNewSize; ++i )
			{
				array.AddToTail( m_pNewValues[ i ] );
			}

			if ( m_bPurge )
			{
				Assert( array.Count() == 0 );
				array.Purge();
			}
		}
	}

private:
	typename CUndoAttributeArrayBase<T>::StorageType_t		*m_pOldValues;
	int					m_nOldSize;
	typename CUndoAttributeArrayBase<T>::StorageType_t		*m_pNewValues;
	int					m_nNewSize;
	bool				m_bPurge;
};



//-----------------------------------------------------------------------------
// CDmArrayAttributeOp implementation.
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Callbacks when elements are added + removed
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences )
{
	CDmElement *pOwner = m_pAttribute->GetOwner();
	if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
	{
		pOwner->OnAttributeArrayElementAdded( m_pAttribute, nFirstElem, nLastElem );
	}
}

template< > inline void CDmArrayAttributeOp< DmElementHandle_t >::OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences )
{
	CDmElement *pOwner = m_pAttribute->GetOwner();
	if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
	{
		pOwner->OnAttributeArrayElementAdded( m_pAttribute, nFirstElem, nLastElem );
	}

	if ( bUpdateElementReferences )
	{
		for ( int i = nFirstElem; i <= nLastElem; ++i )
		{
			g_pDataModelImp->OnElementReferenceAdded( Data()[ i ], m_pAttribute );
		}
	}
}

template< class T >
void CDmArrayAttributeOp<T>::OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem )
{
	CDmElement *pOwner = m_pAttribute->GetOwner();
	if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
	{
		pOwner->OnAttributeArrayElementRemoved( m_pAttribute, nFirstElem, nLastElem );
	}
}

template< > void CDmArrayAttributeOp< DmElementHandle_t >::OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem )
{
	CDmElement *pOwner = m_pAttribute->GetOwner();
	if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
	{
		pOwner->OnAttributeArrayElementRemoved( m_pAttribute, nFirstElem, nLastElem );
	}

	for ( int i = nFirstElem; i <= nLastElem; ++i )
	{
		g_pDataModelImp->OnElementReferenceRemoved( Data()[ i ], m_pAttribute );
	}
}


//-----------------------------------------------------------------------------
// Count
//-----------------------------------------------------------------------------
template< class T >
int CDmArrayAttributeOp<T>::Count() const
{
	return Data().Count();
}


//-----------------------------------------------------------------------------
// Should we insert this element into the list?
//-----------------------------------------------------------------------------
template< class T >
inline bool CDmArrayAttributeOp<T>::ShouldInsertElement( const T& src )
{
	return true;
}

template<> inline bool CDmArrayAttributeOp<DmElementHandle_t>::ShouldInsertElement( const DmElementHandle_t& src )
{
	// For element, we need to check that the type matches
	if ( !IsA( src, Data().m_ElementType ) )
		return false;

	if ( m_pAttribute->IsFlagSet( FATTRIB_NODUPLICATES ) )
	{
		// See if value exists
		int idx = Data().Find( src );
		if ( idx != Data().InvalidIndex() )
			return false;
	}

	return true;
}

template< class T >
inline bool CDmArrayAttributeOp<T>::ShouldInsert( const T& src )
{
	if ( !ShouldInsertElement( src ) )
		return false;

	return m_pAttribute->MarkDirty();
}


//-----------------------------------------------------------------------------
// Insert Before
//-----------------------------------------------------------------------------
template< class T >
int CDmArrayAttributeOp<T>::InsertBefore( int elem, const T& src )
{
	if ( !ShouldInsert( src ) )
		return Data().InvalidIndex();

	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayInsertCopyBefore<T> *pUndo = new CUndoAttributeArrayInsertCopyBefore<T>( m_pAttribute, elem, src );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	int nIndex = Data().InsertBefore( elem, src );
	OnAttributeArrayElementAdded( nIndex, nIndex );
	m_pAttribute->OnChanged( true );
	return nIndex;
}

template< class T >
inline int CDmArrayAttributeOp<T>::AddToTail( const T& src )
{
	return InsertBefore( Data().Count(), src );
}


//-----------------------------------------------------------------------------
// Insert Multiple Before
//-----------------------------------------------------------------------------
template< class T >
int CDmArrayAttributeOp<T>::InsertMultipleBefore( int elem, int num )
{
	if ( !m_pAttribute->MarkDirty() )
		return Data().InvalidIndex();

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayInsertBefore<T> *pUndo = new CUndoAttributeArrayInsertBefore<T>( m_pAttribute, elem, num );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	int index = Data().InsertMultipleBefore( elem, num );
	for ( int i = 0; i < num; ++i )
	{
		CDmAttributeInfo<T>::SetDefaultValue( Data()[ index + i ] );	
	}
	OnAttributeArrayElementAdded( index, index + num - 1 );
	m_pAttribute->OnChanged( true );
	return index;
}


//-----------------------------------------------------------------------------
// Removal
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::FastRemove( int elem )
{
	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, true, elem, 1 );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( elem, elem );
	Data().FastRemove( elem );
	m_pAttribute->OnChanged( true );
}

template< class T >
void CDmArrayAttributeOp<T>::Remove( int elem )
{
	if ( !Data().IsValidIndex( elem ) )
		return;

	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, false, elem, 1 );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( elem, elem );
	Data().Remove( elem );
	m_pAttribute->OnChanged( true );
}

template< class T >
void CDmArrayAttributeOp<T>::RemoveAll()
{
	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, NULL, 0 );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
	Data().RemoveAll();
	m_pAttribute->OnChanged( true );
}

template< class T >
void CDmArrayAttributeOp<T>::RemoveMultiple( int elem, int num )
{
	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, false, elem, num );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( elem, elem + num - 1 );
	Data().RemoveMultiple( elem, num );
	m_pAttribute->OnChanged( true );
}

// Memory deallocation
template< class T >
void CDmArrayAttributeOp<T>::Purge()
{
	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, NULL, true );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
	Data().Purge();
	m_pAttribute->OnChanged( true );
}


//-----------------------------------------------------------------------------
// Copy Array
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::PerformCopyArray( const T *pArray, int nCount )
{
	Data().CopyArray( pArray, nCount );
}

template<> void CDmArrayAttributeOp<DmElementHandle_t>::PerformCopyArray( const DmElementHandle_t *pArray, int nCount )
{
	Data().RemoveAll();
	for ( int i = 0; i < nCount; ++i )
	{
		if ( ShouldInsertElement( pArray[ i ] ) )
		{
			Data().AddToTail( pArray[ i ] );
		}
	}
}

template< class T >
void CDmArrayAttributeOp<T>::CopyArray( const T *pArray, int nCount )
{
	if ( Data().Base() == pArray )
	{
		int nCurrentCount = Data().Count();
		if ( nCurrentCount > nCount )
		{
			RemoveMultiple( nCount, nCurrentCount - nCount );
		}
		else if ( nCurrentCount < nCount )
		{
			InsertMultipleBefore( nCurrentCount, nCount - nCurrentCount );
		}
		return;
	}

	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, pArray, nCount );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
	PerformCopyArray( pArray, nCount );
	OnAttributeArrayElementAdded( 0, Data().Count() - 1 );
	m_pAttribute->OnChanged( true );
}


//-----------------------------------------------------------------------------
// Swap Array
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::SwapArray( CUtlVector< T >& src )
{
	// this is basically just a faster version of CopyArray
	// the end result (for purposes of undo) are the same
	// but there's no copy - just a pointer/etc swap
	if ( !m_pAttribute->MarkDirty() )
		return;

	// UNDO HOOK
	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, src.Base(), src.Count() );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
	Data().Swap( src );
	OnAttributeArrayElementAdded( 0, Data().Count() - 1 );
	m_pAttribute->OnChanged( true );
}


template< > void CDmArrayAttributeOp<DmElementHandle_t>::SwapArray( CUtlVector< DmElementHandle_t >& src )
{
	// This feature doesn't work for elements..
	// Can't do it owing to typesafety reasons as well as supporting the NODUPLICATES feature.
	Assert( 0 );
}


//-----------------------------------------------------------------------------
// Set value
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::Set( int i, const T& value )
{
	if ( i < 0 || i >= Data().Count() )
	{
		Assert( !"CDmAttributeArray<T>::Set out of range value!\n" );
		return;
	}

	// Don't bother doing anything if the attribute is equal
	if ( IsAttributeEqual( Data()[i], value ) )
		return;

	if ( !ShouldInsert( value ) )
		return;

	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoArrayAttributeSetValueElement<T> *pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, i, value );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( i, i ); 
	Data()[i] = value;
	OnAttributeArrayElementAdded( i, i ); 
	m_pAttribute->OnChanged( false );
}

template< class T >
void CDmArrayAttributeOp<T>::Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue )
{
	if ( valueType == ArrayTypeToValueType( pAttribute->GetType() ) )
	{
		// This version is in IDmAttributeOp
		CDmArrayAttributeOp< T > array( pAttribute );
		array.Set( i, *(const T*)pValue );
	}
}


//-----------------------------------------------------------------------------
// Set multiple values
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::SetMultiple( int i, int nCount, const T* pValue )
{
	if ( i < 0 || ( i+nCount ) > Data().Count() )
	{
		AssertMsg( 0, "CDmAttributeArray<T>::SetMultiple out of range value!\n" );
		return;
	}

	// Test for equality
	bool bEqual = true;
	for ( int j = 0; j < nCount; ++j )
	{
		if ( !IsAttributeEqual( Data()[i+j], pValue[j] ) )
		{
			bEqual = false;
			break;
		}
	}
	if ( bEqual )
		return;

	if ( !m_pAttribute->MarkDirty() )
		return;

	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoArrayAttributeSetMultipleValueElement<T> *pUndo = new CUndoArrayAttributeSetMultipleValueElement<T>( m_pAttribute, i, nCount, pValue );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();
	OnAttributeArrayElementRemoved( i, i+nCount-1 ); 
	for ( int j = 0; j < nCount; ++j )
	{
		if ( ShouldInsertElement( pValue[j] ) )
		{
			Data()[i+j] = pValue[j];
		}
	}
	OnAttributeArrayElementAdded( i, i+nCount-1 ); 
	m_pAttribute->OnChanged( false );
}

template< class T >
void CDmArrayAttributeOp<T>::SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue )
{
	if ( valueType == ArrayTypeToValueType( pAttribute->GetType() ) )
	{
		// This version is in IDmAttributeOp
		CDmArrayAttributeOp< T > array( pAttribute );
		array.SetMultiple( i, nCount, (const T*)pValue );
	}
}


//-----------------------------------------------------------------------------
// Version of SetValue that's in IDmAttributeOp
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
{
	Assert( pAttribute->GetType() == valueType );
	if ( pAttribute->GetType() == valueType )
	{
		CDmArrayAttributeOp<T> accessor( pAttribute );
		const CUtlVector<T>* pArray = reinterpret_cast< const CUtlVector<T>* >( pValue );
		accessor.CopyArray( pArray->Base(), pArray->Count() );
	}
}


//-----------------------------------------------------------------------------
// Swap
//-----------------------------------------------------------------------------
template< class T >
void CDmArrayAttributeOp<T>::Swap( int i, int j )
{
	if ( i == j )
		return;

	// TODO - define Swap<T> for all attribute types to make swapping strings 
	// and voids fast (via pointer swaps, rather than 3 copies!)
	T vk = Data()[ i ];
	if ( IsAttributeEqual( vk, Data()[j] ) )
		return;

	if ( !m_pAttribute->MarkDirty() )
		return;

	if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
	{
		CUndoArrayAttributeSetValueElement<T> *pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, i, Data()[ j ] );
		g_pDataModel->AddUndoElement( pUndo );
		pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, j, vk );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_pAttribute->PreChanged();

	OnAttributeArrayElementRemoved( i, i ); 
	Data()[i] = Data()[j];
	OnAttributeArrayElementAdded( i, i ); 

	OnAttributeArrayElementRemoved( j, j ); 
	Data()[j] = vk;
	OnAttributeArrayElementAdded( j, j ); 

	m_pAttribute->OnChanged( false );
}


//-----------------------------------------------------------------------------
// Methods related to serialization
//-----------------------------------------------------------------------------
template< class T >
bool CDmArrayAttributeOp<T>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
{
	if ( !pAttribute->MarkDirty() )
		return false;

	MEM_ALLOC_CREDIT_CLASS();

	CUtlVector< T > tempVal;
	bool bRet = ::Unserialize( buf, tempVal );

	// Don't need undo hook since this goes through Swap route
	CDmArrayAttributeOp<T> accessor( pAttribute );
	accessor.SwapArray( tempVal );

	return bRet;
}

template<> bool CDmArrayAttributeOp<DmElementHandle_t>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
{
	// Need to specialize this because element handles can't use SwapArray
	// because it's incapable of doing type safety checks or looking for FATTRIB_NODUPLICATES
	if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
		return false;

	MEM_ALLOC_CREDIT_CLASS();

	CUtlVector< DmElementHandle_t > tempVal;
	bool bRet = ::Unserialize( buf, tempVal );

	// Don't need undo hook since this goes through copy route
	CDmArrayAttributeOp<DmElementHandle_t> accessor( pAttribute );
	accessor.CopyArray( tempVal.Base(), tempVal.Count() );

	return bRet;
}

// Serialization of a single element
template< class T >
bool CDmArrayAttributeOp<T>::SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf )
{
	CDmrArrayConst<T> array( pAttribute );
	return ::Serialize( buf, array[ nElement ] );
}

template< class T >
bool CDmArrayAttributeOp<T>::UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf )
{
	if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
		return false;

	MEM_ALLOC_CREDIT_CLASS();

	T temp;
	bool bReadElement = ::Unserialize( buf, temp );
	if ( bReadElement )
	{
		pAttribute->PreChanged();

		CDmArrayAttributeOp<T> accessor( pAttribute );
		accessor.AddToTail( temp );

		pAttribute->OnChanged( true );
	}
	return bReadElement;
}

template< class T >
bool CDmArrayAttributeOp<T>::UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf )
{
	if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
		return false;

	CDmrArray<T> array( pAttribute );
	if ( array.Count() <= nElement )
		return false;

	MEM_ALLOC_CREDIT_CLASS();

	pAttribute->PreChanged();
	bool bReadElement = ::Unserialize( buf, *const_cast<T*>( &array[nElement] ) );
	if ( bReadElement )
	{
		pAttribute->OnChanged();
	}
	return bReadElement;
}

template< class T >
void CDmArrayAttributeOp<T>::OnUnserializationFinished( CDmAttribute *pAttribute )
{
	CDmArrayAttributeOp<T> ref( pAttribute );
	int nCount = ref.Count();
	if ( nCount > 0 )
	{
		ref.OnAttributeArrayElementAdded( 0, nCount - 1, false );
	}
	CDmAttributeAccessor::OnChanged( pAttribute, true, true );
}


//-----------------------------------------------------------------------------
//
// CDmAttribute begins here
//
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Memory pool used for CDmAttribute
//-----------------------------------------------------------------------------
CUtlMemoryPool g_AttrAlloc( sizeof( CDmAttribute ), 32, CUtlMemoryPool::GROW_SLOW, "CDmAttribute pool" );


//-----------------------------------------------------------------------------
// Class factory
//-----------------------------------------------------------------------------

// turn memdbg off temporarily so we can get at placement new
#include "tier0/memdbgoff.h"

CDmAttribute *CDmAttribute::CreateAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName )
{
	switch( type )
	{
	case AT_UNKNOWN:
		Assert( 0 );
		return NULL;

	default:
		{
			void *pMem = g_AttrAlloc.Alloc( sizeof( CDmAttribute ) );
			return ::new( pMem ) CDmAttribute( pOwner, type, pAttributeName );
		}
	}
}

CDmAttribute *CDmAttribute::CreateExternalAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pExternalMemory )
{
	switch( type )
	{
	case AT_UNKNOWN:
		Assert( 0 );
		return NULL;

	default:
		{
			void *pMem = g_AttrAlloc.Alloc( sizeof( CDmAttribute ) );
			return ::new( pMem ) CDmAttribute( pOwner, type, pAttributeName, pExternalMemory );
		}
	}
}

void CDmAttribute::DestroyAttribute( CDmAttribute *pAttribute )
{
	if ( !pAttribute )
		return;

	switch( pAttribute->GetType() )
	{
	case AT_UNKNOWN:
		break;

	default:
		pAttribute->~CDmAttribute();

#ifdef _DEBUG
		memset( pAttribute, 0xDD, sizeof(CDmAttribute) );
#endif

		g_AttrAlloc.Free( pAttribute );
		break;
	}
}

// turn memdbg back on after using placement new
#include "tier0/memdbgon.h"


//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CDmAttribute::CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName ) :
	m_pData( NULL )
{
	Init( pOwner, type, pAttributeName );
	CreateAttributeData();
}

CDmAttribute::CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pMemory ) :
	m_pData( pMemory )
{
	Init( pOwner, type, pAttributeName );
	s_pAttrInfo[ GetType() ]->SetDefaultValue( m_pData );
	AddFlag( FATTRIB_EXTERNAL );
}


void CDmAttribute::Init( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName )
{
	// FIXME - this is just here temporarily to catch old code trying to create type and id attributes
	// this shouldn't actually be illegal, since users should be able to create attributes of whatever name they want
	Assert( V_strcmp( pAttributeName, "type" ) && V_strcmp( pAttributeName, "id" ) );

	m_pOwner = pOwner;
	m_Name = g_pDataModel->GetSymbol( pAttributeName );
	m_nFlags = type;
	m_Handle = DMATTRIBUTE_HANDLE_INVALID;
	m_pNext = NULL;
	m_hMailingList = DMMAILINGLIST_INVALID;

	switch ( type )
	{
	case AT_ELEMENT:
	case AT_ELEMENT_ARRAY:
	case AT_OBJECTID:
	case AT_OBJECTID_ARRAY:
		m_nFlags |= FATTRIB_TOPOLOGICAL;
		break;
	}
}

CDmAttribute::~CDmAttribute()
{
	switch( GetType() )
	{
	case AT_ELEMENT:
		g_pDataModelImp->OnElementReferenceRemoved( GetValue<DmElementHandle_t>(), this );
		break;
	
	case AT_ELEMENT_ARRAY:
		{
			CDmrElementArray<> array( this );
			int nElements = array.Count();
			for ( int i = 0; i < nElements; ++i )
			{
				g_pDataModelImp->OnElementReferenceRemoved( array.GetHandle( i ), this );
			}
		}
		break;
	}
	
	CleanupMailingList();
	InvalidateHandle();
	DeleteAttributeData();
}


//-----------------------------------------------------------------------------
// Creates the attribute data
//-----------------------------------------------------------------------------
void CDmAttribute::CreateAttributeData()
{
	// Free the attribute memory
	if ( !IsFlagSet( FATTRIB_EXTERNAL ) )
	{
		Assert( !m_pData );
		m_pData = s_pAttrInfo[ GetType() ]->CreateAttributeData( );
	}
}


//-----------------------------------------------------------------------------
// Deletes the attribute data
//-----------------------------------------------------------------------------
void CDmAttribute::DeleteAttributeData()
{
	// Free the attribute memory
	if ( m_pData && !IsFlagSet( FATTRIB_EXTERNAL ) )
	{
		s_pAttrInfo[ GetType() ]->DestroyAttributeData( m_pData );
		m_pData = NULL;
	}
}


//-----------------------------------------------------------------------------
// Used only in attribute element arrays
//-----------------------------------------------------------------------------
void CDmAttribute::SetElementTypeSymbol( UtlSymId_t typeSymbol )
{
	switch ( GetType() )
	{
	case AT_ELEMENT:
		{
			DmElementAttribute_t *pData = GetData< DmElementHandle_t >();
			Assert( pData->m_Handle == DMELEMENT_HANDLE_INVALID || ::IsA( pData->m_Handle, typeSymbol ) );
			pData->m_ElementType = typeSymbol;
		}
		break;

	case AT_ELEMENT_ARRAY:
		{
#ifdef _DEBUG
			CDmrElementArray<> array( this );
			if ( array.GetElementType() != UTL_INVAL_SYMBOL )
			{
				int i;
				int c = array.Count();
				for ( i = 0; i < c; ++i )
				{
					Assert( array.GetHandle( i ) == DMELEMENT_HANDLE_INVALID || ::IsA( array.GetHandle( i ), typeSymbol ) );
				}
			}
#endif

			DmElementArray_t *pData = GetArrayData< DmElementHandle_t >();
			pData->m_ElementType = typeSymbol;
		}
		break;

	default:
		Assert(0);
		break;
	}
}

UtlSymId_t CDmAttribute::GetElementTypeSymbol() const
{
	switch ( GetType() )
	{
	case AT_ELEMENT:
		return GetData< DmElementHandle_t >()->m_ElementType;

	case AT_ELEMENT_ARRAY:
		return GetArrayData< DmElementHandle_t >()->m_ElementType;

	default:
		Assert(0);
		break;
	}

	return UTL_INVAL_SYMBOL;
}


//-----------------------------------------------------------------------------
// Is modification allowed in this phase?
//-----------------------------------------------------------------------------
bool CDmAttribute::ModificationAllowed() const 
{ 
	if ( IsFlagSet( FATTRIB_READONLY ) )
		return false;

	DmPhase_t phase = g_pDmElementFramework->GetPhase();
	if ( phase == PH_EDIT )
		return true;
	if ( ( phase == PH_OPERATE ) && !IsFlagSet( FATTRIB_TOPOLOGICAL ) )
		return true;

	return false;
}

bool CDmAttribute::MarkDirty() 
{ 
	if ( !ModificationAllowed() )
	{
		Assert( 0 );
		return false;
	}

	AddFlag( FATTRIB_DIRTY | FATTRIB_OPERATOR_DIRTY );
	CDmeElementAccessor::MarkDirty( m_pOwner );

	return true;
}


//-----------------------------------------------------------------------------
// Called before and after the attribute has changed
//-----------------------------------------------------------------------------
void CDmAttribute::PreChanged()
{
	if ( IsFlagSet( FATTRIB_HAS_PRE_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
	{
		m_pOwner->PreAttributeChanged( this );
	}

	// FIXME: What about mailing lists?
}

void CDmAttribute::OnChanged( bool bArrayCountChanged, bool bIsTopological )
{
	if ( IsFlagSet( FATTRIB_HAS_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
	{
		m_pOwner->OnAttributeChanged( this );
	}

	if ( ( m_hMailingList != DMMAILINGLIST_INVALID ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
	{
		if ( !g_pDataModelImp->PostAttributeChanged( m_hMailingList, this ) )
		{
			CleanupMailingList();
		}
	}

	if ( bIsTopological || IsTopological( GetType() ) )
	{
		g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
	}
	else
	{
		g_pDataModelImp->NotifyState( bArrayCountChanged ? NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE : NOTIFY_CHANGE_ATTRIBUTE_VALUE );
	}
}


//-----------------------------------------------------------------------------
// Type conversion related methods
//-----------------------------------------------------------------------------
template< class T > bool CDmAttribute::IsTypeConvertable() const
{
	return ( CDmAttributeInfo< T >::ATTRIBUTE_TYPE == GetType() );
}

template<> bool CDmAttribute::IsTypeConvertable<bool>() const
{
	DmAttributeType_t type = GetType();
	return ( type == AT_BOOL || type == AT_INT || type == AT_FLOAT );
}

template<> bool CDmAttribute::IsTypeConvertable<int>() const
{
	DmAttributeType_t type = GetType();
	return ( type == AT_INT || type == AT_BOOL || type == AT_FLOAT );
}

template<> bool CDmAttribute::IsTypeConvertable<float>() const
{
	DmAttributeType_t type = GetType();
	return ( type == AT_FLOAT || type == AT_INT || type == AT_BOOL );
}

template<> bool CDmAttribute::IsTypeConvertable<QAngle>() const
{
	DmAttributeType_t type = GetType();
	return ( type == AT_QANGLE || type == AT_QUATERNION );
}

template<> bool CDmAttribute::IsTypeConvertable<Quaternion>() const
{
	DmAttributeType_t type = GetType();
	return ( type == AT_QUATERNION || type == AT_QANGLE);
}

template< class T > void CDmAttribute::CopyData( const T& value )
{
	*reinterpret_cast< T* >( m_pData ) = value;
}

template< class T > void CDmAttribute::CopyDataOut( T& value ) const
{
	value = *reinterpret_cast< const T* >( m_pData );
}

template<> void CDmAttribute::CopyData( const bool& value )
{
	switch( GetType() )
	{
	case AT_BOOL:
		*reinterpret_cast< bool* >( m_pData ) = value;
		break;

	case AT_INT:
		*reinterpret_cast< int* >( m_pData ) = value ? 1 : 0;
		break;

	case AT_FLOAT:
		*reinterpret_cast< float* >( m_pData ) = value ? 1.0f : 0.0f;
		break;
	}
}

template<> void CDmAttribute::CopyDataOut( bool& value ) const
{
	switch( GetType() )
	{
	case AT_BOOL:
		value = *reinterpret_cast< bool* >( m_pData );
		break;

	case AT_INT:
		value = *reinterpret_cast< int* >( m_pData ) != 0;
		break;

	case AT_FLOAT:
		value = *reinterpret_cast< float* >( m_pData ) != 0.0f;
		break;
	}
}

template<> void CDmAttribute::CopyData( const int& value )
{
	switch( GetType() )
	{
	case AT_BOOL:
		*reinterpret_cast< bool* >( m_pData ) = value != 0;
		break;

	case AT_INT:
		*reinterpret_cast< int* >( m_pData ) = value;
		break;

	case AT_FLOAT:
		*reinterpret_cast< float* >( m_pData ) = value;
		break;
	}
}

template<> void CDmAttribute::CopyDataOut( int& value ) const
{
	switch( GetType() )
	{
	case AT_BOOL:
		value = *reinterpret_cast< bool* >( m_pData ) ? 1 : 0;
		break;

	case AT_INT:
		value = *reinterpret_cast< int* >( m_pData );
		break;

	case AT_FLOAT:
		value = *reinterpret_cast< float* >( m_pData );
		break;
	}
}

template<> void CDmAttribute::CopyData( const float& value )
{
	switch( GetType() )
	{
	case AT_BOOL:
		*reinterpret_cast< bool* >( m_pData ) = value != 0.0f;
		break;

	case AT_INT:
		*reinterpret_cast< int* >( m_pData ) = value;
		break;

	case AT_FLOAT:
		*reinterpret_cast< float* >( m_pData ) = value;
		break;
	}
}

template<> void CDmAttribute::CopyDataOut( float& value ) const
{
	switch( GetType() )
	{
	case AT_BOOL:
		value = *reinterpret_cast< bool* >( m_pData ) ? 1.0f : 0.0f;
		break;

	case AT_INT:
		value = *reinterpret_cast< int* >( m_pData );
		break;

	case AT_FLOAT:
		value = *reinterpret_cast< float* >( m_pData );
		break;
	}
}

template<> void CDmAttribute::CopyData( const QAngle& value )
{
	switch( GetType() )
	{
	case AT_QANGLE:
		*reinterpret_cast< QAngle* >( m_pData ) = value;
		break;

	case AT_QUATERNION:
		{
			Quaternion qValue;
			AngleQuaternion( value, qValue );
			*reinterpret_cast< Quaternion* >( m_pData ) = qValue;
		}
		break;
	}
}

template<> void CDmAttribute::CopyDataOut( QAngle& value ) const
{
	switch( GetType() )
	{
	case AT_QANGLE:
		value = *reinterpret_cast< QAngle* >( m_pData );
		break;

	case AT_QUATERNION:
		QuaternionAngles( *reinterpret_cast< Quaternion* >( m_pData ), value );
		break;
	}
}

template<> void CDmAttribute::CopyData( const Quaternion& value )
{
	switch( GetType() )
	{
	case AT_QANGLE:
		{
			QAngle aValue;
			QuaternionAngles( value, aValue );
			*reinterpret_cast< QAngle* >( m_pData ) = aValue;
		}
		break;

	case AT_QUATERNION:
		*reinterpret_cast< Quaternion* >( m_pData ) = value;
		break;
	}
}

template<> void CDmAttribute::CopyDataOut( Quaternion& value ) const
{
	switch( GetType() )
	{
	case AT_QANGLE:
		AngleQuaternion( *reinterpret_cast< QAngle* >( m_pData ), value );
		break;

	case AT_QUATERNION:
		value = *reinterpret_cast< Quaternion* >( m_pData );
		break;
	}
}

template<> void CDmAttribute::CopyData( const DmElementHandle_t& value )
{
	g_pDataModelImp->OnElementReferenceRemoved( GetValue<DmElementHandle_t>(), this );
	*reinterpret_cast< DmElementHandle_t* >( m_pData ) = value;
	g_pDataModelImp->OnElementReferenceAdded( value, this );
}


//-----------------------------------------------------------------------------
// Should we be allowed to modify the attribute data?
//-----------------------------------------------------------------------------
template< class T > 
bool CDmAttribute::ShouldModify( const T& value )
{
	if ( !IsTypeConvertable<T>() )
		return false;

	if ( ( GetType() == CDmAttributeInfo<T>::ATTRIBUTE_TYPE ) && IsAttributeEqual( GetValue<T>(), value ) )
		return false;

	return MarkDirty();
}

template<> bool CDmAttribute::ShouldModify( const DmElementHandle_t& value )
{
	if ( !IsTypeConvertable<DmElementHandle_t>() )
		return false;

	if ( IsAttributeEqual( GetValue<DmElementHandle_t>(), value ) )
		return false;

	DmElementAttribute_t *pData = GetData<DmElementHandle_t>();
	if ( pData->m_ElementType != UTL_INVAL_SYMBOL && !::IsA( value, pData->m_ElementType ) )
		return false;

	return MarkDirty();
}


//-----------------------------------------------------------------------------
// Main entry point for single-valued SetValue
//-----------------------------------------------------------------------------
template< class T >
void CDmAttribute::SetValue( const T &value )
{
	if ( !ShouldModify( value ) )
		return;

	// UNDO Hook
	if ( g_pDataModel->UndoEnabledForElement( m_pOwner ) )
	{
		CUndoAttributeSetValueElement<T> *pUndo = new CUndoAttributeSetValueElement<T>( this, value );
		g_pDataModel->AddUndoElement( pUndo );
	}

	bool bIsBeingUnserialized = CDmeElementAccessor::IsBeingUnserialized( m_pOwner );
	if ( IsFlagSet( FATTRIB_HAS_PRE_CALLBACK ) && !bIsBeingUnserialized )
	{
		m_pOwner->PreAttributeChanged( this );
	}

	CopyData< T >( value );

	if ( !bIsBeingUnserialized )
	{
		if ( IsFlagSet( FATTRIB_HAS_CALLBACK ) )
		{
			m_pOwner->OnAttributeChanged( this );
		}

		if ( m_hMailingList != DMMAILINGLIST_INVALID )
		{
			if ( !g_pDataModelImp->PostAttributeChanged( m_hMailingList, this ) )
			{
				CleanupMailingList();
			}
		}
	}

	g_pDataModelImp->NotifyState( IsTopological( GetType() ) ? NOTIFY_CHANGE_TOPOLOGICAL : NOTIFY_CHANGE_ATTRIBUTE_VALUE );
}


//-----------------------------------------------------------------------------
// Versions that work on arrays
//-----------------------------------------------------------------------------
#define ATTRIBUTE_SET_VALUE_ARRAY( _type )										\
	template<> void CDmAttribute::SetValue( const CUtlVector< _type >& value )	\
	{																			\
		CDmArrayAttributeOp< _type > accessor( this );							\
		accessor.CopyArray( value.Base(), value.Count() );						\
	}

void CDmAttribute::SetValue( const CDmAttribute *pAttribute )
{
	s_pAttrInfo[ GetType() ]->SetValue( this, pAttribute->GetType(), pAttribute->GetAttributeData() );
}

void CDmAttribute::SetValue( CDmAttribute *pAttribute )
{
	s_pAttrInfo[ GetType() ]->SetValue( this, pAttribute->GetType(), pAttribute->GetAttributeData() );
}

void CDmAttribute::SetValue( DmAttributeType_t valueType, const void *pValue )
{
	s_pAttrInfo[ GetType() ]->SetValue( this, valueType, pValue );
}


//-----------------------------------------------------------------------------
// Sets the attribute to its default value based on its type
//-----------------------------------------------------------------------------
void CDmAttribute::SetToDefaultValue()
{
	s_pAttrInfo[ GetType() ]->SetToDefaultValue( this );
}


//-----------------------------------------------------------------------------
// Convert to and from string
//-----------------------------------------------------------------------------
void CDmAttribute::SetValueFromString( const char *pValue )
{
	switch ( GetType() )
	{
	case AT_STRING:
		SetValue( pValue );
		break;

	default:
		{
			int nLen = pValue ? Q_strlen( pValue ) : 0;
			if ( nLen == 0 )
			{
				SetToDefaultValue();
				break;
			}

			CUtlBuffer buf( pValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
			if ( !Unserialize( buf ) )
			{
				SetToDefaultValue();
			}
		}
		break;
	}
}

const char *CDmAttribute::GetValueAsString( char *pBuffer, size_t nBufLen ) const
{
	Assert( pBuffer );
	CUtlBuffer buf( pBuffer, nBufLen, CUtlBuffer::TEXT_BUFFER );
	Serialize( buf );
	return pBuffer;
}


//-----------------------------------------------------------------------------
// Name, type
//-----------------------------------------------------------------------------
const char* CDmAttribute::GetTypeString() const
{
	return ::GetTypeString( GetType() );
}

const char *GetTypeString( DmAttributeType_t type )
{
	if ( ( type >= 0 ) && ( type < AT_TYPE_COUNT ) )
		return s_pAttrInfo[ type ]->AttributeTypeName();
	return "unknown";
}


void CDmAttribute::SetName( const char *pNewName )
{
	if ( m_pOwner->HasAttribute( pNewName ) && Q_stricmp( GetName(), pNewName ) )
	{
		Warning( "Tried to rename from '%s' to '%s', but '%s' already exists\n",
			GetName(), pNewName, pNewName );
		return;
	}

	if ( !MarkDirty() )
		return;

	// UNDO Hook
	if ( g_pDataModel->UndoEnabledForElement( m_pOwner ) )
	{
		CUndoAttributeRenameElement *pUndo = new CUndoAttributeRenameElement( this, pNewName );
		g_pDataModel->AddUndoElement( pUndo );
	}

	m_Name = g_pDataModel->GetSymbol( pNewName );
	g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
}


//-----------------------------------------------------------------------------
// Serialization
//-----------------------------------------------------------------------------
bool CDmAttribute::SerializesOnMultipleLines() const
{
	return s_pAttrInfo[ GetType() ]->SerializesOnMultipleLines();
}

bool CDmAttribute::Serialize( CUtlBuffer &buf ) const
{
	return s_pAttrInfo[ GetType() ]->Serialize( this, buf );
}

bool CDmAttribute::Unserialize( CUtlBuffer &buf )
{
	return s_pAttrInfo[ GetType() ]->Unserialize( this, buf );
}

bool CDmAttribute::SerializeElement( int nElement, CUtlBuffer &buf ) const
{
	return s_pAttrInfo[ GetType() ]->SerializeElement( this, nElement, buf );
}

bool CDmAttribute::UnserializeElement( CUtlBuffer &buf )
{
	return s_pAttrInfo[ GetType() ]->UnserializeElement( this, buf );
}

bool CDmAttribute::UnserializeElement( int nElement, CUtlBuffer &buf )
{
	return s_pAttrInfo[ GetType() ]->UnserializeElement( this, nElement, buf );
}

// Called by elements after unserialization of their attributes is complete
void CDmAttribute::OnUnserializationFinished()
{
	return s_pAttrInfo[ GetType() ]->OnUnserializationFinished( this );
}



//-----------------------------------------------------------------------------
// Methods related to attribute change notification
//-----------------------------------------------------------------------------
void CDmAttribute::CleanupMailingList()
{
	if ( m_hMailingList != DMMAILINGLIST_INVALID )
	{
		g_pDataModelImp->DestroyMailingList( m_hMailingList );
		m_hMailingList = DMMAILINGLIST_INVALID;
	}
}

void CDmAttribute::NotifyWhenChanged( DmElementHandle_t h, bool bNotify )
{
	if ( bNotify )
	{
		if ( m_hMailingList == DMMAILINGLIST_INVALID )
		{
			m_hMailingList = g_pDataModelImp->CreateMailingList();
		}
		g_pDataModelImp->AddElementToMailingList( m_hMailingList, h );
		return;
	}

	if ( m_hMailingList != DMMAILINGLIST_INVALID )
	{
		if ( !g_pDataModelImp->RemoveElementFromMailingList( m_hMailingList, h ) )
		{
			CleanupMailingList();
		}
	}
}


//-----------------------------------------------------------------------------
// Get the attribute/create an attribute handle
//-----------------------------------------------------------------------------
DmAttributeHandle_t CDmAttribute::GetHandle( bool bCreate )
{
	if ( (m_Handle == DMATTRIBUTE_HANDLE_INVALID) && bCreate )
	{
		m_Handle = g_pDataModelImp->AcquireAttributeHandle( this );
	}

	Assert( (m_Handle == DMATTRIBUTE_HANDLE_INVALID) || g_pDataModel->IsAttributeHandleValid( m_Handle ) );
	return m_Handle;
}

void CDmAttribute::InvalidateHandle()
{
	g_pDataModelImp->ReleaseAttributeHandle( m_Handle );
	m_Handle = DMATTRIBUTE_HANDLE_INVALID;
}


//-----------------------------------------------------------------------------
// Memory usage estimations
//-----------------------------------------------------------------------------
bool HandleCompare( const DmElementHandle_t &a, const DmElementHandle_t &b )
{
	return a == b;
}

unsigned int HandleHash( const DmElementHandle_t &h )
{
	return (unsigned int)h;
}

int CDmAttribute::EstimateMemoryUsage( TraversalDepth_t depth ) const
{
	CUtlHash< DmElementHandle_t > visited( 1024, 0, 0, HandleCompare, HandleHash );
	return EstimateMemoryUsageInternal( visited, depth, 0 ) ;
}

int CDmAttribute::EstimateMemoryUsageInternal( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) const
{
	int nOverhead = sizeof( *this );
	int nAttributeDataSize = s_pAttrInfo[ GetType() ]->DataSize();
	int nTotalMemory = nOverhead + nAttributeDataSize;
	int nAttributeExtraDataSize = 0;

	if ( IsArrayType( GetType() ) )
	{
		CDmrGenericArrayConst array( this );
		int nCount = array.Count();
		nAttributeExtraDataSize = nCount * s_pAttrInfo[ GetType() ]->ValueSize();	// Data in the UtlVector
		int nMallocOverhead = ( array.Count() == 0 ) ? 0 : 8;	// malloc overhead inside the vector
		nOverhead += nMallocOverhead;
		nTotalMemory += nAttributeExtraDataSize + nMallocOverhead;
	}

	if ( pCategories )
	{
		++pCategories[MEMORY_CATEGORY_ATTRIBUTE_COUNT];
		pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += nAttributeDataSize + nAttributeExtraDataSize;
		pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += nOverhead;
		if ( !IsDataInline() )
		{
			pCategories[MEMORY_CATEGORY_OUTER] -= nAttributeDataSize;
			Assert( pCategories[MEMORY_CATEGORY_OUTER] >= 0 );
			nTotalMemory -= nAttributeDataSize;
		}
	}

	switch ( GetType() )
	{
	case AT_STRING:
		{
			const CUtlString &value = GetValue<CUtlString>();
			if ( pCategories )
			{
				pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += value.Length() + 1;
				pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
			}
			return nTotalMemory + value.Length() + 1 + 8; // string's length skips trailing null
		}

	case AT_STRING_ARRAY:
		{
			const CUtlVector< CUtlString > &array = GetValue< CUtlVector< CUtlString > >( );
			for ( int i = 0; i < array.Count(); ++i )
			{
				int nStrLen = array[ i ].Length() + 1;
				if ( pCategories )
				{
					pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += nStrLen;
					pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
				}
				nTotalMemory += nStrLen + 8; // string's length skips trailing null
			}
			return nTotalMemory;
		}

	case AT_VOID:
		{
			const CUtlBinaryBlock &value = GetValue< CUtlBinaryBlock >();
			if ( pCategories )
			{
				pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += value.Length();
				pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
			}
			return nTotalMemory + value.Length() + 8;
		}

	case AT_VOID_ARRAY:
		{
			const CUtlVector< CUtlBinaryBlock > &array = GetValue< CUtlVector< CUtlBinaryBlock > >();
			for ( int i = 0; i < array.Count(); ++i )
			{
				if ( pCategories )
				{
					pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += array[ i ].Length();
					pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
				}
				nTotalMemory += array[ i ].Length() + 8;
			}
			return nTotalMemory;
		}

	case AT_ELEMENT:
		if ( ShouldTraverse( this, depth ) )
		{
			CDmElement *pElement = GetValueElement<CDmElement>();
			if ( pElement )
			{
				nTotalMemory += CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, depth, pCategories );
			}
		}
		return nTotalMemory;

	case AT_ELEMENT_ARRAY:
		if ( ShouldTraverse( this, depth ) )
		{
			CDmrElementArrayConst<> array( this );
			for ( int i = 0; i < array.Count(); ++i )
			{
				CDmElement *pElement = array[ i ];
				if ( pElement )
				{
					nTotalMemory += CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, depth, pCategories );
				}
			}
		}
		return nTotalMemory;
	}

	return nTotalMemory;
}


//-----------------------------------------------------------------------------
//
// CDmaArrayBase starts here
//
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
template< class T, class B >
CDmaArrayConstBase<T,B>::CDmaArrayConstBase( )
{
	m_pAttribute = NULL;
}


//-----------------------------------------------------------------------------
// Search
//-----------------------------------------------------------------------------
template< class T, class B >
int CDmaArrayConstBase<T,B>::Find( const T &value ) const
{
	return this->Value().Find( value );
}


//-----------------------------------------------------------------------------
// Insertion
//-----------------------------------------------------------------------------
template< class T, class B >
int CDmaArrayBase<T,B>::AddToTail()
{
	T defaultVal;
	CDmAttributeInfo<T>::SetDefaultValue( defaultVal );	
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertBefore( this->Value().Count(), defaultVal );
}

template< class T, class B >
int	CDmaArrayBase<T,B>::InsertBefore( int elem )
{
	T defaultVal;
	CDmAttributeInfo<T>::SetDefaultValue( defaultVal );	
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertBefore( elem, defaultVal );
}

template< class T, class B >
int	CDmaArrayBase<T,B>::AddToTail( const T& src )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertBefore( this->Value().Count(), src );
}

template< class T, class B >
int	CDmaArrayBase<T,B>::InsertBefore( int elem, const T& src )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertBefore( elem, src );
}

template< class T, class B >
int	CDmaArrayBase<T,B>::AddMultipleToTail( int num )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertMultipleBefore( this->Value().Count(), num );
}

template< class T, class B >
int CDmaArrayBase<T,B>::InsertMultipleBefore( int elem, int num )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.InsertMultipleBefore( elem, num );
}

template< class T, class B >
void CDmaArrayBase<T,B>::EnsureCount( int num )
{
	int nCurrentCount = this->Value().Count();
	if ( nCurrentCount < num )
	{
		AddMultipleToTail( num - nCurrentCount );
	}
}


//-----------------------------------------------------------------------------
// Element modification
//-----------------------------------------------------------------------------
template< class T, class B >
void CDmaArrayBase<T,B>::Set( int i, const T& value )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	return accessor.Set( i, value );
}

template< class T, class B >
void CDmaArrayBase<T,B>::SetMultiple( int i, int nCount, const T* pValue )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.SetMultiple( i, nCount, pValue );
}

template< class T, class B >
void CDmaArrayBase<T,B>::Swap( int i, int j )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.Swap( i, j );
}

template< class T, class B >
void CDmaArrayBase<T,B>::SwapArray( CUtlVector< T > &array )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.SwapArray( array );
}


//-----------------------------------------------------------------------------
// Copy
//-----------------------------------------------------------------------------
template< class T, class B >
void CDmaArrayBase<T,B>::CopyArray( const T *pArray, int nCount )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.CopyArray( pArray, nCount );
}


//-----------------------------------------------------------------------------
// Removal
//-----------------------------------------------------------------------------
template< class T, class B >
void CDmaArrayBase<T,B>::FastRemove( int elem )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.FastRemove( elem );
}

template< class T, class B >
void CDmaArrayBase<T,B>::Remove( int elem )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.Remove( elem );
}

template< class T, class B >
void CDmaArrayBase<T,B>::RemoveAll()
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.RemoveAll();
}

template< class T, class B >
void CDmaArrayBase<T,B>::RemoveMultiple( int elem, int num )
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.RemoveMultiple( elem, num );
}


//-----------------------------------------------------------------------------
// Memory management
//-----------------------------------------------------------------------------
template< class T, class B >
void CDmaArrayBase<T,B>::EnsureCapacity( int num )
{
	this->Value().EnsureCapacity( num );
}

template< class T, class B >
void CDmaArrayBase<T,B>::Purge()
{
	CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
	accessor.Purge();
}


//-----------------------------------------------------------------------------
// Attribute initialization
//-----------------------------------------------------------------------------
template< class T, class B >
void CDmaDecorator<T,B>::Init( CDmElement *pOwner, const char *pAttributeName, int nFlags )
{
	Assert( pOwner );
	this->m_pAttribute = pOwner->AddExternalAttribute( pAttributeName, CDmAttributeInfo<CUtlVector<T> >::AttributeType(), &(this->Value()) );
	Assert( this->m_pAttribute );
	if ( nFlags )
	{
		this->m_pAttribute->AddFlag( nFlags );
	}
}


//-----------------------------------------------------------------------------
// Attribute attribute reference
//-----------------------------------------------------------------------------
template< class T, class BaseClass >
void CDmrDecoratorConst<T,BaseClass>::Init( const CDmAttribute* pAttribute )
{
	if ( pAttribute && pAttribute->GetType() == CDmAttributeInfo< CUtlVector< T > >::AttributeType() )
	{
		this->m_pAttribute = const_cast<CDmAttribute*>( pAttribute );
		this->Attach( this->m_pAttribute->GetAttributeData() );
	}
	else
	{
		this->m_pAttribute = NULL;
		this->Attach( NULL );
	}
}

template< class T, class BaseClass >
void CDmrDecoratorConst<T,BaseClass>::Init( const CDmElement *pElement, const char *pAttributeName )
{
	const CDmAttribute *pAttribute = NULL;
	if ( pElement && pAttributeName && pAttributeName[0] )
	{
		pAttribute = pElement->GetAttribute( pAttributeName );
	}
	Init( pAttribute );
}

template< class T, class BaseClass >
bool CDmrDecoratorConst<T,BaseClass>::IsValid() const
{
	return this->m_pAttribute != NULL;
}


template< class T, class BaseClass >
void CDmrDecorator<T,BaseClass>::Init( CDmAttribute* pAttribute )
{
	if ( pAttribute && pAttribute->GetType() == CDmAttributeInfo< CUtlVector< T > >::AttributeType() )
	{
		this->m_pAttribute = pAttribute;
		this->Attach( this->m_pAttribute->GetAttributeData() );
	}
	else
	{
		this->m_pAttribute = NULL;
		this->Attach( NULL );
	}
}

template< class T, class BaseClass >
void CDmrDecorator<T,BaseClass>::Init( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute )
{
	CDmAttribute *pAttribute = NULL;
	if ( pElement && pAttributeName && pAttributeName[0] )
	{
		if ( bAddAttribute )
		{
			pAttribute = pElement->AddAttribute( pAttributeName, CDmAttributeInfo< CUtlVector< T > >::AttributeType() );
		}
		else
		{
			pAttribute = pElement->GetAttribute( pAttributeName );
		}
	}
	Init( pAttribute );
}

template< class T, class BaseClass >
bool CDmrDecorator<T,BaseClass>::IsValid() const
{
	return this->m_pAttribute != NULL;
}


//-----------------------------------------------------------------------------
//
// Generic array access
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Helper macros to make switch statements based on type 
//-----------------------------------------------------------------------------
#define ARRAY_METHOD_VOID( _type, _func )														\
	case CDmAttributeInfo< CUtlVector< _type > >::ATTRIBUTE_TYPE:								\
		{																						\
			CDmrArray< _type > &array = *reinterpret_cast< CDmrArray< _type > * >( &arrayShared );	\
			array.Init( m_pAttribute );															\
			array._func;																		\
		}																						\
		break;

#define APPLY_ARRAY_METHOD_VOID( _func )					\
	CDmrArray<int> arrayShared;								\
	switch( m_pAttribute->GetType() )						\
	{														\
		ARRAY_METHOD_VOID( bool, _func )					\
		ARRAY_METHOD_VOID( int, _func )						\
		ARRAY_METHOD_VOID( float, _func )					\
		ARRAY_METHOD_VOID( Color, _func )					\
		ARRAY_METHOD_VOID( Vector2D, _func )				\
		ARRAY_METHOD_VOID( Vector, _func )					\
		ARRAY_METHOD_VOID( Vector4D, _func )				\
		ARRAY_METHOD_VOID( QAngle, _func )					\
		ARRAY_METHOD_VOID( Quaternion, _func )				\
		ARRAY_METHOD_VOID( VMatrix, _func )					\
		ARRAY_METHOD_VOID( CUtlString, _func )				\
		ARRAY_METHOD_VOID( CUtlBinaryBlock, _func )			\
		ARRAY_METHOD_VOID( DmObjectId_t, _func )			\
		ARRAY_METHOD_VOID( DmElementHandle_t, _func )		\
	default:												\
		break;												\
	}

#define ARRAY_METHOD_RET( _type, _func )														\
	case CDmAttributeInfo< CUtlVector< _type > >::ATTRIBUTE_TYPE:								\
		{																						\
			CDmrArray< _type > &array = *reinterpret_cast< CDmrArray< _type > * >( &arrayShared );	\
			array.Init( m_pAttribute );															\
			return array._func;																	\
		}

#define APPLY_ARRAY_METHOD_RET( _func )					\
	CDmrArray<int> arrayShared;							\
	switch( m_pAttribute->GetType() )					\
	{													\
		ARRAY_METHOD_RET( bool, _func );				\
		ARRAY_METHOD_RET( int, _func );					\
		ARRAY_METHOD_RET( float, _func );				\
		ARRAY_METHOD_RET( Color, _func );				\
		ARRAY_METHOD_RET( Vector2D, _func );			\
		ARRAY_METHOD_RET( Vector, _func );				\
		ARRAY_METHOD_RET( Vector4D, _func );			\
		ARRAY_METHOD_RET( QAngle, _func );				\
		ARRAY_METHOD_RET( Quaternion, _func );			\
		ARRAY_METHOD_RET( VMatrix, _func );				\
		ARRAY_METHOD_RET( CUtlString, _func );			\
		ARRAY_METHOD_RET( CUtlBinaryBlock, _func );		\
		ARRAY_METHOD_RET( DmObjectId_t, _func );		\
		ARRAY_METHOD_RET( DmElementHandle_t, _func );	\
		default:										\
			break;										\
	}

CDmrGenericArrayConst::CDmrGenericArrayConst() : m_pAttribute( NULL )
{
}

CDmrGenericArrayConst::CDmrGenericArrayConst( const CDmAttribute* pAttribute )
{
	Init( pAttribute );
}

CDmrGenericArrayConst::CDmrGenericArrayConst( const CDmElement *pElement, const char *pAttributeName )
{
	Init( pElement, pAttributeName );
}

void CDmrGenericArrayConst::Init( const CDmAttribute *pAttribute )
{
	if ( pAttribute && IsArrayType( pAttribute->GetType() ) )
	{
		m_pAttribute = const_cast<CDmAttribute*>( pAttribute );
	}
	else
	{
		m_pAttribute = NULL;
	}
}

void CDmrGenericArrayConst::Init( const CDmElement *pElement, const char *pAttributeName )
{
	const CDmAttribute *pAttribute = ( pElement && pAttributeName && pAttributeName[0] ) ? pElement->GetAttribute( pAttributeName ) : NULL;
	Init( pAttribute );
}

int CDmrGenericArrayConst::Count() const
{
	APPLY_ARRAY_METHOD_RET( Count() );
	return 0;
}

const void* CDmrGenericArrayConst::GetUntyped( int i ) const
{
	APPLY_ARRAY_METHOD_RET( GetUntyped( i ) );
	return NULL;
}

const char* CDmrGenericArrayConst::GetAsString( int i, char *pBuffer, size_t nBufLen ) const
{
	if ( ( Count() > i ) && ( i >= 0 ) )
	{
		CUtlBuffer buf( pBuffer, nBufLen, CUtlBuffer::TEXT_BUFFER );
		m_pAttribute->SerializeElement( i, buf );
	}
	else
	{
		pBuffer[0] = 0;
	}
	return pBuffer;
}


CDmrGenericArray::CDmrGenericArray( CDmAttribute* pAttribute )
{
	Init( pAttribute );
}

CDmrGenericArray::CDmrGenericArray( CDmElement *pElement, const char *pAttributeName )
{
	Init( pElement, pAttributeName );
}

void CDmrGenericArray::EnsureCount( int num )
{
	APPLY_ARRAY_METHOD_VOID( EnsureCount(num) );
}

int CDmrGenericArray::AddToTail()
{
	APPLY_ARRAY_METHOD_RET( AddToTail() );
	return -1;
}

void CDmrGenericArray::Remove( int elem )
{
	APPLY_ARRAY_METHOD_VOID( Remove(elem) );
}

void CDmrGenericArray::RemoveAll()
{
	APPLY_ARRAY_METHOD_VOID( RemoveAll() );
}

void CDmrGenericArray::SetMultiple( int i, int nCount, DmAttributeType_t valueType, const void *pValue )
{
	s_pAttrInfo[ m_pAttribute->GetType() ]->SetMultiple( m_pAttribute, i, nCount, valueType, pValue );
}

void CDmrGenericArray::Set( int i, DmAttributeType_t valueType, const void *pValue )
{
	s_pAttrInfo[ m_pAttribute->GetType() ]->Set( m_pAttribute, i, valueType, pValue );
}

void CDmrGenericArray::SetFromString( int i, const char *pValue )
{
	if ( ( Count() > i ) && ( i >= 0 ) )
	{
		int nLen = pValue ? Q_strlen( pValue ) : 0;
		CUtlBuffer buf( pValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
		m_pAttribute->UnserializeElement( i, buf );
	}
}


//-----------------------------------------------------------------------------
// Skip unserialization for an attribute type (unserialize into a dummy variable)
//-----------------------------------------------------------------------------
bool SkipUnserialize( CUtlBuffer &buf, DmAttributeType_t type )
{
	if ( type == AT_UNKNOWN )
		return false;

	return s_pAttrInfo[ type ]->SkipUnserialize( buf );
}


//-----------------------------------------------------------------------------
// returns the number of attributes currently allocated
//-----------------------------------------------------------------------------
int GetAllocatedAttributeCount()
{
	return g_AttrAlloc.Count();
}


//-----------------------------------------------------------------------------
// Attribute type->name and name->attribute type
//-----------------------------------------------------------------------------
const char *AttributeTypeName( DmAttributeType_t type )
{
	if ( ( type >= 0 ) && ( type < AT_TYPE_COUNT ) ) 
		return s_pAttrInfo[ type ]->AttributeTypeName();
	return "unknown";
}

DmAttributeType_t AttributeType( const char *pName )
{
	for ( int i = 0; i < AT_TYPE_COUNT; ++i )
	{
		if ( !Q_stricmp( s_pAttrInfo[ i ]->AttributeTypeName(), pName ) )
			return (DmAttributeType_t)i;
	}

	return AT_UNKNOWN;
}


//-----------------------------------------------------------------------------
// Explicit template instantiation for the known attribute types
//-----------------------------------------------------------------------------
template <class T>
class CInstantiateOp
{
public:
	CInstantiateOp()
	{
		s_pAttrInfo[ CDmAttributeInfo<T>::ATTRIBUTE_TYPE ] = new CDmAttributeOp< T >;
	}
};
static CInstantiateOp<DmUnknownAttribute_t> __s_AttrDmUnknownAttribute_t;

#define INSTANTIATE_GENERIC_OPS( _className )	\
	template< > class CInstantiateOp< CUtlVector< _className > >	\
	{														\
	public:													\
		CInstantiateOp()									\
		{													\
			s_pAttrInfo[ CDmAttributeInfo< CUtlVector< _className > >::ATTRIBUTE_TYPE ] = new CDmArrayAttributeOp< _className >; \
		}													\
	};														\
	static CInstantiateOp< _className > __s_Attr ## _className;	\
	static CInstantiateOp< CUtlVector< _className > > __s_AttrArray ## _className;	

#define DEFINE_ATTRIBUTE_TYPE( _type )	\
	INSTANTIATE_GENERIC_OPS( _type )	\
	ATTRIBUTE_SET_VALUE_ARRAY( _type )	\
	template void CDmAttribute::SetValue< _type >( const _type& value );										\
	template class CDmArrayAttributeOp< _type >;																\
	template class CDmaArrayBase< _type, CDmaDataInternal< CUtlVector< _type > > >;								\
	template class CDmaArrayBase< _type, CDmaDataExternal< CUtlVector< _type > > >;								\
	template class CDmaArrayConstBase< _type, CDmaDataInternal< CUtlVector< _type > > >;								\
	template class CDmaArrayConstBase< _type, CDmaDataExternal< CUtlVector< _type > > >;								\
	template class CDmaDecorator< _type, CDmaArrayBase< _type, CDmaDataInternal< CUtlVector< _type > > > >;		\
	template class CDmrDecorator< _type, CDmaArrayBase< _type, CDmaDataExternal< CUtlVector< _type > > > >;		\
	template class CDmrDecoratorConst< _type, CDmaArrayConstBase< _type, CDmaDataExternal< CUtlVector< _type > > > >;


DEFINE_ATTRIBUTE_TYPE( int )
DEFINE_ATTRIBUTE_TYPE( float )
DEFINE_ATTRIBUTE_TYPE( bool )
DEFINE_ATTRIBUTE_TYPE( Color )
DEFINE_ATTRIBUTE_TYPE( Vector2D )
DEFINE_ATTRIBUTE_TYPE( Vector )
DEFINE_ATTRIBUTE_TYPE( Vector4D )
DEFINE_ATTRIBUTE_TYPE( QAngle )
DEFINE_ATTRIBUTE_TYPE( Quaternion )
DEFINE_ATTRIBUTE_TYPE( VMatrix )
DEFINE_ATTRIBUTE_TYPE( CUtlString )
DEFINE_ATTRIBUTE_TYPE( CUtlBinaryBlock )
DEFINE_ATTRIBUTE_TYPE( DmObjectId_t )
DEFINE_ATTRIBUTE_TYPE( DmElementHandle_t )

template class CDmaDecorator< CUtlString, CDmaStringArrayBase< CDmaDataInternal< CUtlVector< CUtlString > > > >;
template class CDmrDecorator< CUtlString, CDmaStringArrayBase< CDmaDataExternal< CUtlVector< CUtlString > > > >;
