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

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

#include "datamodel/attributeflags.h"
#include "datamodel/idatamodel.h"
#include "datamodel/dmattributetypes.h"
#include "datamodel/dmvar.h"
#include "tier1/utlhash.h"

//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CDmElement;
class CUtlBuffer;


//-----------------------------------------------------------------------------
// Used for attribute change callbacks
//-----------------------------------------------------------------------------
typedef unsigned short DmMailingList_t;
enum
{
	DMMAILINGLIST_INVALID = (DmMailingList_t)~0
};


//-----------------------------------------------------------------------------
// Purpose: A general purpose pAttribute.  Eventually will be extensible to arbitrary user types
//-----------------------------------------------------------------------------
class CDmAttribute
{
public:
	// Returns the type
	DmAttributeType_t GetType() const;
	const char *GetTypeString() const;
	template< class T > bool IsA() const;

	// Returns the name. NOTE: The utlsymbol
	// can be turned into a string by using g_pDataModel->String();
	const char *GetName() const;
	UtlSymId_t	GetNameSymbol() const;
	void		SetName( const char *newName );

	// Gets the attribute value
	// NOTE: GetValueUntyped is used with GetType() for use w/ SetValue( type, void* )
	template< class T > const T& GetValue() const;
	template< class T > const T& GetValue( const T& defaultValue ) const;
	const char					*GetValueString() const;
	template< class E > E		*GetValueElement() const;
	const void					*GetValueUntyped() const; 

	// Sets the attribute value
	template< class T > void SetValue( const T &value );
	template< class E > void SetValue( E* pValue );
	void	SetValue( const void *pValue, size_t nSize );

	// Copies w/ type conversion (if possible) from another attribute
	void	SetValue( const CDmAttribute *pAttribute );
	void	SetValue( CDmAttribute *pAttribute );
	void	SetValue( DmAttributeType_t valueType, const void *pValue );

	// Sets the attribute to its default value based on its type
	void	SetToDefaultValue();

	// Convert to and from string
	void SetValueFromString( const char *pValue );
	const char *GetValueAsString( char *pBuffer, size_t nBufLen ) const;

	// Used for element and element array attributes; it specifies which type of
	// elements are valid to be referred to by this attribute
	void		SetElementTypeSymbol( UtlSymId_t typeSymbol );
	UtlSymId_t	GetElementTypeSymbol() const;

	// Returns the next attribute
	CDmAttribute *NextAttribute();
	const CDmAttribute *NextAttribute() const;

	// Returns the owner
	CDmElement *GetOwner();

	// Methods related to flags
	void	AddFlag( int flags );
	void	RemoveFlag( int flags );
	void	ClearFlags();
	int		GetFlags() const;
	bool	IsFlagSet( int flags ) const;

	// Serialization
	bool	Serialize( CUtlBuffer &buf ) const;
	bool	Unserialize( CUtlBuffer &buf );

	// Serialization of a single element. 
	// First version of UnserializeElement adds to tail if it worked
	// Second version overwrites, but does not add, the element at the specified index 
	bool	SerializeElement( int nElement, CUtlBuffer &buf ) const;
	bool	UnserializeElement( CUtlBuffer &buf );
	bool	UnserializeElement( int nElement, CUtlBuffer &buf );

	// Does this attribute serialize on multiple lines?
	bool	SerializesOnMultipleLines() const;

	// Get the attribute/create an attribute handle
	DmAttributeHandle_t GetHandle( bool bCreate = true );

	// Notify external elements upon change ( Calls OnAttributeChanged )
	// Pass false here to stop notification
	void	NotifyWhenChanged( DmElementHandle_t h, bool bNotify );

