///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ContainerRegistry.cpp
//
// Created by Martin G.Harris (27/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"
#include "Entity/Container/Asura_ContainerRegistry.h"

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Asura
#include "Entity/Container/Asura_ClientContainerInstance.h"
#include "Entity/Container/Asura_Container.h"

///////////////////////////////////////////////////////////////////////////////
// Asura_ContainerRegistry
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////
// MapClassificationToIndex //
//////////////////////////////
// Return index in registry of specified classification (or -1 if not valid).
// Maps the classifications to a contiguous range.
int Asura_ContainerRegistry::MapClassificationToIndex(u_int uClassification)
{
	int iIndex = -1;
	if (uClassification > 0 && uClassification < AsuraContainerClass_LastEngineClassification)
	{
		iIndex = static_cast<int>(uClassification - 1);
	}
	else if (uClassification >= AsuraContainerClass_ProjectSpecific)
	{
		iIndex = static_cast<int>(uClassification - AsuraContainerClass_ProjectSpecific + AsuraContainerClass_LastEngineClassification - 1);
	}

	if (iIndex >= iCONTAINER_TYPES_MAX_NUMBER)
	{
		Asura_Assertf(0, ("Index too large (%d), may need to increase iCONTAINER_TYPES_MAX_NUMBER.", iIndex));
		iIndex = -1;
	}

	Asura_Assertf(iIndex != -1 || uClassification == 0, ("Unknown ASURA_CONTAINER_CLASSIFICATION (%u)", uClassification));
	
	return iIndex;
}

//////////////
// AddEntry //
//////////////
void Asura_ContainerRegistry::AddEntry(u_int uClassification, PFN_ASURA_CONTAINER_CREATE pfnFunction, PFN_CLIENT_INSTANCE_CREATE pfnInstance)
{
	const int iIndex = MapClassificationToIndex(uClassification);
	if (iIndex < 0)
	{
		Asura_Assertf(0, ("Failed to add container class (%u) to registry.", uClassification));
		return;
	}

	Asura_Assert(iIndex>=0 && iIndex<iCONTAINER_TYPES_MAX_NUMBER, "Invalid index.");

	if (m_axRegistry[iIndex].m_pfnAsuraContainerCreateFn)
	{
		Asura_Assertf(0, ("Tried to add container class (%u) to registry twice.", uClassification));
		return;
	}

	m_axRegistry[iIndex].m_pfnAsuraContainerCreateFn = pfnFunction;
	m_axRegistry[iIndex].m_pfnClientInstanceCreateFn = pfnInstance;
}

///////////////////////
// AddServerInstance //
///////////////////////
// Only a few container classes have corresponding server instances, so this is a separate function.
void Asura_ContainerRegistry::AddServerInstance(u_int uClassification, PFN_SERVER_INSTANCE_CREATE pfnFunction)
{
	const int iIndex = MapClassificationToIndex(uClassification);
	if (iIndex < 0)
	{
		Asura_Assertf(0, ("Failed to add container server instance (class = %u) to registry.", uClassification));
		return;
	}

	Asura_Assert(iIndex>=0 && iIndex<iCONTAINER_TYPES_MAX_NUMBER, "Invalid index.");

	if (m_axRegistry[iIndex].m_pfnServerInstanceCreateFn)
	{
		Asura_Assertf(0, ("Tried to add container server instance (class = %u) to registry twice.", uClassification));
		return;
	}

	m_axRegistry[iIndex].m_pfnServerInstanceCreateFn = pfnFunction;
}

/////////////////////
// AddClientEntity //
/////////////////////
void Asura_ContainerRegistry::AddClientEntity(PFN_CLIENT_ENTITY_CREATE pfnFunction, bool bProjectOverride)
{
	// Don't let the Asura version overwrite the project version.
	if (m_pfnClientEntityCreateFn && !bProjectOverride) return;

	m_pfnClientEntityCreateFn = pfnFunction;
}

