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

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

#include "datamodel/idatamodel.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmattribute.h"
#include "datamodel/dmattributevar.h"
#include "tier1/utlvector.h"
#include "tier1/utlsymbol.h"


//-----------------------------------------------------------------------------
// Internal interface for IDmElementFactory
//-----------------------------------------------------------------------------
class IDmElementFactoryInternal : public IDmElementFactory
{
public:
	virtual void SetElementTypeSymbol( CUtlSymbol sym ) = 0;
	virtual bool IsAbstract() const = 0;
};


//-----------------------------------------------------------------------------
// Class used to register factories into a global list
//-----------------------------------------------------------------------------
class CDmElementFactoryHelper
{
public:
	// Static list of helpers
	static CDmElementFactoryHelper *s_pHelpers[2];

	// Create all the hud elements
	static void InstallFactories( );

public:
	// Construction
	CDmElementFactoryHelper( const char *pClassName, IDmElementFactoryInternal *pFactory, bool bIsStandardFactory );

	// Accessors
	CDmElementFactoryHelper *GetNext( void );

	const char *GetClassname();
	IDmElementFactoryInternal *GetFactory();

private:
	// Next factory in list
	CDmElementFactoryHelper	*m_pNext;
	// Creation function to use for this technology
	IDmElementFactoryInternal *m_pFactory;
	const char				*m_pszClassname;
};


//-----------------------------------------------------------------------------
// Inline methods 
//-----------------------------------------------------------------------------
inline const char *CDmElementFactoryHelper::GetClassname() 
{ 
	return m_pszClassname; 
}

inline IDmElementFactoryInternal *CDmElementFactoryHelper::GetFactory() 
{ 
	return m_pFactory; 
}


//-----------------------------------------------------------------------------
// Helper Template factory for simple creation of factories
//-----------------------------------------------------------------------------
template <class T >
class CDmElementFactory : public IDmElementFactoryInternal
{
public:
	CDmElementFactory( const char *pLookupName ) : m_pLookupName( pLookupName ) {}

	// Creation, destruction
	virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id )
	{
		return new T( handle, m_pLookupName, id, pElementName, fileid );
	}

	virtual void Destroy( DmElementHandle_t hElement )
	{
		CDmElement *pElement = g_pDataModel->GetElement( hElement );
		if ( pElement )
		{
			T *pActualElement = static_cast< T* >( pElement );
			delete pActualElement;
		}
	}

	// Sets the type symbol, used for "isa" implementation
	virtual void SetElementTypeSymbol( CUtlSymbol sym )
	{
		T::SetTypeSymbol( sym );
	}

	virtual bool IsAbstract() const { return false; }

private:
	const char *m_pLookupName;
};


template < class T >
class CDmAbstractElementFactory : public IDmElementFactoryInternal
{
public:
	CDmAbstractElementFactory() {}

	// Creation, destruction
	virtual CDmElement* Create( DmElementHandle_t handle, const char *pElementType, const char *pElementName, DmFileId_t fileid, const DmObjectId_t &id )
	{
		return NULL;
	}

	virtual void Destroy( DmElementHandle_t hElement )
	{
	}

	// Sets the type symbol, used for "isa" implementation
	virtual void SetElementTypeSymbol( CUtlSymbol sym )
	{
		T::SetTypeSymbol( sym );
	}

	virtual bool IsAbstract() const { return true; }

private:
};


//-----------------------------------------------------------------------------
// Helper macro to create the class factory 
//-----------------------------------------------------------------------------
#if defined( MOVIEOBJECTS_LIB ) || defined ( DATAMODEL_LIB ) || defined ( DMECONTROLS_LIB )

#define IMPLEMENT_ELEMENT_FACTORY( lookupName, className )	\
	IMPLEMENT_ELEMENT( className )							\
	CDmElementFactory< className > g_##className##_Factory( #lookupName );							\
	CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, true );	\
	className *g_##className##LinkerHack = NULL;

#define IMPLEMENT_ABSTRACT_ELEMENT( lookupName, className )	\
	IMPLEMENT_ELEMENT( className )							\
	CDmAbstractElementFactory< className > g_##className##_Factory;									\
	CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, true );	\
	className *g_##className##LinkerHack = NULL;

#else

#define IMPLEMENT_ELEMENT_FACTORY( lookupName, className )	\
	IMPLEMENT_ELEMENT( className )							\
	CDmElementFactory< className > g_##className##_Factory( #lookupName );						\
	CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false );	\
	className *g_##className##LinkerHack = NULL;

#define IMPLEMENT_ABSTRACT_ELEMENT( lookupName, className )	\
	IMPLEMENT_ELEMENT( className )							\
	CDmAbstractElementFactory< className > g_##className##_Factory;									\
	CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false );	\
	className *g_##className##LinkerHack = NULL;

#endif


// Used by classes defined in movieobjects or scenedatabase that must be explicitly installed
#define IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( lookupName, className )	\
	IMPLEMENT_ELEMENT( className )							\
	CDmElementFactory< className > g_##className##_Factory( #lookupName );						\
	CDmElementFactoryHelper g_##className##_Helper( #lookupName, &g_##className##_Factory, false );	\
	className *g_##className##LinkerHack = NULL;

//-----------------------------------------------------------------------------
// Installs dm element factories
//-----------------------------------------------------------------------------
void InstallDmElementFactories( );


#endif // DMELEMENTFACTORYHELPER_H