//////////////////////////////////////////////////////////////////////////////
//
// AVP_ServerEntity_PhysicsObject.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Entity/PhysicalObject/PhysicsObject/AVP_ServerEntity_PhysicsObject.h"

// Engine independent.
#include "Blueprint/Asura_Blueprint.h"
#include "Blueprint/Asura_Blueprint_System.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"
#include "Mesh/AVP_PolygonFlags.h"
#include "Shapes/Asura_Shapes.h"
#include "Shapes/Asura_Shape_System.h"
#include "SpecialFX/Asura_Fragment_System.h"

// AVP headers
#include "Entity/PhysicalObject/PhysicsObject/AVP_PhysicsObject_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ServerEntity_Actor.h"

// G L O B A L S /////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
static bool g_bNewObjectShouldBeStatic;
#endif

static const u_int	g_uCurrentChunkVersion	= 0;


// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////

//
// AVP_ServerEntity_PhysicsObject
//

Asura_Hashed_List<AVP_ServerEntity_PhysicsObject> AVP_ServerEntity_PhysicsObject::s_xList( 5 );


AVP_ServerEntity_PhysicsObject::AVP_ServerEntity_PhysicsObject( Asura_Guid uGUID, bool bAllocateDynamicsBlock ) 
: Asura_Entity_Server( uGUID )
, PARENT_ASURA( bAllocateDynamicsBlock )
, m_uExistenceTiedTo( ASURA_GUID_UNREGISTERED )
{
	if(m_pxDynamicObject)
	{
		m_pxDynamicObject->SetFlagsOfPolysToIgnore(m_pxDynamicObject->GetFlagsOfPolysToIgnore() | AVP_PolygonFlag_IgnoreForPhysicsObjectCollision);
	}

    s_xList.AddToList( this );

//  SetAlwaysUpdateClient( false );
}


AVP_ServerEntity_PhysicsObject::~AVP_ServerEntity_PhysicsObject( void )
{
    s_xList.RemoveFromList( this );
}

bool AVP_ServerEntity_PhysicsObject::HandleMessage( const Asura_Message* pxMsg ) 
{
	switch( pxMsg->GetMsgID() )
	{
		case ASURA_MSG_CLIENT_REQUESTCREATEMSG:
		{
			MakeCreateMessage(pxMsg->GetFromGuid());
			return true;
		}

		default:
		{
			return PARENT_ASURA::HandleMessage( pxMsg ) || PARENT::HandleMessage( pxMsg );
		}
	}
}

void AVP_ServerEntity_PhysicsObject::Update( void )
{
	// CW - Not sure where to put this, placed here for the time being
	// Need to make SetupPhysicsObject virtual?
	if(m_pxDynamicObject)
	{
		m_pxDynamicObject->SetReportEnvironmentCollisionFX(true);
	}

	// This allows convenient cleanup of physics objects after an actor has gone.
	if (m_uExistenceTiedTo != ASURA_GUID_UNREGISTERED)
	{
		if (!AVP_ServerEntity_Actor::FindEntity(m_uExistenceTiedTo)) SetThisAndClientsAsDestroyed();
	}

	PARENT_ASURA::Update(); 
    PARENT::Update();
}

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

	xStream >> uVersion;
	Asura_Assert( uVersion <= g_uCurrentChunkVersion, "Unsupported chunk version" );

	PARENT_ASURA::ReadFromChunkStream(xStream);
	PARENT::ReadFromChunkStream(xStream);

	// Any data specific to this class would go here.
	if(m_pxDynamicObject)
	{
		m_pxDynamicObject->SetFlagsOfPolysToIgnore(m_pxDynamicObject->GetFlagsOfPolysToIgnore() | AVP_PolygonFlag_IgnoreForPhysicsObjectCollision);
	}
}

void AVP_ServerEntity_PhysicsObject::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
    xStream << g_uCurrentChunkVersion;

    PARENT_ASURA::WriteToChunkStream( xStream );
	PARENT::WriteToChunkStream( xStream );

    // Any data specific to this class would go here.
}


void AVP_ServerEntity_PhysicsObject::InitialiseFromChunkBuffer( const char*& pcChunkBfr )
{
	Asura_Assert( false, "Shouldn't be here" );
}

void AVP_ServerEntity_PhysicsObject::FillInChunkBuffer( char*& pcChunkBfr ) 
{
    Asura_Assert( false, "Shouldn't be here" );
}

int AVP_ServerEntity_PhysicsObject::GetChunkBufferSize( void )
{
    Asura_Assert( false, "Shouldn't be here" );
    return 0;
}


void AVP_ServerEntity_PhysicsObject::MakeClientUpdateMessage( void )
{
	// TODO: Shouldn't static objects just be using the 'do not always
	// update my client' and burst update functionality rather than manually
	// suppressing updates and then constantly sending and guaranteeing
	// messages when dead? (See also ForceUpdate() in the header which would
	// also not be necessary when RequestClientUpdate() is available.)
	// (MH - 03.01.2007) - removed the bit that sent updates guaranteed when dead.

	// Most static objects wouldn't change state very often
	// so there isn't any need to send update messages every 0.1s!
	// Should only send messages when a player connects or if the object changes state.
	if( IsStatic() )
	{
		bool bSendUpdateMessage = GetHealth() == 0.0f
			|| Asura_Network::IsExternalNetworkActive()
			|| !GetClientUpdated();

		if( !bSendUpdateMessage ) { return; }
	}

	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE, sizeof(AVP_Message_PhysicsObject_ClientUpdate), GetClassification() );
	if( !pxMsg ) { return; }

	SetClientUpdated(true);

	AVP_Message_PhysicsObject_ClientUpdate* pxMsgData = static_cast<AVP_Message_PhysicsObject_ClientUpdate*>( pxMsg->GetData() );
	pxMsgData->FillPhysics( this );

	Send( pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES );
}

void AVP_ServerEntity_PhysicsObject::MakeCreateMessage( Asura_Guid uRequestingClient, bool bSendGuaranteed /*= false*/ )
{
	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTCREATE, sizeof(AVP_Message_PhysicsObject_ClientCreate), GetClassification() );
	if( !pxMsg ) { return; }

	AVP_Message_PhysicsObject_ClientCreate* pxMsgData = static_cast<AVP_Message_PhysicsObject_ClientCreate*>( pxMsg->GetData() );
	pxMsgData->FillPhysics( this );

	pxMsg->SetToGuid( ASURA_GUID_MY_CLIENT_ENTITIES );
	pxMsg->SetFromGuid( GetGuid() );
	if (bSendGuaranteed)
	{
		pxMsg->UseGuaranteedDelivery();
	}
	Asura_Network::SendToSpecificMachine( pxMsg, uRequestingClient );
}

#ifdef ASURA_TOOLS

void AVP_ServerEntity_PhysicsObject::SetNewObjectShouldBeStatic( bool bStatic )
{
    g_bNewObjectShouldBeStatic = bStatic;
}


void AVP_ServerEntity_PhysicsObject::CreateFromTools( Asura_Guid uGUID )
{ 
    AVP_ServerEntity_PhysicsObject*	pxPhysicsObj	= new AVP_ServerEntity_PhysicsObject( uGUID ); 
	const ASURA_PHYSICS_OBJECT_TYPE	eType			= (g_bNewObjectShouldBeStatic)
													? ASURA_PHYSICS_OBJECT_STATIC
													: ASURA_PHYSICS_OBJECT_DYNAMIC;

	pxPhysicsObj->SetupPhysicsObject(eType);
}

void AVP_ServerEntity_PhysicsObject::ApplyChangesForTemplateInstance( Tools_EntityTemplateInstance* pxInstance )
{
    PARENT_ASURA::ApplyChangesForTemplateInstance( pxInstance );
    PARENT::ApplyChangesForTemplateInstance( pxInstance );
}

bool AVP_ServerEntity_PhysicsObject::CanBeIncludedInTemplate( void ) const
{
    return( GetClassification() == AsuraEntityClass_PhysicsObject );
}

#endif // #ifdef ASURA_TOOLS

void AVP_ServerEntity_PhysicsObject::OnDeath(const AVP_EntityDamage_ApplyToEntity& xDamage)
{
	if( !ChangeToDestroyedShape() )
	{
		SetThisAndClientsAsDestroyed();
	}

	Asura_Shape* pxShape = Asura_Shape_System::GetShape( GetShapeFileID() );
	if( m_pxDynamicObject && pxShape )
	{
		Asura_Fragment_System::GenerateFragments( pxShape->GetFragmentHashID(),
                                                  GetPosition(),
                                                  GetOrientation(),
                                                  &m_pxDynamicObject->GetBoundingBox(),
                                                  &m_pxDynamicObject->GetLinVelocity(),
                                                  0.01f );
	}

	PARENT::OnDeath(xDamage);
}