/////////////////////
// AddServerEntity //
/////////////////////
void Asura_ContainerRegistry::AddServerEntity(PFN_SERVER_ENTITY_CREATE pfnFunction, bool bProjectOverride)
{
	// Don't let the Asura version overwrite the project version.
	if (m_pfnServerEntityCreateFn && !bProjectOverride) return;

	m_pfnServerEntityCreateFn = pfnFunction;
}

//////////////////////
// CreateAsuraClass //
//////////////////////
Asura_Container* Asura_ContainerRegistry::CreateAsuraClass(u_int uClassification)
{
	Asura_Container* pxContainer = 0;

	const int iIndex = MapClassificationToIndex(uClassification);
	if (iIndex >= 0 && iIndex < iCONTAINER_TYPES_MAX_NUMBER)
	{
		PFN_ASURA_CONTAINER_CREATE pfnFunction = GetInstance().m_axRegistry[iIndex].m_pfnAsuraContainerCreateFn;
		if (pfnFunction)
		{
			pxContainer = pfnFunction();
			if (pxContainer)
			{
				pxContainer->m_uClassification = uClassification;
			}
		}
	}
	
	Asura_Assertf(pxContainer, ("Failed to create Asura_Container (classification = %u).", uClassification));
	return pxContainer;
}

//////////////////////////
// CreateClientInstance //
//////////////////////////
Asura_ClientContainerInstance* Asura_ContainerRegistry::CreateClientInstance(const Asura_Container* pxContainer)
{
	Asura_Assert(pxContainer, "Null container pointer");
	if (!pxContainer) return 0;

	const u_int uClassification = pxContainer->GetClassification();
	Asura_ClientContainerInstance* pxInstance = 0;

	const int iIndex = MapClassificationToIndex(uClassification);
	if (iIndex >= 0 && iIndex < iCONTAINER_TYPES_MAX_NUMBER)
	{
		PFN_CLIENT_INSTANCE_CREATE pfnFunction = GetInstance().m_axRegistry[iIndex].m_pfnClientInstanceCreateFn;
		if (pfnFunction)
		{
			pxInstance = pfnFunction(pxContainer);
		}
	}
	
	Asura_Assertf(pxInstance, ("Failed to create Asura_ClientContainerInstance (classification = %u).", uClassification));
	return pxInstance;
}

//////////////////////////
// CreateServerInstance //
//////////////////////////
Asura_ServerContainerInstance* Asura_ContainerRegistry::CreateServerInstance(const Asura_Container* pxContainer)
{
	Asura_Assert(pxContainer, "Null container pointer");
	if (!pxContainer) return 0;

	const u_int uClassification = pxContainer->GetClassification();

	const int iIndex = MapClassificationToIndex(uClassification);
	if (iIndex >= 0 && iIndex < iCONTAINER_TYPES_MAX_NUMBER)
	{
		PFN_SERVER_INSTANCE_CREATE pfnFunction = GetInstance().m_axRegistry[iIndex].m_pfnServerInstanceCreateFn;
		if (pfnFunction)
		{
			return pfnFunction(pxContainer);
		}
	}
	
	return 0;
}

////////////////////////
// CreateClientEntity //
////////////////////////
Asura_ClientEntity_Container* Asura_ContainerRegistry::CreateClientEntity()
{
	PFN_CLIENT_ENTITY_CREATE pfnFunction = GetInstance().m_pfnClientEntityCreateFn;
	if (pfnFunction)
	{
		return pfnFunction();
	}
	Asura_Assert(0, "Container client entity creation function is null!");
	return 0;
}

////////////////////////
// CreateServerEntity //
////////////////////////
Asura_ServerEntity_Container* Asura_ContainerRegistry::CreateServerEntity(Asura_Hash_ID uGuid)
{
	PFN_SERVER_ENTITY_CREATE pfnFunction = GetInstance().m_pfnServerEntityCreateFn;
	if (pfnFunction)
	{
		return pfnFunction(uGuid);
	}
	Asura_Assert(0, "Container server entity creation function is null!");
	return 0;
}

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM
