///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ContainerRegistry.h
//
// Created by Martin G.Harris (27/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Registry for different types of container.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_CONTAINER_REGISTRY_H__
#define __ASURA_CONTAINER_REGISTRY_H__

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes
#include "Entity/Container/Asura_Container_Classification.h"

// Forward declarations
class Asura_Container;
class Asura_ClientContainerInstance;
class Asura_ServerContainerInstance;
class Asura_ClientEntity_Container;
class Asura_ServerEntity_Container;

/////////////////////////////
// Asura_ContainerRegistry //
/////////////////////////////
// Registry for different types of container class. Allowing Asura and projects to automatically register
// container classes. Corresponding Tools classes should also be registered using Tools_ContainerRegistry.
// Projects can define their own client and server entities too (in which case a corresponding Tools entity
// needs to be registered).
class Asura_ContainerRegistry
{
public:
	// Typedefs for creation functions.
	typedef Asura_Container* (*PFN_ASURA_CONTAINER_CREATE) (void);
	typedef Asura_ClientContainerInstance* (*PFN_CLIENT_INSTANCE_CREATE) (const Asura_Container*);
	typedef Asura_ServerContainerInstance* (*PFN_SERVER_INSTANCE_CREATE) (const Asura_Container*);
	typedef Asura_ClientEntity_Container*  (*PFN_CLIENT_ENTITY_CREATE) (void);
	typedef Asura_ServerEntity_Container*  (*PFN_SERVER_ENTITY_CREATE) (Asura_Hash_ID);

	// Add entries.
	void AddEntry(u_int uClassification, PFN_ASURA_CONTAINER_CREATE pfnFunction, PFN_CLIENT_INSTANCE_CREATE pfnInstance);
	void AddServerInstance(u_int uClassification, PFN_SERVER_INSTANCE_CREATE pfnFunction);
	void AddClientEntity(PFN_CLIENT_ENTITY_CREATE pfnFunction, bool bProjectOverride);
	void AddServerEntity(PFN_SERVER_ENTITY_CREATE pfnFunction, bool bProjectOverride);

	// Creation functions.
	static Asura_Container* CreateAsuraClass(u_int uClassification);
	static Asura_ClientContainerInstance* CreateClientInstance(const Asura_Container* pxContainer);
	static Asura_ServerContainerInstance* CreateServerInstance(const Asura_Container* pxContainer);
	static Asura_ClientEntity_Container* CreateClientEntity();
	static Asura_ServerEntity_Container* CreateServerEntity(Asura_Hash_ID uGuid);

	// Get instance.
	static Asura_ContainerRegistry& GetInstance()
	{
		static Asura_ContainerRegistry s_xInstance;
		return s_xInstance;
	}

	static int MapClassificationToIndex(u_int uClassification);

private:
	Asura_ContainerRegistry() : m_pfnClientEntityCreateFn(0), m_pfnServerEntityCreateFn(0) {}

	class Entry
	{
	public:
		Entry()
			: m_pfnAsuraContainerCreateFn(0)
			, m_pfnClientInstanceCreateFn(0)
			, m_pfnServerInstanceCreateFn(0)
		{}

		PFN_ASURA_CONTAINER_CREATE	m_pfnAsuraContainerCreateFn;	// Asura container creation function.
		PFN_CLIENT_INSTANCE_CREATE	m_pfnClientInstanceCreateFn;	// Client instance creation function.
		PFN_SERVER_INSTANCE_CREATE	m_pfnServerInstanceCreateFn;	// Server instance creation function.
	};
	
	Entry m_axRegistry[iCONTAINER_TYPES_MAX_NUMBER];

	PFN_CLIENT_ENTITY_CREATE m_pfnClientEntityCreateFn; // Client entity creation function.
	PFN_SERVER_ENTITY_CREATE m_pfnServerEntityCreateFn; // Server entity creation function.
};

//////////////////////////////////
// Asura_ContainerRegisterClass //
//////////////////////////////////
// Helper to register classes derived from Asura_Container and Asura_ClientContainerInstance respectively.
template <class CONTAINER, class CLIENTINSTANCE> class Asura_ContainerRegisterClass
{
public:
	// uClassification is from ASURA_CONTAINER_CLASSIFICATION or equivalent project enum.
	Asura_ContainerRegisterClass(u_int uClassification)
	{
		Asura_ContainerRegistry::GetInstance().AddEntry(uClassification, Create, CreateInstance);
	}

	static Asura_Container* Create() { return new CONTAINER; }
	static Asura_ClientContainerInstance* CreateInstance(const Asura_Container* pxContainer)
	{
		return new CLIENTINSTANCE(static_cast<const CONTAINER*>(pxContainer));
	}
};

/////////////////////////////////////////
// Asura_ContainerRegisterClientEntity //
/////////////////////////////////////////
// Helper to register project-specific client entity derived from Asura_ClientEntity_Container.
// bProjectOverride is true for a project-specific entity and false for an Asura entity.
template<class CLIENTENTITY> class Asura_ContainerRegisterClientEntity
{
public:
	Asura_ContainerRegisterClientEntity(bool bProjectOverride = true)
	{
		Asura_ContainerRegistry::GetInstance().AddClientEntity(Create, bProjectOverride);
	}

	static Asura_ClientEntity_Container* Create() { return new CLIENTENTITY; }
};

/////////////////////////////////////////
// Asura_ContainerRegisterServerEntity //
/////////////////////////////////////////
// Helper to register project-specific server entity derived from Asura_ServerEntity_Container.
// bProjectOverride is true for a project-specific entity and false for an Asura entity.
template<class SERVERENTITY> class Asura_ContainerRegisterServerEntity
{
public:
	Asura_ContainerRegisterServerEntity(bool bProjectOverride = true)
	{
		Asura_ContainerRegistry::GetInstance().AddServerEntity(Create, bProjectOverride);
	}

	static Asura_ServerEntity_Container* Create(Asura_Hash_ID uGuid) { return new SERVERENTITY(uGuid); }
};

///////////////////////////////////////////
// Asura_ContainerRegisterServerInstance //
///////////////////////////////////////////
// Helper to register a server instance derived from Asura_ServerContainerInstance.
template<class CONTAINER, class SERVERINSTANCE> class Asura_ContainerRegisterServerInstance
{
public:
	Asura_ContainerRegisterServerInstance(u_int uClassification)
	{
		Asura_ContainerRegistry::GetInstance().AddServerInstance(uClassification, Create);
	}

	static Asura_ServerContainerInstance* Create(const Asura_Container* pxContainer) { return new SERVERINSTANCE(static_cast<const CONTAINER*>(pxContainer)); }
};


#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

#endif //__ASURA_CONTAINER_REGISTRY_H__

