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

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

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Asura
#include "Debugging/Asura_Assert.h"
#include "Entity/Container/Asura_ClientContainerInstance.h"
#include "Entity/Container/Asura_Container.h"
#include "Entity/Container/Asura_ContainerRegistry.h"
#include "Entity/Container/Asura_ServerContainerInstance.h"

// Static data
Asura_TemplateHashTree<Asura_Container*> Asura_Container_System::s_xContainerTree;
bool Asura_Container_System::s_bDoingStateChange = false;
bool Asura_Container_System::s_bDoingActivate	 = false;
bool Asura_Container_System::s_bDoingDeactivate	 = false;
bool Asura_Container_System::s_bDoingCreate		 = false;
bool Asura_Container_System::s_bDoingDamageOverrideChange = false;

////////////////
// Initialise //
////////////////
void Asura_Container_System::Initialise()
{
	s_bDoingStateChange	= false;
	s_bDoingActivate	= false;
	s_bDoingDeactivate	= false;
	s_bDoingCreate		= false;
	s_bDoingDamageOverrideChange = false;
}

//////////////
// Shutdown //
//////////////
void Asura_Container_System::Shutdown()
{
	DeleteAllContainers();
}

///////////
// Reset //
///////////
void Asura_Container_System::Reset()
{
}

/////////////////////////
// DeleteAllContainers //
/////////////////////////
// Delete all of the containers and reset the tree.
void Asura_Container_System::DeleteAllContainers()
{
	// Delete all of the containers.
	Asura_TemplateHashTree_Iterator<Asura_Container*> xIt(s_xContainerTree);
	for ( ; !xIt.Done(); xIt.Next() )
	{
		Asura_Container** ppxContainer = xIt.GetCurrent();
		if (!ppxContainer) continue;
		
		const Asura_Container* pxContainer = (*ppxContainer);
		if (pxContainer)
		{
			// There should be no outstanding instances at this time.
			Asura_Assertf(pxContainer->m_uNumInstances == 0, ("Instance count is %u for container (hash 0x%08x). Should be zero!", pxContainer->m_uNumInstances, pxContainer->GetHash()));

			*ppxContainer = 0;
			delete pxContainer;
		}
	}

	// Clear the tree.
	s_xContainerTree.Reset();
}

/////////////////////
// DeleteContainer //
/////////////////////
bool Asura_Container_System::DeleteContainer(Asura_Hash_ID uContainerHash)
{
	bool bOk = false;

	Asura_Container* pxContainer = FindContainer(uContainerHash);
	if (pxContainer)
	{
		// There should be no outstanding instances at this time.
		Asura_Assertf(pxContainer->m_uNumInstances == 0, ("Instance count is %u for container (hash 0x%08x). Should be zero!", pxContainer->m_uNumInstances, pxContainer->GetHash()));

		bOk = s_xContainerTree.DeleteNodeByHash(uContainerHash);
		delete pxContainer;
	}

	return bOk;
}

///////////////////
// FindContainer //
///////////////////
// Find the specified container.
Asura_Container* Asura_Container_System::FindContainer(Asura_Hash_ID uContainerHash)
{
	Asura_Container** ppxContainer = s_xContainerTree.GetDataByHash(uContainerHash);
	if (ppxContainer == 0 || (*ppxContainer) == 0)
	{
		Asura_Assertf(0, ("Failed to find container with hash 0x%08x", uContainerHash));
		return 0;
	}

	return *ppxContainer;
}

//////////////////////////
// CreateClientInstance //
//////////////////////////
// Create an instance of the specified container. Returns NULL if container is not found.
Asura_ClientContainerInstance* Asura_Container_System::CreateClientInstance(Asura_Hash_ID uContainerHash)
{
	if (uContainerHash == ASURA_HASH_ID_UNSET) return 0;
	
	Asura_Container* pxContainer = FindContainer(uContainerHash);
	if (!pxContainer) return 0;

	++(pxContainer->m_uNumInstances);
	return Asura_ContainerRegistry::CreateClientInstance(pxContainer);
}

