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

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

#include "Core/Asura.h"

#ifndef ASURA_TARGET_SM3

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

// Engine independent.
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#ifdef ASURA_TOOLS
#include "Core/Asura_TextureManagement.h"
#endif
#include "Entity/Asura_Entity_Classification.h"
#include "Debugging/Asura_Assert.h"
#include "Light/Asura_Photon.h"
#include "Network/Asura_Message.h"
#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
#include "Network/Asura_NetMsg.h"
#endif
#include "Network/Asura_Network.h"

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

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

//
// Asura_ServerEntity_DestructibleLight
//

Asura_Hashed_List<Asura_ServerEntity_DestructibleLight> Asura_ServerEntity_DestructibleLight::s_xList;


Asura_ServerEntity_DestructibleLight::Asura_ServerEntity_DestructibleLight()
: Asura_Entity_Server(ASURA_GUID_UNREGISTERED) // ignored
, m_fRange(8.0f)
, m_fR(128.0f)
, m_fG(128.0f)
, m_fB(128.0f)
, m_fBrightness(1.0f)
{
	Classification = AsuraEntityClass_DestructibleLight;
	SetDestructible( true ); // Make destructible by default!
	m_uDestructibleLightFlags = 0;

	// (AAM - 29.03.2007) Give some health by default too, otherwise we die immediately!
	SetHealth( 1.0f );

	m_fCoronaRadius = 0.5f;
	m_uCoronaTexture = ASURA_HASH_ID_UNSET;

	m_bUseBoundingBox = false;
	m_xBoundingBox.MinX = 0.0f;
	m_xBoundingBox.MaxX = 0.0f;
	m_xBoundingBox.MinY = 0.0f;
	m_xBoundingBox.MaxY = 0.0f;
	m_xBoundingBox.MinZ = 0.0f;
	m_xBoundingBox.MaxZ = 0.0f;

	s_xList.AddToList(this);

    // (MH - 08.09.2006) to maintain old behaviour (the class used to derive from
	// StaticObject, which is now PhysicsObject) force all destructible lights to be static.
	SetupPhysicsObject( ASURA_PHYSICS_OBJECT_STATIC );

	if(m_pxDynamicObject)
	{
		//default to no collisions , until a shape is chosen. RAR 11/07/03
		m_pxDynamicObject->SetIgnore( true );
	}
}


Asura_ServerEntity_DestructibleLight::~Asura_ServerEntity_DestructibleLight()
{
	s_xList.RemoveFromList(this);
}


///////////////////////
// Chunk buffer code //
///////////////////////
void Asura_ServerEntity_DestructibleLight::WriteToChunkStream (Asura_Chunk_Stream& xStream) const
{
	Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_uDestructibleLightFlags);

	/*
	This entity type can be used in template files , so any changes to WriteToChunkStream may
	require ApplyChangesForTemplateInstance() to be updated as well.
	Please see the comment at the top of Tools/EntityTemplate/Tools_EntityTemplate_System.h before 
	making any changes.
	RAR 29/04/2004
	*/
	const int	iVersion	= ChunkData::CURRENT_VERSION;


	// Create the bitfield...

	xFlagsBitfield.Add	(m_bHasCorona,						1);
	xFlagsBitfield.Add	(m_bUseBoundingBox,					1);
	xFlagsBitfield.Add	(m_bCorona_DontFadeWithDistance,	1);
	xFlagsBitfield.Add	(m_bDontLightEnv,					1);


	// Write the data...

	xStream << iVersion;

	xStream << m_fR;
	xStream << m_fG;
	xStream << m_fB;
	xStream << m_fRange;
	xStream << m_fBrightness;
	xStream << m_xBoundingBox;
	xStream << m_fCoronaRadius;
	xStream << m_uCoronaTexture;
	xStream << xFlagsBitfield;
}


void Asura_ServerEntity_DestructibleLight::FillInChunkBuffer(char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream	xStream(pcChunkBuffer);

	Asura_ServerEntity_DestructibleLight::WriteToChunkStream(xStream);
}

void Asura_ServerEntity_DestructibleLight::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	int iVersion;
	xStream >> iVersion;

	if (iVersion == ChunkData::CURRENT_VERSION || iVersion == 4)
	{
		ChunkData										xData;
		Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(xData.m_uFlags);

		xStream >> xData.m_fR;
		xStream >> xData.m_fG;
		xStream >> xData.m_fB;
		xStream >> xData.m_fRange;
		xStream >> xData.m_fBrightness;
		xStream >> xData.m_xBoundingBox;
		xStream >> xData.m_fCoronaRadius;
		xStream >> xData.m_uCoronaTexture;
		xStream >> xBitfield;
		
		m_fR = xData.m_fR;
		m_fG = xData.m_fG;
		m_fB = xData.m_fB;


		m_fBrightness = xData.m_fBrightness;
		m_fRange = xData.m_fRange;
		m_xBoundingBox = xData.m_xBoundingBox;

		m_bHasCorona = xData.m_bHasCorona;
		m_bUseBoundingBox = xData.m_bUseBoundingBox;
		m_bCorona_DontFadeWithDistance = xData.m_bCorona_DontFadeWithDistance;
		//Don't light env flag added with version 5
		if(iVersion>=5) m_bDontLightEnv = xData.m_bDontLightEnv;


		m_fCoronaRadius = xData.m_fCoronaRadius;
		m_uCoronaTexture = xData.m_uCoronaTexture;
	}
#if defined(ASURA_TOOLS)
	else if (iVersion == 3)
	{
		ChunkData_v3									xData;
		Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(xData.m_uFlags);

		xStream >> xData.m_fR;
		xStream >> xData.m_fG;
		xStream >> xData.m_fB;
		xStream >> xData.m_fRange;
		xStream >> xData.m_fBrightness;
		xStream >> xData.m_xBoundingBox;
		xStream >> xBitfield;
		
		m_fR = xData.m_fR;
		m_fG = xData.m_fG;
		m_fB = xData.m_fB;


		m_fBrightness = xData.m_fBrightness;
		m_fRange = xData.m_fRange;
		m_xBoundingBox = xData.m_xBoundingBox;

		m_bHasCorona = xData.m_bHasCorona;
		m_bUseBoundingBox = xData.m_bUseBoundingBox;
	}
	else if (iVersion == 2)
	{
		ChunkData_v2									xData;
		Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(xData.m_uFlags);

		xStream >> xData.m_fR;
		xStream >> xData.m_fG;
		xStream >> xData.m_fB;
		xStream >> xData.m_fRange;
		xStream >> xData.m_fBrightness;
		xStream >> xBitfield;

		m_fR = xData.m_fR;
		m_fG = xData.m_fG;
		m_fB = xData.m_fB;

		/// Richard May - 05/03/2003 17:34:51 - these compensations are necessary cos there 
		/// was a mess up with the versioning between 2 & 3
		if( (m_fR <= 1.0f) && (m_fG <= 1.0f) && (m_fB <= 1.0f) )
		{
			m_fR *= 255.0f;
			m_fG *= 255.0f;
			m_fB *= 255.0f;
		}

		m_fBrightness = xData.m_fBrightness;
		m_fRange = xData.m_fRange;
		m_bHasCorona = xData.m_bHasCorona;
	}
	else if (iVersion == 1)
	{
		ChunkData_v1									xData;
		Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(xData.m_uFlags);

		xStream >> xData.m_fR;
		xStream >> xData.m_fG;
		xStream >> xData.m_fB;
		xStream >> xData.m_fRange;
		xStream >> xBitfield;

		m_fR			= xData.m_fR * 255.0f;
		m_fG			= xData.m_fG * 255.0f;
		m_fB			= xData.m_fB * 255.0f;
		m_fRange		= xData.m_fRange;
		m_bHasCorona	= xData.m_bHasCorona;
	}
	else if (iVersion == 0)
	{
		xStream >> m_fR;
		xStream >> m_fG;
		xStream >> m_fB;
		xStream >> m_fRange;
	}
#endif	// defined(ASURA_TOOLS)
	else
	{
		Asura_Assert(0,"Unknown version number");
	}

#ifdef ASURA_TOOLS
	if(iVersion<3 && m_bHasCorona)
	{
		//need to convert the corona settings
		m_fCoronaRadius = m_fRange / 2.5f;
		m_uCoronaTexture	= ASURA_HASH_ID_UNSET;
	}
#endif //ASURA_TOOLS

    SetNoCollisionsFlag( !HasShapeOrSkin() );
}

void Asura_ServerEntity_DestructibleLight::InitialiseFromChunkBuffer(const char*& pcChunkBuffer)
{
	Asura_Chunk_ChunkBuffer_Stream	xStream(pcChunkBuffer);

	Asura_ServerEntity_DestructibleLight::ReadFromChunkStream(xStream);
}


///////////////////////////////////////////
// Send a message to update the clients. //
///////////////////////////////////////////
void Asura_ServerEntity_DestructibleLight::MakeClientUpdateMessage()
{
	// TODO: 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.

	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE,
															 sizeof(Asura_Message_DestructibleLight_ClientUpdate),
															 GetClassification() );
	if (pxMessage)
	{
		Asura_Message_DestructibleLight_ClientUpdate* pxData = 
							static_cast<Asura_Message_DestructibleLight_ClientUpdate*>(pxMessage->GetData());

		pxData->FillDestructibleLight(this);
	
		// (MH - 03.09.2002) if health is 0, guarantee the message to ensure client is destroyed.
		// instead, might want to have guaranteed as a parameter to this function.
		if (pxData->m_fHealth == 0.0f)
		{
			pxMessage->UseGuaranteedDelivery();
		}

        Send(pxMessage,ASURA_GUID_MY_CLIENT_ENTITIES);
	}
}

#ifdef ASURA_TOOLS

void Asura_ServerEntity_DestructibleLight::TraverseUsedTextures(void (*pfnCallback)(int iTextureID,void* pUserData),void* pUserData) const
{
	if(m_bHasCorona)
	{
		int iTextureID = Asura_Texture_Management::GetIndexFromHashID(m_uCoronaTexture);
		if(iTextureID != ASURA_TEXTURE_NONE)
		{
			pfnCallback(iTextureID,pUserData);
		}	
	}
}


void Asura_ServerEntity_DestructibleLight::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	if(m_bUseBoundingBox)
	{
		m_xBoundingBox = pxInstance->TransformBoundingBox(m_xBoundingBox);
	}
}	

#endif // #ifdef ASURA_TOOLS

//
// Asura_Message_DestructibleLight_ClientUpdate
//

void Asura_Message_DestructibleLight_ClientUpdate::FillDestructibleLight( const Asura_ServerEntity_DestructibleLight* pxObject )
{
    FillPhysics( pxObject );

    m_fRange = pxObject->GetRange();
	pxObject->GetRGB(m_fR, m_fG, m_fB);
	m_bHasCorona = pxObject->HasCorona();
	m_fBrightness = pxObject->GetBrightness();
	m_xBoundingBox = pxObject->GetBoundingBox();
	m_bUseBoundingBox = pxObject->GetUseBoundingBox();
	m_fCoronaRadius = pxObject->GetCoronaRadius();
	m_uCoronaTexture = pxObject->GetCoronaTextureHash();
	m_bCorona_DontFadeWithDistance = pxObject->GetCoronaFlag_DontFadeWithDistance();
	m_bDontLightEnv = pxObject->GetDontLightEnvFlag();
}

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM

void Asura_Message_DestructibleLight_ClientUpdate::RegisterFields(u_short usMsgID, u_short usEntityClassification)
{
	Asura_Assert(usMsgID == ASURA_MSG_CLIENTUPDATE, "wrong msg ID");
	Asura_Assert(usEntityClassification == AsuraEntityClass_DestructibleLight, "wrong entity classification");
	Asura_Assert(!Asura_NetMsg::FindNetMsgObject(usMsgID, usEntityClassification), "already exists");
	
	u_char ucNumFields = GetNumFields();

	Asura_NetMsg* pxNetMsg = new Asura_NetMsg(usMsgID, usEntityClassification, ucNumFields, sizeof(Asura_Message_DestructibleLight_ClientUpdate));
	RegisterFields(pxNetMsg);
	pxNetMsg->EndRegistration();
}

void Asura_Message_DestructibleLight_ClientUpdate::RegisterFields(class Asura_NetMsg* pxNetMsg)
{
	const Asura_Message_DestructibleLight_ClientUpdate xDummy;
	PARENT::RegisterFields(pxNetMsg);

	// TODO: optimise these settings. many are probably constant values!
    Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fRange,			1,  Asura_Threshold_AnyChange,	0.0f);
    Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fR,				1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fG,				1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fB,				1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fBrightness,		1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_xBoundingBox,		6,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_float,	m_fCoronaRadius,	1,  Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint,	m_uCoronaTexture,	1,	Asura_Threshold_AnyChange,	0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uchar,	m_ucDestLightFlags,	1,  Asura_Threshold_AnyChange,	0.0f);
}

#endif // #ifdef ASURA_SUPPORT_NETMSG_SYSTEM

#endif // #ifndef ASURA_TARGET_SM3
