///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ServerContainerInstance.cpp
//
// Created by Martin G.Harris (21/08/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Asura
#include "Entity/Container/Asura_ClientContainerInstance.h"
#include "Entity/Container/Asura_ContainerRegistry.h"
#include "Entity/Container/Asura_Container_Messages.h"
#include "Entity/Container/Asura_Container_System.h"
#include "Physics/Asura_Physics_Object.h"

// Register the server instances.
Asura_ContainerRegisterServerInstance<Asura_Container_Collection, Asura_ServerContainerInstance_Collection> xSCICollection(AsuraContainerClass_Collection);

Asura_ContainerRegisterServerInstance<Asura_Container_Health, Asura_ServerContainerInstance_Health> xSCIHealth(AsuraContainerClass_Health);

Asura_ContainerRegisterServerInstance<Asura_Container_Mesh, Asura_ServerContainerInstance_Mesh> xSCIMesh(AsuraContainerClass_Mesh);


///////////////////////////////////////////////////////////////////////////////
// Asura_ServerContainerInstance
///////////////////////////////////////////////////////////////////////////////
Asura_ServerContainerInstance::Asura_ServerContainerInstance(const Asura_Container* pxContainer)
: m_pxContainer(pxContainer)
, m_uUniqueID(uASURA_CONTAINER_UNIQUE_ID_INVALID)
{
	m_xInputState.Reset();
}

// Chunkstream functions.
const int iCHUNK_ASURA_SCI_END_TERMINATOR = -1;

const int iCHUNK_ASURA_SCI_VERSION_HEADER =  1;

/////////////////
// ReadAllData //
/////////////////
// Read all of the serialisable data for the server instance containers.
void Asura_ServerContainerInstance::ReadAllData(Asura_Chunk_Stream& xStream, Asura_ServerContainerInstance* pxInstance)
{
	int iVersion = 0;
	xStream >> iVersion;

	if (iVersion == 0)
	{
		// No data was written.
		return;
	}

	if (iVersion < 0 || iVersion > iCHUNK_ASURA_SCI_VERSION_HEADER)
	{
		Asura_Assertf(0, ("Invalid version (%d) for server container instance data header", iVersion));
		return;
	}

	if (!pxInstance)
	{
		SkipAllData(xStream, iVersion);
		return;
	}

	while (true)
	{
		// Check for the terminator
		if (xStream.PeekInt() == iCHUNK_ASURA_SCI_END_TERMINATOR)
		{
			int iTerminator;
			xStream >> iTerminator;
			return;
		}

		// Read header
		u_short usSize = 0;
		xStream >> usSize;
		
		u_short usVersion = 0;
		xStream >> usVersion;

		u_int uUniqueID = 0;
		xStream >> uUniqueID;

		Asura_Hash_ID uHash = ASURA_HASH_ID_UNSET;
		xStream >> uHash;

		u_int uClass = 0;
		xStream >> uClass;

		Asura_ServerContainerInstance* pxFound = pxInstance->FindFromUniqueID_Safe(uUniqueID, uHash, uClass);
		Asura_Assert(pxFound, "Server instance not found.");
		if (pxFound)
		{
			// Found a match. Read the data.
			pxFound->ReadFromChunkStream(xStream, usVersion);
		}
		else
		{
			// No match found. Skip the data.
			const u_int uBytesToSkip = usSize;
			const u_int uSkipped = xStream.Skip(uBytesToSkip);

			if (uBytesToSkip != uSkipped)
			{
				Asura_Assert(0, "Error: Unexpectedly reached end of chunkstream.");
				return;
			}	
		}
	}
}

/////////////////
// SkipAllData //
/////////////////
void Asura_ServerContainerInstance::SkipAllData(Asura_Chunk_Stream& xStream, int iVersion)
{
	while (true)
	{
		// Check for the terminator
		if (xStream.PeekInt() == iCHUNK_ASURA_SCI_END_TERMINATOR)
		{
			int iTerminator;
			xStream >> iTerminator;
			return;
		}

		// Skip header
		const u_int uBytesToSkip = SkipHeader(xStream);

		// Skip remaining bytes
		const u_int uSkipped = xStream.Skip(uBytesToSkip);

		if (uBytesToSkip != uSkipped)
		{
			Asura_Assert(0, "Error: Unexpectedly reached end of chunkstream.");
			return;
		}
	}
}