//////////////////////////////
// CreateClientInstanceBase //
//////////////////////////////
// As CreateClientInstance, but only creates certain basic types of container (i.e. health and collections).
// These types need to be created earlier in order to get the damage overrides to work.
Asura_ClientContainerInstance* Asura_Container_System::CreateClientInstanceBase(Asura_Hash_ID uContainerHash)
{
	if (uContainerHash == ASURA_HASH_ID_UNSET) return 0;
	
	Asura_Container* pxContainer = FindContainer(uContainerHash);
	if (!pxContainer) return 0;

	u_int uClass = pxContainer->GetClassification();

	// For attachments we are actually interested in the child's classification.
	if (uClass == AsuraContainerClass_Attachment)
	{
		const Asura_Container_Attachment* const pxAttachment = static_cast<Asura_Container_Attachment*>(pxContainer);
		uClass = pxAttachment->GetChildClassification();
	}

	if (!IsBaseClassification(uClass))
	{
		return 0;	
	}

	++(pxContainer->m_uNumInstances);
	return Asura_ContainerRegistry::CreateClientInstance(pxContainer);
}

//////////////////////////
// IsBaseClassification //
//////////////////////////
// Returns true if it is a basic container type (i.e. health or collection).
bool Asura_Container_System::IsBaseClassification(u_int uClassification)
{
	return uClassification == AsuraContainerClass_Collection ||
		   uClassification == AsuraContainerClass_Health;
}

/////////////////////
// DestroyInstance //
/////////////////////
// Destroy the instance.
void Asura_Container_System::DestroyInstance(Asura_ClientContainerInstance* pxInstance)
{
	if (!pxInstance) return;
		
	const Asura_Hash_ID uContainerHash = pxInstance->GetContainerHash();
	Asura_Container* pxContainer = FindContainer(uContainerHash);
	if (pxContainer)
	{
		if (pxContainer->m_uNumInstances > 0)
		{
			--(pxContainer->m_uNumInstances);
		}
		else
		{
			Asura_Assertf(0, ("Instance count wrong for container (hash 0x%08x)", uContainerHash));
		}
	}

	delete pxInstance;
}

//////////////////////////
// CreateServerInstance //
//////////////////////////
// Create a server instance of the specified container. Returns NULL if container not found (or if it has no server instance registered).
Asura_ServerContainerInstance* Asura_Container_System::CreateServerInstance(Asura_Hash_ID uContainerHash)
{
	if (uContainerHash == ASURA_HASH_ID_UNSET) return 0;

	Asura_Container* pxContainer = FindContainer(uContainerHash);
	if (!pxContainer) return 0;

	// Skip past attachments.
	if (pxContainer->GetClassification() == AsuraContainerClass_Attachment)
	{
		const Asura_Container_Attachment* pxAttachment = static_cast<const Asura_Container_Attachment*>(pxContainer);
		return CreateServerInstance(pxAttachment->GetChildContainerHash());
	}

	return Asura_ContainerRegistry::CreateServerInstance(pxContainer);
}

///////////////////////////
// DestroyServerInstance //
///////////////////////////
void Asura_Container_System::DestroyServerInstance(Asura_ServerContainerInstance* pxInstance)
{
	delete pxInstance;
}

//////////////////
// AddContainer //
//////////////////
// Add container to the tree. Return true if added successfully.
bool Asura_Container_System::AddContainer(Asura_Container* pxContainer)
{
	if (!pxContainer) return false;

	const Asura_Hash_ID uContainerHash = pxContainer->GetHash();
	Asura_Assert(uContainerHash != ASURA_HASH_ID_UNSET, "Hash not set for Asura_Container!");

	Asura_Container** ppxContainer = s_xContainerTree.AddNode(uContainerHash, pxContainer);
	Asura_Assertf(ppxContainer, ("Failed to add container (hash 0x%08x) to tree.", uContainerHash));
	return (ppxContainer != 0);
}

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM
