//////////////////////////////////////////////////////////////////////////////
//
// Asura_ServerEntity_ForceField_Conveyor.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Entity/Intangible/Asura_ServerEntity_ForceField_Conveyor.h"

#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Physics/ForceFields/Asura_Physics_ForceField_Conveyor.h"

#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#endif


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////

Asura_Hashed_List<Asura_ServerEntity_ForceField_Conveyor>	Asura_ServerEntity_ForceField_Conveyor::s_xList;


////////////////////////////////////////////////////////////////////////////////
//
// Constructors / Destructors
//
////////////////////////////////////////////////////////////////////////////////
Asura_ServerEntity_ForceField_Conveyor::Asura_ServerEntity_ForceField_Conveyor(bool bInitiallyActive)
: PARENT(ASURA_GUID_UNREGISTERED)
{
	m_xForce.Zero();
	m_xForceEuler.Zero();

	Classification = AsuraEntityClass_ForceField_Conveyor;
	
	s_xList.AddToList(this);

	m_uNumVolumes = 0;
	m_pxVolumes = 0;
	m_ppxConveyor = 0;
}

Asura_ServerEntity_ForceField_Conveyor::Asura_ServerEntity_ForceField_Conveyor(const Asura_Guid uGuid)
: PARENT(uGuid)
{	
	m_xForce.Zero();
	m_xForceEuler.Zero();

	Classification = AsuraEntityClass_ForceField_Conveyor;
	s_xList.AddToList(this);

	m_uNumVolumes = 0;
	m_pxVolumes = 0;
	m_ppxConveyor = 0;
}


Asura_ServerEntity_ForceField_Conveyor* Asura_ServerEntity_ForceField_Conveyor::Create(const Asura_Guid uGuid)
{
	return(new Asura_ServerEntity_ForceField_Conveyor(uGuid));
}



Asura_ServerEntity_ForceField_Conveyor* Asura_ServerEntity_ForceField_Conveyor::Create(const Asura_Guid uGuid, Asura_Chunk_Stream& xStream)
{
	Asura_ServerEntity_ForceField_Conveyor* pxForceField = new Asura_ServerEntity_ForceField_Conveyor(uGuid);

	pxForceField->ReadFromChunkStream(xStream);
	pxForceField->CreatePhysicsFields();

	return pxForceField;
}

void Asura_ServerEntity_ForceField_Conveyor::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int uVersion;
	xStream >> uVersion;

	if ( uVersion == CURRENT_CHUNK_VERSION || uVersion == 0)
	{
		PARENT::ReadFromChunkStream(xStream);

		xStream >> m_xForce;
		xStream >> m_xForceEuler;

		if(uVersion == 0)
		{
			// The force is correct, but the euler needs fixing-up to rotate it to the correct orientation.

			Asura_Matrix_3x3 xOri;
			Asura_Maths::OrientationFromDirection(xOri, m_xForce);

			m_xForceEuler.FromMatrix(xOri);
		}

		u_int uOldNumVolumes = m_uNumVolumes;
		xStream >> m_uNumVolumes;
		if (m_uNumVolumes != uOldNumVolumes)
		{
			delete [] m_pxVolumes;
			m_pxVolumes = new Asura_OrientedVolume[m_uNumVolumes];
		}
		for (u_int u = 0; u < m_uNumVolumes; ++u)
		{
			xStream >> m_pxVolumes[u].m_xBox;
			xStream >> m_pxVolumes[u].m_xOrient;
		}

	}
    else
    {
        Asura_Assertf( false, ( "Asura_ServerEntity_ForceField_Conveyor::ReadFromChunkStream - unhandled chunk version (%u)", uVersion ) );
    }
}


void Asura_ServerEntity_ForceField_Conveyor::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << static_cast<u_int>(CURRENT_CHUNK_VERSION);

	PARENT::WriteToChunkStream( xStream );

	xStream << m_xForce;
	xStream << m_xForceEuler;

	xStream << m_uNumVolumes;
	for (u_int u = 0; u < m_uNumVolumes; ++u)
	{
		xStream << m_pxVolumes[u].m_xBox;
		xStream << m_pxVolumes[u].m_xOrient;
	}
}

void Asura_ServerEntity_ForceField_Conveyor::CreatePhysicsFields()
{
	Asura_AVector3 xAPosition(GetPosition());
	Asura_AVector3 xAForce(GetForce());

	m_ppxConveyor = new Asura_Physics_ForceField_Conveyor*[m_uNumVolumes];

	for(u_int uVol=0; uVol < m_uNumVolumes; uVol++)
	{	
		
		Asura_AMatrix3x3 xAOri;
		m_pxVolumes[uVol].m_xOrient.ToMatrix(xAOri);

		//Asura_OBB xOBB(m_pxVolumes[uVol].m_xBox, xAPosition, xAOri);
		Asura_OBB xOBB(m_pxVolumes[uVol].m_xBox, xAPosition);		
		xOBB.SetOrientation(xAOri);
		
		// Kick off the forcefield
		m_ppxConveyor[uVol] = new Asura_Physics_ForceField_Conveyor(xAForce, xOBB);

		m_ppxConveyor[uVol]->SetActive(IsActive());
	}	
}


void Asura_ServerEntity_ForceField_Conveyor::DestroyPhysicsFields()
{
	if(m_ppxConveyor)
	{
		// Remove the stored conveyors
		for(u_int uConveyor=0; uConveyor < m_uNumVolumes; uConveyor++)
		{
			delete m_ppxConveyor[uConveyor];
		}
	
		delete[] m_ppxConveyor;
		m_ppxConveyor = 0;
	}
}


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

	DestroyPhysicsFields();

	if(m_pxVolumes)
	{
		delete[] m_pxVolumes;
	}
}




void Asura_ServerEntity_ForceField_Conveyor::OnActivation()
{
	PARENT::OnActivation();
	SetFieldsActive(true);
}

void Asura_ServerEntity_ForceField_Conveyor::OnDeactivation()
{
	PARENT::OnDeactivation();
	SetFieldsActive(false);
}


void Asura_ServerEntity_ForceField_Conveyor::SetFieldsActive(bool bActive)
{
	for(u_int uVol=0; uVol < m_uNumVolumes; uVol++)
	{	
		m_ppxConveyor[uVol]->SetActive(bActive);
	}	
}

////////////////////////////////////////////////////////////////////////////////
//
// Static methods.
//
////////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_TOOLS
Asura_ServerEntity_ForceField_Conveyor* Asura_ServerEntity_ForceField_Conveyor::CreateFromTools()
{
	return new Asura_ServerEntity_ForceField_Conveyor(true);
}
#endif //ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_ServerEntity_ForceField_Conveyor::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	for(u_int u=0; u<m_uNumVolumes; u++)
	{
		m_pxVolumes[u] = pxInstance->TransformOrientedVolume(m_pxVolumes[u]);
	}

	// ensure that the force fields position and 'force' direction
	// are updated to take into account any template positional changes
	PARENT::ApplyChangesForTemplateInstance(pxInstance);

	m_xForce = m_xForce * pxInstance->GetRelativeMatrix();
	m_xForceEuler = pxInstance->TransformEuler(m_xForceEuler);

}
#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_ServerEntity_ForceField_Conveyor::AddVolume()
{
	DestroyPhysicsFields();

	Asura_OrientedVolume* pxVolumes = new Asura_OrientedVolume[m_uNumVolumes + 1];

	for(u_int u = 0; u < m_uNumVolumes; ++u)
	{
		pxVolumes[u] = m_pxVolumes[u];
	}

	//initialise the new volume
	pxVolumes[m_uNumVolumes].m_xBox.Zero();
	pxVolumes[m_uNumVolumes].m_xBox.Grow(1.0f);
	pxVolumes[m_uNumVolumes].m_xOrient = Asura_Maths::IdentityQuat;

	pxVolumes[m_uNumVolumes].m_xBox.Displace(GetPosition());

	//replace the old array
	delete [] m_pxVolumes;
	m_pxVolumes = pxVolumes;
	++m_uNumVolumes;

	CreatePhysicsFields();
}
#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_ServerEntity_ForceField_Conveyor::RemoveVolume(u_int uIndex)
{
	Asura_Assert(uIndex >= 0 && uIndex < m_uNumVolumes,"");

	DestroyPhysicsFields();

	//shuffle down any volumes after the one being deleted
	for(u_int u = uIndex; u < m_uNumVolumes - 1; ++u)
	{
		m_pxVolumes[u] = m_pxVolumes[u + 1];
	}
	--m_uNumVolumes;

	CreatePhysicsFields();
}
#endif // ASURA_TOOLS

#ifdef ASURA_TOOLS
void Asura_ServerEntity_ForceField_Conveyor::UpdatePhysicsFields()
{
	DestroyPhysicsFields();
	CreatePhysicsFields();
}
#endif