//////////////////
// WriteAllData //
//////////////////
// Write all of the serialisable data for the server instance containers.
void Asura_ServerContainerInstance::WriteAllData(Asura_Chunk_Stream& xStream, Asura_ServerContainerInstance* pxInstance)
{
	if (!pxInstance)
	{
		// No data to write. Just write a zero marker.
		int iZero = 0;
		xStream << iZero;
		return;
	}

	// Overall version number.
	xStream << iCHUNK_ASURA_SCI_VERSION_HEADER;

	// Traverse the tree writing out headers and data for each instance that needs to be serialised.
	pxInstance->WriteToChunkStream(xStream);

	// Mark end.
	xStream << iCHUNK_ASURA_SCI_END_TERMINATOR;
}

/////////////////
// WriteHeader //
/////////////////
// Write out a header.
void Asura_ServerContainerInstance::WriteHeader(Asura_Chunk_Stream& xStream, u_int uSize, u_short usVersion) const
{
	const u_short usSize = static_cast<u_short>(uSize);
	
	xStream << usSize;
	xStream << usVersion;

	xStream << m_uUniqueID;
	xStream << GetContainerHash();
	xStream << GetClassification();
}

////////////////
// SkipHeader //
////////////////
// Skip the header and return the size of the remaining data
u_int Asura_ServerContainerInstance::SkipHeader(Asura_Chunk_Stream& xStream)
{
	u_short usSize = 0;
	xStream >> usSize;
	
	u_short usDummy;
	xStream >> usDummy;

	u_int uDummy;
	xStream >> uDummy;
	xStream >> uDummy;
	xStream >> uDummy;

	return static_cast<u_int>(usSize);
}

//////////////////////
// GetContainerHash //
//////////////////////
Asura_Hash_ID Asura_ServerContainerInstance::GetContainerHash() const
{
	return (m_pxContainer ? m_pxContainer->GetHash() : ASURA_HASH_ID_UNSET);
}

///////////////////////
// GetClassification //
///////////////////////
u_int Asura_ServerContainerInstance::GetClassification() const
{
	return (m_pxContainer ? m_pxContainer->GetClassification() : static_cast<u_int>(AsuraContainerClass_NotSet));
}

////////////////////////////////
// FindCollectionFromUniqueID //
////////////////////////////////
// Use the unique ID to find the relevant parent collection.
Asura_ServerContainerInstance_Collection* Asura_ServerContainerInstance::FindCollectionFromUniqueID(u_int uUniqueID)
{
	return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Asura_ServerContainerInstance_Collection
///////////////////////////////////////////////////////////////////////////////

Asura_ServerContainerInstance_Collection::Asura_ServerContainerInstance_Collection(const Asura_Container_Collection* pxContainer)
: Asura_ServerContainerInstance(pxContainer)
, m_uNumChildren(0)
, m_ppxChildren(0)
, m_pxPosOrientData(0)
{
	if (pxContainer)
	{
		m_uNumChildren = pxContainer->GetNumChildren();
		if (m_uNumChildren)
		{
			m_ppxChildren = new Asura_ServerContainerInstance*[m_uNumChildren];	
			for (u_int u=0; u<m_uNumChildren; ++u)
			{
				const Asura_Hash_ID uChildContainerHash = pxContainer->GetChildContainerHash(u);
				m_ppxChildren[u] = Asura_Container_System::CreateServerInstance(uChildContainerHash);
			}
		}
	}
}

Asura_ServerContainerInstance_Collection::~Asura_ServerContainerInstance_Collection()
{
	DeleteAllChildren();
	delete m_pxPosOrientData;
}

////////////////
// Initialise //
////////////////
void Asura_ServerContainerInstance_Collection::Initialise(Asura_ServerContainerInstance_Properties& xData)
{
	for (u_int u=0; u<m_uNumChildren; ++u)
	{
		if (m_ppxChildren[u])
		{
			// Note: This limits the instance to a depth of 4 not including the root collection.
			// It also limits the number of children in each to be 255.
			// If this isn't enough we'll need to rethink the method of generating the unique id. 
			u_int uNextID = ((m_uUniqueID<<8) + u + 1);
			if ((m_uUniqueID & 0xff000000) != 0 || u >= 0xff)
			{
				uNextID = uASURA_CONTAINER_UNIQUE_ID_INVALID;
				Asura_Assertf(0, ("Invalid Unique ID generated for server container instance (0x%08x).", GetContainerHash()));
			}
			
			m_ppxChildren[u]->SetUniqueID(uNextID);
			m_ppxChildren[u]->Initialise(xData);
		}
	}
}

/////////////////
// ChangeState //
/////////////////
// Change state of collection, updating child states.
void Asura_ServerContainerInstance_Collection::ChangeState(const Asura_Container_InputState& xInputState)
{
	PARENT::ChangeState(xInputState);
	
	if (m_ppxChildren)
	{
		const Asura_Container_Collection* pxCollection = static_cast<const Asura_Container_Collection*>(m_pxContainer);
		if (pxCollection)
		{
			Asura_Assert(pxCollection->GetNumInputs() == xInputState.GetNumInputs(), "Mismatch in number of state inputs");

			// Find the health value.
			const Asura_ServerContainerInstance_Health* const pxHealth = FindHealth(false);
			const float fHealthPercent = (pxHealth ? pxHealth->GetHealthPercent() : 100.0f);

			// Find the relevant output state. Applying any damage override, which substitutes damaged states as needed.
			Asura_Container_OutputState xOutputState;
			pxCollection->ConvertState(xInputState, fHealthPercent, xOutputState);

			for (u_int u=0; u<m_uNumChildren; ++u)
			{
				if (m_ppxChildren[u])
				{
					const Asura_Hash_ID uChildContainerHash = pxCollection->GetChildContainerHash(u);
					
					Asura_Container_InputState xChildInputState;
					xOutputState.GetChildInputState(uChildContainerHash, xChildInputState);

					m_ppxChildren[u]->ChangeState(xChildInputState);
				}
			}
		}
	}
}

///////////////////////////
// FindFromUniqueID_Safe //
///////////////////////////
// Find instance from unique ID.

// Note: modifying the container by adding/removing children may cause any
// saved data to become out-of-date (as the uUniqueID will change).
// Any such unrecognised data will be skipped, but we could use the
// uHash parameter to try to fix it up.

Asura_ServerContainerInstance* Asura_ServerContainerInstance_Collection::FindFromUniqueID_Safe(u_int uUniqueID, Asura_Hash_ID uHash, u_int uClass)
{
	if (uUniqueID == uASURA_CONTAINER_UNIQUE_ID_INVALID) return 0;
	
	if (uUniqueID == 0)
	{
		if (uHash == GetContainerHash() && uClass == GetClassification())
		{
			return this;
		}
		return 0;
	}

	while ((uUniqueID & 0xff000000) == 0)
	{
		uUniqueID <<= 8;
	}

	const u_int uChild = ((uUniqueID & 0xff000000) >> 24) - 1;
	if (uChild >= 0xff || uChild >= m_uNumChildren)
	{
		Asura_Assert(0, "Invalid child ID");
		return 0;
	}

	if (m_ppxChildren[uChild])
	{
		return m_ppxChildren[uChild]->FindFromUniqueID_Safe(uUniqueID<<8, uHash, uClass);
	}

	return 0;
}

//////////////////////
// FindFromUniqueID //
//////////////////////
// Find instance from unique ID.
Asura_ServerContainerInstance* Asura_ServerContainerInstance_Collection::FindFromUniqueID(u_int uUniqueID)
{
	if (uUniqueID == uASURA_CONTAINER_UNIQUE_ID_INVALID) return 0;
	if (uUniqueID == 0) return this;

	while ((uUniqueID & 0xff000000) == 0)
	{
		uUniqueID <<= 8;
	}

	const u_int uChild = ((uUniqueID & 0xff000000) >> 24) - 1;
	if (uChild >= 0xff || uChild >= m_uNumChildren)
	{
		Asura_Assert(0, "Invalid child ID");
		return 0;
	}

	if (m_ppxChildren[uChild])
	{
		return m_ppxChildren[uChild]->FindFromUniqueID(uUniqueID<<8);
	}

	return 0;
}

////////////////////////////////
// FindCollectionFromUniqueID //
////////////////////////////////
// Use the unique ID to find the relevant parent collection.
Asura_ServerContainerInstance_Collection* Asura_ServerContainerInstance_Collection::FindCollectionFromUniqueID(u_int uUniqueID)
{
	if (uUniqueID == uASURA_CONTAINER_UNIQUE_ID_INVALID) return 0;
	if (uUniqueID == 0) return this;

	// Move the child to the most significant byte.
	while ((uUniqueID & 0xff000000) == 0)
	{
		uUniqueID <<= 8;
	}

	const u_int uChild = ((uUniqueID & 0xff000000) >> 24) - 1;
	if (uChild >= 0xff || uChild >= m_uNumChildren)
	{
		Asura_Assert(0, "Invalid child ID");
		return 0;
	}

	// Shift the child off the end.
	uUniqueID <<= 8;

	Asura_ServerContainerInstance* pxChild = m_ppxChildren[uChild]; 

	if (pxChild && pxChild->GetClassification() == AsuraContainerClass_Collection)
	{
		return pxChild->FindCollectionFromUniqueID(uUniqueID);
	}
	else if (uUniqueID == 0)
	{
		// Child server instance wasn't a collection, so this is the parent collection.
		return this;
	}

	// Not found.
	return 0;
}

///////////////////////
// DeleteAllChildren //
///////////////////////
void Asura_ServerContainerInstance_Collection::DeleteAllChildren()
{
	if (m_ppxChildren)
	{
		for (u_int u=0; u<m_uNumChildren; ++u)
		{
			if (m_ppxChildren[u])
			{
				Asura_Container_System::DestroyServerInstance(m_ppxChildren[u]);
				m_ppxChildren[u] = 0;
			}
		}

		delete [] m_ppxChildren;
		m_ppxChildren = 0;
	}

	m_uNumChildren = 0;
}

////////////////////////
// WriteToChunkStream //
////////////////////////
const u_short usCHUNK_ASURA_SCI_VERSION_COLLECTION = 1; 

// Traverse tree, writing out instances as needed.
void Asura_ServerContainerInstance_Collection::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	// Write child data.
	if (m_ppxChildren)
	{
		for (u_int u=0; u<m_uNumChildren; ++u)
		{
			if (m_ppxChildren[u])
			{
				m_ppxChildren[u]->WriteToChunkStream(xStream);
			}
		}
	}

	// Write physics data.
	if (m_pxPosOrientData)
	{
		const u_int uSize = 3*sizeof(Asura_Vector_3) + sizeof(Asura_Quat); // Size of data.
		WriteHeader(xStream, uSize, usCHUNK_ASURA_SCI_VERSION_COLLECTION);

		xStream << m_pxPosOrientData->m_xPosition;
		xStream << m_pxPosOrientData->m_xOrient;
		xStream << m_pxPosOrientData->m_xLinVelocity;
		xStream << m_pxPosOrientData->m_xAngVelocity;
	}
}

/////////////////////////
// ReadFromChunkStream //
/////////////////////////
void Asura_ServerContainerInstance_Collection::ReadFromChunkStream(Asura_Chunk_Stream& xStream, u_short usVersion)
{
	if (usVersion > usCHUNK_ASURA_SCI_VERSION_COLLECTION)
	{
		Asura_Assertf(0, ("Unknown chunk version (%u) in Asura_ServerContainerInstance_Collection", static_cast<u_int>(usVersion)));
		return;
	}

	if (!m_pxPosOrientData)
	{
		m_pxPosOrientData = new Asura_Container_PosOrientVel;
	}
	
	xStream >> m_pxPosOrientData->m_xPosition;
	xStream >> m_pxPosOrientData->m_xOrient;
	
	if (usVersion > 0)
	{
		xStream >> m_pxPosOrientData->m_xLinVelocity;
		xStream >> m_pxPosOrientData->m_xAngVelocity;
	}
}

////////////////
// FindHealth //
////////////////
// Find the relevant health container. For ApplyDamage we only consider enabled health containers
// (if disabled it means that the collection is invulnerable to damage). For working out damage overrides
// we use the health value whether the container is enabled or not.
Asura_ServerContainerInstance_Health* Asura_ServerContainerInstance_Collection::FindHealth(bool bEnabledOnly /*= true*/)
{
	if (!m_pxContainer) return 0;
	const Asura_Container_Collection* const pxCollection = static_cast<const Asura_Container_Collection* const>(m_pxContainer);
	const int iIndex = pxCollection->FindHealthIndex();

	if (iIndex >= 0 &&  static_cast<u_int>(iIndex) < m_uNumChildren)
	{
		Asura_ServerContainerInstance* pxHealth = m_ppxChildren[iIndex];
		if (pxHealth && pxHealth->GetClassification() == AsuraContainerClass_Health)
		{
			if ( !bEnabledOnly || !pxHealth->IsOff() )
			{
				return static_cast<Asura_ServerContainerInstance_Health*>(pxHealth);
			}
		}

		Asura_Assert(pxHealth, "Couldn't find the health container.");
	}

	return 0;
}

//////////////////////
// FillPhysicsParam //
//////////////////////
// Fill out the physics parameters.
void Asura_ServerContainerInstance_Collection::FillPhysicsParam(Asura_Container_PhysicsParam*& pxPhysicsData, u_int& uNumLeftToWrite) const
{
	if (uNumLeftToWrite == 0) return; // Finished

	if (m_pxPosOrientData)
	{
		pxPhysicsData->m_uUniqueID = m_uUniqueID;
		pxPhysicsData->m_xPosition = m_pxPosOrientData->m_xPosition;
		pxPhysicsData->m_xOrient   = m_pxPosOrientData->m_xOrient;

		--uNumLeftToWrite;
		++pxPhysicsData;

		if (uNumLeftToWrite == 0) return; // Finished
	}

	if (m_ppxChildren)
	{
		for (u_int u=0; u<m_uNumChildren; ++u)
		{
			if (m_ppxChildren[u])
			{
				m_ppxChildren[u]->FillPhysicsParam(pxPhysicsData, uNumLeftToWrite);
				if (uNumLeftToWrite == 0) return; // Finished
			}
		}
	}
}

////////////////////
// FillFloatParam //
////////////////////
// Fill out the float parameters. Appends float parameters to creation message.
void Asura_ServerContainerInstance_Collection::FillFloatParam(Asura_Container_FloatParam*& pxFloatData, u_int& uNumLeftToWrite) const
{
	if (uNumLeftToWrite == 0) return; // Finished

	if (m_ppxChildren)
	{
		for (u_int u=0; u<m_uNumChildren; ++u)
		{
			if (m_ppxChildren[u])
			{
				m_ppxChildren[u]->FillFloatParam(pxFloatData, uNumLeftToWrite);
				if (uNumLeftToWrite == 0) return; // Finished
			}
		}
	}
}

//////////////////////////////
// HasDamageOverrideChanged //
//////////////////////////////
// Check whether the damage override has changed.
bool Asura_ServerContainerInstance_Collection::HasDamageOverrideChanged(float fOldHealthPercent, float fNewHealthPercent) const
{
	if (!m_pxContainer) return false;

	const Asura_Container_Collection* const pxCollection = static_cast<const Asura_Container_Collection*>(m_pxContainer);
	return pxCollection->HasDamageOverrideChanged(m_xInputState, fOldHealthPercent, fNewHealthPercent);
}

///////////////////////
// StorePhysicsParam //
///////////////////////
// Store the physics data. Return true if added new data.
bool Asura_ServerContainerInstance_Collection::StorePhysicsParam(const Asura_Physics_Object* pxPhysics)
{
	bool bAdded = false;
	if (!m_pxPosOrientData)
	{
		m_pxPosOrientData = new Asura_Container_PosOrientVel;
		bAdded = true;
	}

	m_pxPosOrientData->SetDataFromPhysics(pxPhysics);
	
	return bAdded;
}

////////////////////////
// GetNumPhysicsParam //
////////////////////////
void Asura_ServerContainerInstance_Collection::GetNumPhysicsParam(u_int& uNumber) const
{
	if (m_pxPosOrientData)
	{
		++uNumber;
	}

	if (m_ppxChildren)
	{
		for (u_int u=0; u<m_uNumChildren; ++u)
		{
			if (m_ppxChildren[u])
			{
				m_ppxChildren[u]->GetNumPhysicsParam(uNumber);
			}
		}
	}
}

////////////////////////
// GetPhysicsVelocity //
////////////////////////
// Look up the stored physics velocities for the given container instance.
// Return true if found, in which case xLinVelocity and xAngVelocity are set with the output data.
bool Asura_ServerContainerInstance_Collection::GetPhysicsVelocity(Asura_Vector_3& xLinVelocity, Asura_Vector_3& xAngVelocity) const
{
	if (m_pxPosOrientData)
	{
		xLinVelocity = m_pxPosOrientData->m_xLinVelocity;
		xAngVelocity = m_pxPosOrientData->m_xAngVelocity;
		return true;
	}

	return false;
}

//////////////
// Traverse //
//////////////
// Traverse the tree with callback functor.
void Asura_ServerContainerInstance_Collection::Traverse(Asura_Container_ServerFunctor& xFunctor) const
{
	xFunctor.Process(this);

	for (u_int u=0; u<m_uNumChildren; ++u)
	{
		if (m_ppxChildren[u])
		{
			m_ppxChildren[u]->Traverse(xFunctor);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Asura_ServerContainerInstance_Health
///////////////////////////////////////////////////////////////////////////////
Asura_ServerContainerInstance_Health::Asura_ServerContainerInstance_Health(const Asura_Container_Health* pxContainer)
: Asura_ServerContainerInstance(pxContainer)
, m_fHealth(100.0f)
{
	if (pxContainer)
	{
		m_fHealth = Asura_Maths::ClampToRange(pxContainer->GetInitialHealth(), 0.0f, pxContainer->GetMaxHealth());
	}
}

const u_short usCHUNK_ASURA_SCI_VERSION_HEALTH = 0; 

// Chunkstream functions.
void Asura_ServerContainerInstance_Health::ReadFromChunkStream(Asura_Chunk_Stream& xStream, u_short usVersion)
{
	if (usVersion > usCHUNK_ASURA_SCI_VERSION_HEALTH)
	{
		Asura_Assertf(0, ("Unknown chunk version (%u) in Asura_ServerContainerInstance_Health", static_cast<u_int>(usVersion)));
		return;
	}
	
	xStream >> m_fHealth;
	ClampToRange();
}

void Asura_ServerContainerInstance_Health::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	const u_int uSize = sizeof(float); // Size of data.
	WriteHeader(xStream, uSize, usCHUNK_ASURA_SCI_VERSION_HEALTH);

	xStream << m_fHealth;
}

//////////////////////
// GetHealthPercent //
//////////////////////
// Get the health as a percentage of the maximum value.
float Asura_ServerContainerInstance_Health::GetHealthPercent() const
{
	const Asura_Container_Health* const pxContainer = static_cast<const Asura_Container_Health*>(m_pxContainer);
	if (pxContainer)
	{
		const float fMax = pxContainer->GetMaxHealth();
		if (fMax > 0.0f && m_fHealth != fMax)
		{
			const float fPercent = 100.0f * m_fHealth / fMax;
			return Asura_Maths::ClampToRange(fPercent, 0.0f, 100.0f);
		}
	}

	return 100.0f;
}

///////////////
// SetHealth //
///////////////
void Asura_ServerContainerInstance_Health::SetHealth(float fHealth)
{
	m_fHealth = fHealth;
	ClampToRange();
}

/////////////////
// ApplyDamage //
/////////////////
// Returns true if the health was changed.
bool Asura_ServerContainerInstance_Health::ApplyDamage(float fDamage)
{
	const float fInitialHealth = m_fHealth;
	m_fHealth -= fDamage;
	ClampToRange();
	return (fInitialHealth != m_fHealth);
}

//////////////////
// ClampToRange //
//////////////////
void Asura_ServerContainerInstance_Health::ClampToRange()
{
	const Asura_Container_Health* const pxContainer = static_cast<const Asura_Container_Health*>(m_pxContainer);
	if (pxContainer)
	{
		m_fHealth = Asura_Maths::ClampToRange(m_fHealth, 0.0f, pxContainer->GetMaxHealth());
	}
}

////////////////////
// FillFloatParam //
////////////////////
// Fill out the float parameters. Appends health values to creation message.
void Asura_ServerContainerInstance_Health::FillFloatParam(Asura_Container_FloatParam*& pxFloatData, u_int& uNumLeftToWrite) const
{
	Asura_Assert(uNumLeftToWrite > 0, "Error: tried to write out too many float parameters.");
	if (uNumLeftToWrite > 0)
	{
		pxFloatData->m_uUniqueID   = m_uUniqueID;
		pxFloatData->m_fFloatValue = m_fHealth;

		--uNumLeftToWrite;
		++pxFloatData;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Asura_ServerContainerInstance_Mesh
///////////////////////////////////////////////////////////////////////////////

Asura_ServerContainerInstance_Mesh::Asura_ServerContainerInstance_Mesh(const Asura_Container_Mesh* pxContainer)
: Asura_ServerContainerInstance(pxContainer)
, m_pxPosOrientData(0)
{
}

Asura_ServerContainerInstance_Mesh::~Asura_ServerContainerInstance_Mesh()
{
	delete m_pxPosOrientData;
}

//////////////////////
// FillPhysicsParam //
//////////////////////
// Fill out the physics parameters.
void Asura_ServerContainerInstance_Mesh::FillPhysicsParam(Asura_Container_PhysicsParam*& pxPhysicsData, u_int& uNumLeftToWrite) const
{
	if (m_pxPosOrientData && uNumLeftToWrite > 0)
	{
		pxPhysicsData->m_uUniqueID = m_uUniqueID;
		pxPhysicsData->m_xPosition = m_pxPosOrientData->m_xPosition;
		pxPhysicsData->m_xOrient   = m_pxPosOrientData->m_xOrient;

		--uNumLeftToWrite;
		++pxPhysicsData;
	}
}

///////////////////////
// StorePhysicsParam //
///////////////////////
// Store the physics data. Return true if added new data.
bool Asura_ServerContainerInstance_Mesh::StorePhysicsParam(const Asura_Physics_Object* pxPhysics)
{
	bool bAdded = false;
	if (!m_pxPosOrientData)
	{
		m_pxPosOrientData = new Asura_Container_PosOrientVel;
		bAdded = true;
	}

	m_pxPosOrientData->SetDataFromPhysics(pxPhysics);

	return bAdded;
}

////////////////////////
// GetNumPhysicsParam //
////////////////////////
void Asura_ServerContainerInstance_Mesh::GetNumPhysicsParam(u_int& uNumber) const
{
	if (m_pxPosOrientData)
	{
		++uNumber;
	}
}

////////////////////////
// GetPhysicsVelocity //
////////////////////////
// Look up the stored physics velocities for the given container instance.
// Return true if found, in which case xLinVelocity and xAngVelocity are set with the output data.
bool Asura_ServerContainerInstance_Mesh::GetPhysicsVelocity(Asura_Vector_3& xLinVelocity, Asura_Vector_3& xAngVelocity) const
{
	if (m_pxPosOrientData)
	{
		xLinVelocity = m_pxPosOrientData->m_xLinVelocity;
		xAngVelocity = m_pxPosOrientData->m_xAngVelocity;
		return true;
	}

	return false;
}


////////////////////////
// WriteToChunkStream //
////////////////////////
const u_short usCHUNK_ASURA_SCI_VERSION_MESH = 1;

void Asura_ServerContainerInstance_Mesh::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	// Write physics data.
	if (m_pxPosOrientData)
	{
		const u_int uSize = 3*sizeof(Asura_Vector_3) + sizeof(Asura_Quat); // Size of data.
		WriteHeader(xStream, uSize, usCHUNK_ASURA_SCI_VERSION_MESH);

		xStream << m_pxPosOrientData->m_xPosition;
		xStream << m_pxPosOrientData->m_xOrient;
		xStream << m_pxPosOrientData->m_xLinVelocity;
		xStream << m_pxPosOrientData->m_xAngVelocity;
	}
}


/////////////////////////
// ReadFromChunkStream //
/////////////////////////
void Asura_ServerContainerInstance_Mesh::ReadFromChunkStream(Asura_Chunk_Stream& xStream, u_short usVersion)
{
	if (usVersion > usCHUNK_ASURA_SCI_VERSION_MESH)
	{
		Asura_Assertf(0, ("Unknown chunk version (%u) in Asura_ServerContainerInstance_Mesh", static_cast<u_int>(usVersion)));
		return;
	}

	if (!m_pxPosOrientData)
	{
		m_pxPosOrientData = new Asura_Container_PosOrientVel;
	}
	
	xStream >> m_pxPosOrientData->m_xPosition;
	xStream >> m_pxPosOrientData->m_xOrient;

	if (usVersion > 0)
	{
		xStream >> m_pxPosOrientData->m_xLinVelocity;
		xStream >> m_pxPosOrientData->m_xAngVelocity;
	}
}

////////////////
// Initialise //
////////////////
void Asura_ServerContainerInstance_Mesh::Initialise(Asura_ServerContainerInstance_Properties& xData)
{
	const Asura_Container_Mesh* pxMeshContainer = static_cast<const Asura_Container_Mesh*>(m_pxContainer);
	if (pxMeshContainer && pxMeshContainer->ShouldBlockWaypointLinks())
	{
		// Set if any of the meshes are flagged as blocking links.
		xData.m_bBlockWaypointLinks = true;
	}
}

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