	// estimate memory overhead
	int		EstimateMemoryUsage( TraversalDepth_t depth ) const;

private:
	// Class factory
	static CDmAttribute *CreateAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName );
	static CDmAttribute *CreateExternalAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pExternalMemory );
	static void DestroyAttribute( CDmAttribute *pAttribute );

	// Constructor, destructor
	CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName );
	CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pMemory );
	~CDmAttribute();

	// Used when constructing CDmAttributes
	void Init( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName );

	// Used when shutting down, indicates DmAttributeHandle_t referring to this are invalid
	void InvalidateHandle();

	// Used when shutting down, indicates no more change notifications will be sent to listening elements
	void CleanupMailingList();

	// Called when the attribute changes
	void PreChanged();
	void OnChanged( bool bArrayCountChanged = false, bool bIsTopological = false );

	// Is modification allowed in this phase?
	bool ModificationAllowed() const;

	// Mark the attribute as being dirty
	bool MarkDirty();

	// Is the data inline in a containing element class?
	bool IsDataInline() const;

	// Allocates, frees internal data storage
	void CreateAttributeData();
	void DeleteAttributeData();

	// Gets at the internal data storage 
	void* GetAttributeData();
	const void*	GetAttributeData() const;
	template < class T > typename CDmAttributeInfo< T >::StorageType_t* GetData();
	template < class T > const typename CDmAttributeInfo< T >::StorageType_t* GetData() const;
	template < class T > typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* GetArrayData();
	template < class T > const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* GetArrayData() const;

	// Used by CDmElement to manage the list of attributes it owns
	CDmAttribute **GetNextAttributeRef();

	// Implementational function used for memory consumption estimation computation
	int EstimateMemoryUsageInternal( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) const;

	// Called by elements after unserialization of their attributes is complete
	void OnUnserializationFinished();

	template< class T > bool IsTypeConvertable() const;
	template< class T > bool ShouldModify( const T& src );
	template< class T > void CopyData( const T& src );
	template< class T > void CopyDataOut( T& dest ) const;

private:
	CDmAttribute *m_pNext;
	void *m_pData;
	CDmElement *m_pOwner;
	int m_nFlags;
	DmAttributeHandle_t m_Handle;
	CUtlSymbol m_Name;
	DmMailingList_t m_hMailingList;

	friend class CDmElement;
	friend class CDmAttributeAccessor;
	template< class T > friend class CDmrElementArray;
	template< class E > friend class CDmrElementArrayConst;
	template< class T > friend class CDmaArrayAccessor;
	template< class T, class B > friend class CDmrDecorator;
	template< class T, class B > friend class CDmrDecoratorConst;
	template< class T > friend class CDmArrayAttributeOp;
};

	 
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline DmAttributeType_t CDmAttribute::GetType() const
{
	return (DmAttributeType_t)( m_nFlags & FATTRIB_TYPEMASK );
}

template< class T > inline bool CDmAttribute::IsA() const
{
	return GetType() == CDmAttributeInfo< T >::AttributeType();
}

inline const char *CDmAttribute::GetName() const
{
	return g_pDataModel->GetString( m_Name );
}

inline UtlSymId_t CDmAttribute::GetNameSymbol() const
{
	return m_Name;
}


//-----------------------------------------------------------------------------
// Iteration
//-----------------------------------------------------------------------------
inline CDmAttribute *CDmAttribute::NextAttribute()
{
	return m_pNext;
}

inline const CDmAttribute *CDmAttribute::NextAttribute() const
{
	return m_pNext;
}


//-----------------------------------------------------------------------------
// Returns the owner
//-----------------------------------------------------------------------------
inline CDmElement *CDmAttribute::GetOwner()
{
	return m_pOwner;
}


//-----------------------------------------------------------------------------
// Value getting methods
//-----------------------------------------------------------------------------
template< class T > 
inline const T& CDmAttribute::GetValue( const T& defaultValue ) const
{
	if ( GetType() == CDmAttributeInfo< T >::ATTRIBUTE_TYPE )
		return *reinterpret_cast< const T* >( m_pData );

	if ( IsTypeConvertable< T >() )
	{
		static T tempVal;
		CopyDataOut( tempVal );
		return tempVal;
	}

	Assert( 0 );
	return defaultValue;
}

template< class T > 
inline const T& CDmAttribute::GetValue() const
{
	static CDmaVar< T > defaultVal;
	return GetValue( defaultVal.Get() );
}

inline const char *CDmAttribute::GetValueString() const
{
	Assert( GetType() == AT_STRING );
	if ( GetType() != AT_STRING )
		return NULL;

	return GetValue< CUtlString >();
}

