///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ClientEntity_Container.cpp
//
// Created by Martin G.Harris (6/5/2008)
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
// Client entity that manages a container instance.
//
///////////////////////////////////////////////////////////////////////////////

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

#ifdef ASURA_USE_CONTAINER_SYSTEM

// Includes
#include "Core/Asura_LOS.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Entity/Asura_PosAndOriInterpolator.h"
#include "Entity/Container/Asura_ClientContainerInstance.h"
#include "Entity/Container/Asura_Container_Messages.h"
#include "Entity/Container/Asura_Container_System.h"
#include "Entity/Container/Asura_ContainerRegistry.h"
#include "Entity/Container/Asura_ContainerState.h"
#include "Entity/Container/Asura_ServerEntity_Container.h"
#include "Network/Asura_Guid.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Rendering/Asura_Render.h"
#include "Physics/Asura_Physics_Object.h"

// Register the client entity.
Asura_ContainerRegisterClientEntity<Asura_ClientEntity_Container> xRegisterClientEntity(false);

///////////////////////////////////////////////////////////////////////////////
// Asura_Container_PhysicsBlock
///////////////////////////////////////////////////////////////////////////////
Asura_Container_PhysicsBlock::~Asura_Container_PhysicsBlock()
{
	delete m_pxInterpolator;
	m_pxInterpolator = 0;

	delete m_pxPosOri;
	m_pxPosOri = 0;

	delete m_pxNext;
	m_pxNext = 0;
}

///////////////////////////////////////////////////////////////////////////////
// Asura_Container_PhysicsBlock_Storage
///////////////////////////////////////////////////////////////////////////////

//////////////////////////////
// CopyDataToDynamicPhysics //
//////////////////////////////
// Copy data to dynamic physics object to keep non-server machines in step with the server.
// Also restores dynamic physics pos/ori/vel for server machines after a state change.
void Asura_Container_PhysicsBlock_Storage::CopyDataToDynamicPhysics()
{
	if (!m_pxHead) return; // No stored physics blocks.

	Asura_ServerEntity_Container* pxServer = 0;

	if (Asura_Network::IsServer())
	{
		pxServer = Asura_ServerEntity_Container::FindEntity(GetOwner());
		Asura_Assert(pxServer, "Null server entity.");
	}

	const bool bPendingVelocityData = (pxServer ? pxServer->HasPendingVelocityData() : false);

	// Loop over the stored physics blocks copying data back to the physics objects.
	for (Asura_Container_PhysicsBlock* pxCurrent = m_pxHead;
		 pxCurrent;
		 pxCurrent = pxCurrent->m_pxNext)
	{
		Asura_Physics_Object* pxPhysics = pxCurrent->m_pxPhysics;
		if (!pxPhysics) continue;

		const Asura_Container_PosOrient* const pxPosOri = pxCurrent->m_pxPosOri;

		// MGH 25/9/2008: Copy stored velocity data from the server entity. To avoid message bloat I didn't include
		// the velocity in the update/create messages as the data is only needed on the server.
		if (pxServer)
		{
			if (pxPosOri != 0 || bPendingVelocityData)
			{
				Asura_Vector_3 xLinVelocity;
				Asura_Vector_3 xAngVelocity;
				if (pxServer->GetPhysicsVelocity(pxCurrent->m_uUniqueID, xLinVelocity, xAngVelocity))
				{
					// Don't wake up objects that aren't moving.
					if (!xLinVelocity.ApproximatelyEquals(Asura_Maths::ZeroVector3) ||
						!xAngVelocity.ApproximatelyEquals(Asura_Maths::ZeroVector3) )
					{
						pxPhysics->SetVelocities(xLinVelocity, xAngVelocity);
					}
				}
			}
		}

		// Now set up the pos/ori.
		CopyPosOriToDynamicPhysics(pxCurrent);
	}
}

////////////////////////////////
// CopyPosOriToDynamicPhysics //
////////////////////////////////
// Copy the position/orientation back to the relevant physics object.
// Return true, if we initialised the pos/ori data in the physics object.
bool Asura_Container_PhysicsBlock_Storage::CopyPosOriToDynamicPhysics(Asura_Container_PhysicsBlock* pxCurrent)
{
	if (!pxCurrent) return false;

	Asura_Physics_Object* pxPhysics = pxCurrent->m_pxPhysics;
	if(!pxPhysics) return false;

	const Asura_Container_PosOrient* const pxPosOri = pxCurrent->m_pxPosOri;
	if (pxPosOri)
	{
		// Copy the data to the physics block from the pos/ori store.
		const Asura_Vector_3& xNewPos = pxPosOri->m_xPosition;

		Asura_Matrix_3x3 xNewOri;
		pxPosOri->m_xOrient.ToMatrix(xNewOri);

		MovePhysics(pxPhysics, xNewPos, xNewOri);
		return true;
	}
	
	// Copy the data to the physics block from the interpolator.
	const Asura_PosAndOriInterpolator* const pxInterpolator = pxCurrent->m_pxInterpolator;
	if (pxInterpolator)
	{
		Asura_Assert(pxPhysics->GetType() == ASURA_PHYSICS_OBJECT_EXTERNALCONTROL, "Physics object should be external control!");

		Asura_Vector_3 xNewPos;
		Asura_Vector_3 xNewVel;
		Asura_Matrix_3x3 xNewOri;
		pxInterpolator->GetCurrentPosition(xNewPos, xNewVel);
		pxInterpolator->GetCurrentOrientation(xNewOri);

		MovePhysics(pxPhysics, xNewPos, xNewOri);
		return true;
	}

	return false;
}

/////////////////
// MovePhysics //
/////////////////
// Move physics object if necessary.
// Don't set pos/orient if they haven't changed as we don't want to
// wake up the physics object unnecessarily.
void Asura_Container_PhysicsBlock_Storage::MovePhysics(Asura_Physics_Object* pxPhysics, const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOri)
{
	if (!pxPhysics) return;

	if (!xPos.ApproximatelyEquals(pxPhysics->GetPosition()))
	{
		pxPhysics->SetPosition(xPos);
	}

	if (!xOri.ApproximatelyEquals(pxPhysics->GetOrientation()))
	{
		pxPhysics->SetOrientation(xOri);
	}
}

///////////////
// FindOrAdd //
///////////////
// Find the physics block with the relevant unique ID or add a new one if needed.
Asura_Container_PhysicsBlock& Asura_Container_PhysicsBlock_Storage::FindOrAdd(const u_int uUniqueID, bool* pbFound /*=0*/)
{
	for (Asura_Container_PhysicsBlock* pxCurrent = m_pxHead;
		 pxCurrent;
		 pxCurrent = pxCurrent->m_pxNext)
	{
		if (pxCurrent->m_uUniqueID == uUniqueID)
		{
			if (pbFound) *pbFound = true;
			return *pxCurrent; // Found
		}
	}
	
	// Add new entry
	Asura_Container_PhysicsBlock* pxNew = new Asura_Container_PhysicsBlock;
	pxNew->m_uUniqueID = uUniqueID;
	pxNew->m_pxNext = m_pxHead;
	m_pxHead = pxNew;

	if (pbFound) *pbFound = false;
	++m_iNumElements;
	return *pxNew; 
}

////////////////////////
// ResetPhysicsBlocks //
////////////////////////
void Asura_Container_PhysicsBlock_Storage::ResetPhysicsBlocks()
{
	for (Asura_Container_PhysicsBlock* pxCurrent = m_pxHead;
		 pxCurrent;
		 pxCurrent = pxCurrent->m_pxNext)
	{
		pxCurrent->m_pxPhysics = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Asura_ClientEntity_Container
///////////////////////////////////////////////////////////////////////////////

// Static data
Asura_Hashed_List<Asura_ClientEntity_Container> Asura_ClientEntity_Container::s_xList(6);


Asura_ClientEntity_Container::Asura_ClientEntity_Container()
: m_uContainerHash(ASURA_HASH_ID_UNSET)
, m_pxInstance(0)
, m_pxCachedLOSAABB(0)
, m_fStateTimeStamp(0.0f)
, m_uFlags(0)
{
	Classification = AsuraEntityClass_Container;
	s_xList.AddToList(this);

	m_xInputState.Reset();

	m_xPhysicsBlockStorage.SetOwner(GetGuid());

	// Many containers just sit around and don't need the pos/ori interpolator.
	// Those that do have dynamic physics objects, may need multiple interpolators in any case.
#if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
	if ( m_pxPosAndOriInterpolator )
	{
		delete m_pxPosAndOriInterpolator;
		m_pxPosAndOriInterpolator = NULL;
	}
#endif // #if !defined(ASURA_NO_NETWORK_LAG_COMPENSATION)
}

Asura_ClientEntity_Container::~Asura_ClientEntity_Container()
{
	s_xList.RemoveFromList(this);

	// Parent destructor will remove this from the LOS list.
	m_bAddedToLOSList = false;

	DestroyInstance();
}

///////////////////
// HandleMessage //
///////////////////
bool Asura_ClientEntity_Container::HandleMessage(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage, "Null message");
	if (!pxMessage) return false;

	switch (pxMessage->GetMsgID())
	{
	case ASURA_MSG_CLIENTCREATE:
		{
			const Asura_Message_Container_Create* pxData = static_cast<const Asura_Message_Container_Create*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				// Fill out and create the instance, if not already done.
				pxData->FillOut(this, pxMessage->GetSentTime());
			}

			return true;
		}

	case ASURA_MSG_CLIENTUPDATE:
		{
			// No dynamic physics objects.
			const Asura_Message_Container_ClientUpdate* pxData = static_cast<const Asura_Message_Container_ClientUpdate*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				pxData->FillOut(this, pxMessage->GetSentTime());
			}

			return true;
		}

	case ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED1:
		{
			// One dynamic physics object.
			const Asura_Message_Container_ClientUpdate_Extended1* pxData = static_cast<const Asura_Message_Container_ClientUpdate_Extended1*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				pxData->FillOut(this, pxMessage->GetSentTime());
			}

			return true;
		}

	case ASURA_MSG_CONTAINER_CLIENTUPDATE_EXTENDED:
		{
			// Upto uMAX_DYNAMIC_PHYSICS_PER_CONTAINER_ENTITY dynamic physics objects.
			const Asura_Message_Container_ClientUpdate_Extended* pxData = static_cast<const Asura_Message_Container_ClientUpdate_Extended*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				pxData->FillOut(this, pxMessage->GetSentTime());
			}

			return true;
		}

	case ASURA_MSG_CONTAINER_CHANGE_HEALTH:
		{
			const Asura_Message_Container_ChangeHealth* pxData = static_cast<const Asura_Message_Container_ChangeHealth*>(pxMessage->GetData());
			Asura_Assert(pxData, "Null message data pointer");
			if (pxData)
			{
				SetHealth(pxData->m_uUniqueID, pxData->m_fHealth, pxMessage->GetSentTime());
			}

			return true;
		}
	
	default:
		break;
	}

	return PARENT::HandleMessage(pxMessage);
}

//////////////////////////////
// SendRequestCreateMessage //
//////////////////////////////
void Asura_ClientEntity_Container::SendRequestCreateMessage(Asura_Guid uGuid)
{
	if (uGuid < ASURA_GUID_CLIENT_START)
	{
		Asura_Assertf(0, ("Invalid guid for create request (%u)", uGuid));
		return;
	}

	Asura_Message* pxRequestMsg = Asura_Network::CreateMessage(ASURA_MSG_CLIENT_REQUESTCREATEMSG, 0, AsuraEntityClass_Container);
	if ( pxRequestMsg )
	{
		pxRequestMsg->SetToGuid( uGuid );
		pxRequestMsg->SetFromGuid( Asura_Network::GetClientID() );
		Asura_Network::Send( pxRequestMsg );
	}
}

////////////
// Create //
////////////
// Create from creation message.
void Asura_ClientEntity_Container::Create(const Asura_Message* pxMessage)
{
	Asura_Assert(pxMessage, "Null message pointer");
	if (!pxMessage) return;

	if (pxMessage->GetMsgID() != ASURA_MSG_CLIENTCREATE)
	{
		Asura_Assert(0, "Wrong msg ID!");
		return;
	}

	if (!pxMessage->GetData())
	{
		Asura_Assert(0, "Null message data pointer");
		return;
	}

	Asura_ClientEntity_Container* pxClient = Asura_ContainerRegistry::CreateClientEntity();
	if (pxClient)
	{
		pxClient->HandleMessage(pxMessage);
	}
}

////////////////////
// PreStateChange //
////////////////////
// Called just before a state change.
void Asura_ClientEntity_Container::PreStateChange()
{
	Asura_Container_System::SetDoingStateChange(true);
}

/////////////////////
// PostStateChange //
/////////////////////
// Called just after a state change.
void Asura_ClientEntity_Container::PostStateChange()
{
	Asura_Container_System::SetDoingStateChange(false);
}

////////////////////
// CreateInstance //
////////////////////
void Asura_ClientEntity_Container::CreateInstance()
{
	// Clear physics blocks, but not the interpolators.
	m_xPhysicsBlockStorage.ResetPhysicsBlocks();

	const bool bTurnOff = m_xInputState.IsOff();

	if (m_pxInstance)
	{
		// We are unlikely to ever want to change container type, but just in case.
		// Also destroy instance if it is being switched "off".
		if (m_pxInstance->GetContainerHash() != m_uContainerHash || bTurnOff)
		{
			PreStateChange();
			DestroyInstance();
			PostStateChange();
		}
	}

	if (!m_pxInstance && !bTurnOff)
	{
		// Create the instance from scratch. Just create the collections and health containers.
		m_pxInstance = Asura_Container_System::CreateClientInstance(m_uContainerHash);

		if (m_pxInstance)
		{
			// Restore any float parameters (e.g. health data).
			CopyFloatParamToInstance();
		}
	}

	int iNumDynamicPhysics = 0;

	if (m_pxInstance)
	{
		// Change to new state, creating and destroying sub-containers as necessary.
		// Also unlink/relink marker attachments as needed.
		// We've already destroyed the instance for the "off" case, so the root container must be on.
		PreStateChange();
		m_pxInstance->ChangeState(m_xInputState);
		PostStateChange();
	
		// Collect data about the instance and initialise it.
		Asura_ContainerInstance_Properties xData(this, GetGuid());

		m_pxInstance->Initialise(0, xData);
		m_pxInstance->TeleportTo(GetCurrentPosition(), GetCurrentOrientation());

		// Set up the flags from the data.
		m_bShouldCallRender		  = (xData.m_iNumRender > 0);
		m_bShouldCallRenderShadow = (xData.m_iNumRenderShadow > 0);
		m_bShouldCallUpdate		  = (xData.m_iNumUpdate > 0);
		m_bEnableLOSTest		  = (xData.m_iNumLOS > 0);
		m_bCanCacheAABBForLOS	  = (xData.m_iNumMovingLOS == 0) && m_bEnableLOSTest;
		m_bNeedToRecalculateAABBForLOS = true;

#ifdef ASURA_TOOLS
		m_bShouldCallRender = true; // Always render in Tools.
		m_bShouldCallUpdate = true; // Always update in Tools.
#endif //ASURA_TOOLS

		// Set up physics.
		iNumDynamicPhysics = xData.m_iNumDynamicPhysics;

		// Set up LOS.
		if (!m_bCanCacheAABBForLOS)
		{
			delete m_pxCachedLOSAABB;
			m_pxCachedLOSAABB = 0;
		}
		else if (!m_pxCachedLOSAABB)
		{
			m_pxCachedLOSAABB = new Asura_Bounding_Box;
			m_pxCachedLOSAABB->Reset();
		}

		if (m_bEnableLOSTest)
		{
			AddToLOSList();
		}
		else
		{
			RemoveFromLOSList();
		}		
	}
	else
	{
		RemoveFromLOSList();

		// Clear all of the flags now, including m_bAddedToLOSList.
		m_uFlags = 0;
	}

	// Add or remove from the updateable list as needed.
	const bool bShouldUpdate = m_bShouldCallUpdate || (!Asura_Network::IsServer() && iNumDynamicPhysics>0);
	if (IsUpdateable() != bShouldUpdate)
	{
		if (bShouldUpdate)
		{
			AddToUpdateableClientList(this);
		}
		else
		{
			RemoveFromUpdateableClientList(this);
		}
	}
	
	Asura_Assertf(m_pxInstance || m_uContainerHash == ASURA_HASH_ID_UNSET || m_xInputState.IsOff(), ("Failed to create container instance (0x%08x)", m_uContainerHash));
}

/////////////////////
// DestroyInstance //
/////////////////////
void Asura_ClientEntity_Container::DestroyInstance()
{
	Asura_Container_System::DestroyInstance(m_pxInstance);
	m_pxInstance = 0;

	delete m_pxCachedLOSAABB;
	m_pxCachedLOSAABB = 0;

	m_xPhysicsBlockStorage.ResetPhysicsBlocks();
}

///////////////////////
// AddDynamicPhysics //
///////////////////////
// Add to the array of physics blocks.
// Return true, if we initialised the pos/ori data in the physics object.
bool Asura_ClientEntity_Container::AddDynamicPhysics(Asura_Physics_Object* pxPhysics, u_int uUniqueID)
{
	if (uUniqueID == uASURA_CONTAINER_UNIQUE_ID_INVALID)
	{
		Asura_Assert(0, "Invalid container unique ID");
		return false;
	}

	bool bFound = false;
	Asura_Container_PhysicsBlock& xBlock = m_xPhysicsBlockStorage.FindOrAdd(uUniqueID, &bFound);

	// MGH 19/9/2008: When a number of meshes (e.g. different damaged versions) in a collection are sharing
	// the same physics block, then only one can be enabled at any given time.
	// Otherwise load/save and update messaging won't work properly.
	Asura_Assertf(xBlock.m_pxPhysics == 0, ("Collection has more than one enabled mesh with shared dynamic physics turned on! (entity %u)", GetGuid()));

	xBlock.m_pxPhysics = pxPhysics;

	if (bFound)
	{
		// MGH 25/11/2008: Ensure that any stored position/orientation data is copied
		// to the physics block immediately. This is to fix a problem in which the pos/ori
		// would be at the origin during the initialisation, causing PFX (that were attached
		// to physics objects) to be created at the container origin and then moved on the
		// first frame to the correct position.
		return m_xPhysicsBlockStorage.CopyPosOriToDynamicPhysics(&xBlock);
	}
	return false;
}

/////////////////////////
// ProcessPhysicsParam //
/////////////////////////
// Process dynamic physics pos/ori information from the server.
void Asura_ClientEntity_Container::ProcessPhysicsParam(u_int uNumPhysicsParam, const Asura_Container_PhysicsParam* pxPhysicsData, float fServerTime)
{
	if (!pxPhysicsData) return;

	for (u_int u=0; u<uNumPhysicsParam; ++u)
	{
		const Asura_Container_PhysicsParam& xData = pxPhysicsData[u];
		ProcessDynamicPhysicsData(xData.m_uUniqueID, xData.m_xPosition, xData.m_xOrient, fServerTime);
	}
}

///////////////////////////////
// ProcessDynamicPhysicsData //
///////////////////////////////
// Process dynamic physics pos/ori information from the server.
void Asura_ClientEntity_Container::ProcessDynamicPhysicsData(u_int uUniqueID,
															 const Asura_Vector_3& xPosition,
															 const Asura_Quat& xOrient,
															 float fServerTime)
{
	if (uUniqueID == uASURA_CONTAINER_UNIQUE_ID_INVALID) return;

	Asura_Container_PhysicsBlock& xBlock = m_xPhysicsBlockStorage.FindOrAdd(uUniqueID);

	if (Asura_Network::IsServer())
	{
		// Server machines need to store this data so that they can restore pos/ori data after an entity reactivates.
		// (On deactivation its physics block will be deleted.)
		if (!xBlock.m_pxPosOri)
		{
			xBlock.m_pxPosOri = new Asura_Container_PosOrient;
		}

		Asura_Container_PosOrient* pxPosOri = xBlock.m_pxPosOri;
		if (pxPosOri)
		{
			pxPosOri->m_xPosition = xPosition;
			pxPosOri->m_xOrient   = xOrient;
		}
	}
	else
	{
		// Non-server machines have interpolators.
		if (xBlock.m_pxInterpolator == 0)
		{
			xBlock.m_pxInterpolator = new Asura_PosAndOriInterpolator;
		}

		Asura_PosAndOriInterpolator* pxInterpolator = xBlock.m_pxInterpolator;
		if (pxInterpolator)
		{
			// Note: update messages typically arrive every 0.1s, but the time difference between creation
			// and update can be very small (e.g. 0.0001s), resulting in division by a small number,
			// which was causing bogus huge position vectors.
			const float fMinTimeDiff = 0.04f;
			const float fLastServerTime = (pxInterpolator->GetLastReportedServerTime() - pxInterpolator->GetProcessNewDataIncrement());
			if (Asura_Maths::Abs(fLastServerTime - fServerTime) > fMinTimeDiff)
			{
				// Add the data to the interpolator.
				pxInterpolator->ProcessNewData(xPosition, xOrient, fServerTime, 0);
			}
		}
	}
}

////////////////////////
// ProcessStateChange //
////////////////////////
// State has changed or entity has been activated. Need to copy back any stored pos/ori data to the physics.
void Asura_ClientEntity_Container::ProcessStateChange()
{
	m_xPhysicsBlockStorage.CopyDataToDynamicPhysics();

	// Update any project-specific stuff.
	PostChangeInitialisation();
}

///////////////////////
// ProcessFloatParam //
///////////////////////
// Process float parameters (such as, the health of sub-containers).
// On receiving a creation message we allocate storage for the floats and then make a
// copy of the data that came from the server. We'll need the data whenever we change
// state or activate the entity.
void Asura_ClientEntity_Container::ProcessFloatParam(u_int uNumFloatParam, const Asura_Container_FloatParam* pxFloatParamData, float fServerTime)
{
	// Allocate memory for storage if needed.
	if (m_xFloatParamStorage.GetSize() != uNumFloatParam)
	{
		m_xFloatParamStorage.Allocate(uNumFloatParam);
	}

	// Update the data in the storage array.
	if (uNumFloatParam > 0 && pxFloatParamData)
	{
		for (u_int u=0; u<uNumFloatParam; ++u)
		{
			m_xFloatParamStorage.Add(pxFloatParamData[u].m_uUniqueID, pxFloatParamData[u].m_fFloatValue, fServerTime);
		}
	}
}

//////////////////
// AddToLOSList //
//////////////////
// Add to LOS list if not already in the list. Set flag.
void Asura_ClientEntity_Container::AddToLOSList()
{
	if (!m_bAddedToLOSList)
	{
		AddToLineOfSightList();
		m_bAddedToLOSList = true;
	}
}

///////////////////////
// RemoveFromLOSList //
///////////////////////
// Remove from LOS list if present. Clear flag.
void Asura_ClientEntity_Container::RemoveFromLOSList()
{
	if (m_bAddedToLOSList)
	{
		RemoveFromLineOfSightList();
		m_bAddedToLOSList = false;
	}
}

////////////
// Update //
////////////
void Asura_ClientEntity_Container::Update()
{
	PARENT::Update();

	// Only copy data from interpolator to physics objects for non-server machines.
	if (!Asura_Network::IsServer())
	{
		m_xPhysicsBlockStorage.CopyDataToDynamicPhysics();
	}

	if (m_pxInstance && m_bShouldCallUpdate)
	{
		m_pxInstance->Update();
	}
}

////////////
// Render //
////////////
void Asura_ClientEntity_Container::Render()
{
	if (!m_bShouldCallRender)
	{
		// None of the containers require a Render call.
		return;
	}

	if (IsInvisible())
	{
		return;
	}

	if (!Asura_Render::IsSystemEnabled(ASURA_RENDER_SYSTEM_OBJECTS))
	{
		return;
	}

	// TODO check global visibility here?

	if (m_pxInstance)
	{
		m_pxInstance->Render(GetCurrentPosition(), GetCurrentOrientation());
	}
}

//////////////////
// RenderShadow //
//////////////////
void Asura_ClientEntity_Container::RenderShadow()
{
	if (!m_bShouldCallRenderShadow)
	{
		// None of the containers require a RenderShadow call.
		return;
	}

	if (IsInvisible())
	{
		return;
	}

	if (!Asura_Render::IsSystemEnabled(ASURA_RENDER_SYSTEM_OBJECTS))
	{
		return;
	}

	// TODO check global visibility here?

	if (m_pxInstance)
	{
		m_pxInstance->RenderShadow(0);
	}
}

////////////////
// TeleportTo //
////////////////
void Asura_ClientEntity_Container::TeleportTo( const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation )
{
	// The cached LOS AABB is now invalid.
	m_bNeedToRecalculateAABBForLOS = true;

	PARENT::TeleportTo(xPosition, xOrientation);

	if (m_pxInstance)
	{
		m_pxInstance->TeleportTo(xPosition, xOrientation);
	}
}

//////////////////////
// OnEntityActivate //
//////////////////////
void Asura_ClientEntity_Container::OnEntityActivate()
{
	PARENT::OnEntityActivate();

	Asura_Container_System::SetDoingActivate(true);
	
	// Create instance or update it as necessary.
	CreateInstance();

	// Restore any pos/ori data for dynamic physics objects.
	ProcessStateChange();

	Asura_Container_System::SetDoingActivate(false);
}

////////////////////////
// OnEntityDeactivate //
////////////////////////
void Asura_ClientEntity_Container::OnEntityDeactivate()
{
	// Removes from LOS list, render list, updateable list as necessary.
	PARENT::OnEntityDeactivate();

	// Clear all of the flags now, including m_bAddedToLOSList.
	m_uFlags = 0;

	Asura_Container_System::SetDoingDeactivate(true);

	// Destroy the instance.
	DestroyInstance();

	// Update any project-specific stuff.
	PostChangeInitialisation();

	Asura_Container_System::SetDoingDeactivate(false);
}

////////////////////////
// GetLineOfSightAABB //
////////////////////////
// Returns the conservative axially-aligned WORLD SPACE box covering the entity.
void Asura_ClientEntity_Container::GetLineOfSightAABB( Asura_Bounding_Box& xAABB )
{
	if (m_pxCachedLOSAABB && m_bCanCacheAABBForLOS && !m_bNeedToRecalculateAABBForLOS)
	{
		// The LOS AABB doesn't move so we can just return the cached copy.
		xAABB = *m_pxCachedLOSAABB;
		return;
	}

	m_bNeedToRecalculateAABBForLOS = false;

	bool bSetAABB = false;
	if (m_pxInstance && m_bEnableLOSTest)
	{
		bSetAABB = m_pxInstance->GetLineOfSightAABB(xAABB);
	}
	
	if (!bSetAABB)
	{
		// Not interested in doing a LOS test. Remove from the LOS list.
		xAABB.SetToPoint(GetCurrentPosition());
		RemoveFromLOSList();
	}

	// Save a copy of the AABB.
	if (m_pxCachedLOSAABB)
	{
		*m_pxCachedLOSAABB = xAABB;
	}
}

/////////////
// LOSTest //
/////////////
bool Asura_ClientEntity_Container::LOSTest(const Asura_LOS_InputData* pxInput, Asura_LOS_OutputData* pxOutput)
{
	if ( IsInvisible() )
	{
		return false;
	}

	if (pxInput->m_uEntityToIgnore == GetGuid())
	{
		return false;
	}

	if (!m_bEnableLOSTest)
	{
		// None of the containers provide a LOS test.
		return false;
	}

	if (!pxInput || !pxOutput)
	{
		Asura_Assert(0,"Null pointer");
		return false;
	}

	if (m_pxInstance)
	{
		if (m_pxInstance->LOSTest(pxInput, pxOutput))
		{
			// A closer hit. Set the guid.
			pxOutput->m_uHitEntity = GetGuid();
			return true;
		}
	}

	return false;
}

///////////////////
// SetInputState //
///////////////////
// Return true if the state actually changed.
bool Asura_ClientEntity_Container::SetInputState(const Asura_Container_InputState& xInputState, float fServerTime)
{
	if (fServerTime < m_fStateTimeStamp)
	{
		return false; // Old message, ignore.
	}

	m_fStateTimeStamp = fServerTime;

	if (m_xInputState != xInputState)
	{
		// Change the state.
		m_xInputState = xInputState;

		if (IsEntityActive())
		{
			// Create or update the instance.
			CreateInstance();
		}
		else
		{
			// If the entity is inactive then we can ignore the state change for now.
			// We'll implement it when the entity is activated.
			// The instance should already have been destroyed, but let's make sure.
			DestroyInstance();
		}
		return true;
	}

	return false;
}

///////////////
// SetHealth //
///////////////
// Set health from change health message.
void Asura_ClientEntity_Container::SetHealth(u_int uUniqueID, float fHealth, float fServerTime)
{
	// Store this data. It'll be needed if we deactive then active the entity, or after state changes.
	if (!m_xFloatParamStorage.Add(uUniqueID, fHealth, fServerTime))
	{
		return;
	}

	if (!m_pxInstance) return;

	Asura_ClientContainerInstance* pxInstance = m_pxInstance->FindFromUniqueID(uUniqueID);
	if (pxInstance && pxInstance->GetClassification() == AsuraContainerClass_Health)
	{
		Asura_ClientContainerInstance_Health* const pxHealthInstance = static_cast<Asura_ClientContainerInstance_Health*>(pxInstance);
		
		const float fOldHealthPercent = pxHealthInstance->GetHealthPercent();
		pxHealthInstance->SetParam(fHealth);
		const float fNewHealthPercent = pxHealthInstance->GetHealthPercent();

		// Note: CreateInstance is relatively slow (it does quite a lot of work), so we only want to call it if absolutely necessary.
		if (pxHealthInstance->HasDamageOverrideChanged(fOldHealthPercent, fNewHealthPercent))
		{
			Asura_Container_System::SetDoingDamageOverrideChange(true);

			// Refresh any damage overrides that have changed.
			CreateInstance();
			// Restore any pos/ori data for dynamic physics objects.
			ProcessStateChange();

			Asura_Container_System::SetDoingDamageOverrideChange(false);
		}
	}
}

//////////////////////////////
// CopyFloatParamToInstance //
//////////////////////////////
void Asura_ClientEntity_Container::CopyFloatParamToInstance()
{
	m_xFloatParamStorage.CopyToInstance(m_pxInstance);
}

/////////////////////////////////////
// FindUniqueIDFromRigidBodyHandle //
/////////////////////////////////////
// Return the unique ID of the mesh container which has the given rigid body handle
// (or return uASURA_CONTAINER_UNIQUE_ID_INVALID if not found).
u_int Asura_ClientEntity_Container::FindUniqueIDFromRigidBodyHandle(Asura_Handle iHandle) const
{
	if (m_pxInstance)
	{
		Asura_ClientContainerInstance* pxFound = m_pxInstance->FindFromRigidBodyHandle(iHandle);
		if (pxFound)
		{
			return pxFound->GetUniqueID();
		}
	}
	return uASURA_CONTAINER_UNIQUE_ID_INVALID;
}

//////////////////////
// FindFromUniqueID //
//////////////////////
// Find the container corresponding to a unique ID, assuming that it is enabled.
// Return 0, if not found (maybe it doesn't exist, or is disabled, or the entity might be deactivated).
const Asura_ClientContainerInstance* Asura_ClientEntity_Container::FindFromUniqueID(u_int uUniqueID) const
{
	if (m_pxInstance)
	{
		return m_pxInstance->FindFromUniqueID(uUniqueID);
	}
	return 0;
}

///////////////////
// FindBlueprint //
///////////////////
// Find the blueprint in the root collection.
// Set bIncludeDisabled to include both enabled and disabled blueprint containers.
// If uBlueprintTypeHash != ASURA_HASH_ID_UNSET, then only return blueprints of the specified type.
// Returns NULL if no relevant blueprint found.
const Asura_Container_Blueprint* Asura_ClientEntity_Container::FindBlueprint(Asura_Hash_ID uBlueprintTypeHash,
																			 bool bIncludeDisabled) const
{
	if (m_pxInstance && m_pxInstance->GetClassification() == AsuraContainerClass_Collection)
	{
		return static_cast<const Asura_ClientContainerInstance_Collection*>(m_pxInstance)->FindBlueprint(uBlueprintTypeHash, bIncludeDisabled);
	}
	else if (bIncludeDisabled)
	{
		// Instance hasn't been created (the entity might be deactivated). Just look it up directly from the container.
		const Asura_Container* pxContainer = Asura_Container_System::FindContainer(m_uContainerHash);
		if (pxContainer && pxContainer->GetClassification() == AsuraContainerClass_Collection)
		{
			const Asura_Container_Collection* const pxCollection = static_cast<const Asura_Container_Collection*>(pxContainer);
			return pxCollection->FindBlueprintContainer(uBlueprintTypeHash);
		}
	}
	return 0;
}

#endif //#ifdef ASURA_USE_CONTAINER_SYSTEM