// used with GetType() for use w/ SetValue( type, void* )
inline const void* CDmAttribute::GetValueUntyped() const
{ 
	return m_pData; 
} 

#ifndef _LINUX
template< class E > 
inline E* CDmAttribute::GetValueElement() const
{
	Assert( GetType() == AT_ELEMENT );
	if ( GetType() == AT_ELEMENT )
		return GetElement<E>( this->GetValue<DmElementHandle_t>( ) );
	return NULL;
}
#endif


//-----------------------------------------------------------------------------
// Value setting methods
//-----------------------------------------------------------------------------
template< class E > 
inline void CDmAttribute::SetValue( E* pValue )
{
	Assert( GetType() == AT_ELEMENT );
	if ( GetType() == AT_ELEMENT )
	{
		SetValue( pValue ? pValue->GetHandle() : DMELEMENT_HANDLE_INVALID );
	}
}

template<>
inline void CDmAttribute::SetValue( const char *pValue )
{
	int nLen = pValue ? Q_strlen( pValue ) + 1 : 0;
	CUtlString str( pValue, nLen );
	return SetValue( str );
}

template<>
inline void CDmAttribute::SetValue( char *pValue )
{
	return SetValue( (const char *)pValue );
}

inline void CDmAttribute::SetValue( const void *pValue, size_t nSize )
{
	CUtlBinaryBlock buf( pValue, nSize );
	return SetValue( buf );
}


//-----------------------------------------------------------------------------
// Methods related to flags
//-----------------------------------------------------------------------------
inline void CDmAttribute::AddFlag( int nFlags )
{
	m_nFlags |= nFlags;
}

inline void CDmAttribute::RemoveFlag( int nFlags )
{
	m_nFlags &= ~nFlags;
}

inline void CDmAttribute::ClearFlags()
{
	m_nFlags = 0;
}

inline int CDmAttribute::GetFlags() const
{
	return m_nFlags;
}

inline bool CDmAttribute::IsFlagSet( int nFlags ) const
{
	return ( nFlags & m_nFlags ) ? true : false;
}

inline bool CDmAttribute::IsDataInline() const
{ 
	return !IsFlagSet(FATTRIB_EXTERNAL); 
}


//-----------------------------------------------------------------------------
// Gets at the internal data storage 
//-----------------------------------------------------------------------------
inline void* CDmAttribute::GetAttributeData() 
{ 
	return m_pData; 
}

inline const void* CDmAttribute::GetAttributeData() const 
{ 
	return m_pData; 
}

template < class T >
inline typename CDmAttributeInfo< T >::StorageType_t* CDmAttribute::GetData()
{
	return ( typename CDmAttributeInfo< T >::StorageType_t* )m_pData;
}

template < class T >
inline typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* CDmAttribute::GetArrayData()
{
	return ( typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* )m_pData;
}

template < class T >
inline const typename CDmAttributeInfo< T >::StorageType_t* CDmAttribute::GetData() const
{
	return ( const typename CDmAttributeInfo< T >::StorageType_t* )m_pData;
}

template < class T >
inline const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* CDmAttribute::GetArrayData() const
{
	return ( const typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t* )m_pData;
}


//-----------------------------------------------------------------------------
// Used by CDmElement to manage the list of attributes it owns
//-----------------------------------------------------------------------------
inline CDmAttribute **CDmAttribute::GetNextAttributeRef()
{
	return &m_pNext;
}


//-----------------------------------------------------------------------------
// helper function for determining which attributes/elements to traverse during copy/find/save/etc.
//-----------------------------------------------------------------------------
inline bool ShouldTraverse( const CDmAttribute *pAttr, TraversalDepth_t depth )
{
	switch ( depth )
	{
	case TD_NONE:
		return false;

	case TD_SHALLOW:
		if ( !pAttr->IsFlagSet( FATTRIB_MUSTCOPY ) )
			return false;
		// fall-through intentional
	case TD_DEEP:
		if ( pAttr->IsFlagSet( FATTRIB_NEVERCOPY ) )
			return false;
		// fall-through intentional
	case TD_ALL:
		return true;
	}

	Assert( 0 );
	return false;
}

#endif // DMATTRIBUTE_H
