//////////////////////////////////////////////////////////////////////////////
//
// Asura_ServerEntity_DebugWaypointTrigger.cpp
//
// Created by JAT - 02 April 2007
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//		Copied from Asura_ServerEntity_DebugMessageTrigger and extended
//
//////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#if defined ASURA_USE_DEBUG_MESSAGE_TRIGGER

#define COMMAND_DEBUGWAYPOINTTRIGGERENABLE		L"DebugWaypointTriggerEnable"

// This file's header.
#include "Entity/Triggers/Asura_ServerEntity_DebugWaypointTrigger.h"

// Engine independent.
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_LOS.h"
#include "Core/Asura_Timers.h"
#include "Entity/PhysicalObject/Asura_ServerEntity_PhysicalObject.h"

#include "Interface/Asura_CommandConsole.h"
#include "Interface/Asura_CommandConsole_IO.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Font_System.h"

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

DVSVAR bool	g_bRenderDebugWaypointTriggerMessages = true;
DVSVAR bool	g_bRenderDebugWaypointTriggerShapes = true;

static Asura_Hash_ID g_uDefaultShapeID = ASURA_HASH_ID_UNSET;

static const float fASURA_DEBUGWAYPOINTTRIGGER_TRIGGER_DEFAULT_RADIUS = 5.0f;

void Asura_ServerEntity_DebugWaypointTrigger::SetDefaultShapeName( const char* const pszDefaultShapeName )
{
	g_uDefaultShapeID = Asura_GetHashID(pszDefaultShapeName);
}
void Asura_ServerEntity_DebugWaypointTrigger::SetDefaultShapeID( const Asura_Hash_ID uDefaultShapeID )
{
	g_uDefaultShapeID = uDefaultShapeID;
}

Asura_Hash_ID Asura_ServerEntity_DebugWaypointTrigger::GetDefaultShapeID()
{
	return g_uDefaultShapeID;
}

// ASURA_GUID_CLIENT_START is a reasonable default, as many projects at least start with the player actor using this guid.
Asura_Guid Asura_ServerEntity_DebugWaypointTrigger::s_uTriggeringGuid = ASURA_GUID_CLIENT_START;
bool Asura_ServerEntity_DebugWaypointTrigger::s_bEnableMessages = true;
u_int Asura_ServerEntity_DebugWaypointTrigger::s_uStringDisplayCount = 0;

Asura_ServerEntity_DebugWaypointTrigger::Asura_ServerEntity_DebugWaypointTrigger( void )
: Asura_ServerEntity_Trigger()
{
	Classification = AsuraEntityClass_DebugWaypointTrigger;
	SetAlwaysUpdateClient(true);
	InitData();
}

Asura_ServerEntity_DebugWaypointTrigger::Asura_ServerEntity_DebugWaypointTrigger( Asura_Guid uGuid )
: Asura_ServerEntity_Trigger( uGuid )
{
	Classification = AsuraEntityClass_DebugWaypointTrigger;
	SetAlwaysUpdateClient(true);
	InitData();
}

Asura_ServerEntity_DebugWaypointTrigger::~Asura_ServerEntity_DebugWaypointTrigger()
{	
}

void Asura_ServerEntity_DebugWaypointTrigger::InitData(void)
{
	m_xClientData.m_fR = 255.0f;
	m_xClientData.m_fG = 255.0f;
	m_xClientData.m_fB = 255.0f;

	m_xTitleString = "Title";
	m_xStringToDisplay[0] = "Text";
	for(int32 i=1; i<8; i++)
	{
		m_xStringToDisplay[i] = "";
	}

	m_xClientData.m_fTimeToDisplayString = -1.0f;

	// DJG 23/03/2007 - trigger radii default to 5m
	m_xClientData.m_fTriggerRadius = fASURA_DEBUGWAYPOINTTRIGGER_TRIGGER_DEFAULT_RADIUS;

	m_xClientData.m_xPosition.Zero();
	m_xClientData.m_xOrientation.Identity();

	m_xClientData.m_uShapeID = ASURA_HASH_ID_UNSET;
	m_xClientData.m_uAnimID = ASURA_HASH_ID_UNSET;

	m_uChunkedFlags = 0;
	m_uUnchunkedFlags = 0;
	// The radius triggering and use look-at flags used to default to true. Retain that.
	m_bUseRadiusTriggering = true;
	m_bUseLookAtTest = true;

	UpdateClientStrings();
}

bool Asura_ServerEntity_DebugWaypointTrigger::HandleMessage(const Asura_Message* pxMessage)
{
	if( IsDestroyed() ) { return true; } // ignore all further messages?

	if (pxMessage)
	{
		switch (pxMessage->GetMsgID())
		{
			case ASURA_MSG_RESET:
			{
				return true;
			}

			case ASURA_MSG_CLIENT_REQUESTCREATEMSG:
			{
				MakeCreateMessage(pxMessage->GetFromGuid()); 
				return true; 
			}
		}
	}

	return PARENT::HandleMessage(pxMessage);
}

void Asura_ServerEntity_DebugWaypointTrigger::OnDestruction()
{
	PARENT::OnDestruction();
	// NY - 18 June 2008 - triggers don't normally actually destroy on destroy - debug waypoint triggers need to however
	SetThisAndClientsAsDestroyed();
}

#ifdef ASURA_TOOLS
void Asura_ServerEntity_DebugWaypointTrigger::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	PARENT::ApplyChangesForTemplateInstance(pxInstance);

	m_xClientData.m_xPosition = pxInstance->TransformPosition(m_xClientData.m_xPosition);
	m_xClientData.m_xOrientation = pxInstance->TransformMatrix(m_xClientData.m_xOrientation);
}

void Asura_ServerEntity_DebugWaypointTrigger::TraverseUsedShapes(void (*pfnCallback)(Asura_Hash_ID uShapeHash,void* pUserData),void* pUserData) const
{
	//Report which shape is used.
	//we need to return unset if we are using skin/anim
	if(m_bSkin)
	{
		pfnCallback( ASURA_HASH_ID_UNSET, pUserData );
	}
	else
	{
		pfnCallback( GetShapeID(), pUserData );
	}
}

#endif //#ifdef ASURA_TOOLS

bool Asura_ServerEntity_DebugWaypointTrigger::IsTriggered()
{
	return m_bTriggered;
}

void Asura_ServerEntity_DebugWaypointTrigger::OnTrigger()
{
	if(PARENT::IsActive())
	{
		m_bTriggered = true;
		PARENT::OnTrigger();
	}	
}

void Asura_ServerEntity_DebugWaypointTrigger::OnUnTrigger()
{
	if(PARENT::IsActive())
	{
		// Turn off the message
		m_bTriggered = false;
		PARENT::OnUnTrigger();
	}
}

void Asura_ServerEntity_DebugWaypointTrigger::OnActivation()
{
	PARENT::OnActivation();
	
	//forward the activation message to the clients , since the activatable class doesn't do that at the moment.
	//RAR 06/12/2007
#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_ACTIVATE );
	if( pxMessage )
	{
		pxMessage->UseGuaranteedDelivery();
		Send( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
#else //#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	MakeCreateMessage(ASURA_GUID_UNREGISTERED, true);
#endif //#else //#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	SetAlwaysUpdateClient(true);
}

void Asura_ServerEntity_DebugWaypointTrigger::OnDeactivation()
{
	PARENT::OnDeactivation();
	
	//forward the deactivation message to the clients , since the activatable class doesn't do that at the moment.
	//RAR 06/12/2007
#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_DEACTIVATE );
#else //#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	Asura_Message* pxMessage = Asura_Network::CreateMessage( ASURA_MSG_DESTRUCT );
#endif //#else //#ifdef ASURA_ALLOW_INACTIVE_CLIENT_ENTITIES
	if( pxMessage )
	{
		pxMessage->UseGuaranteedDelivery();
		Send( pxMessage, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
	SetNeverUpdateClient();
}


void Asura_ServerEntity_DebugWaypointTrigger::Update()
{
	// note that we don't call the parent update function here as we have many different triggers
	if ( IsDestroyed() || !IsActive() )
	{
		return;
	}
	//______ Check the radius for triggering ____

	if ( m_bUseRadiusTriggering )
	{
		// should we activate?
		Asura_ServerEntity_PhysicalObject* pxPlayer = Asura_ServerEntity_PhysicalObject::FindEntity(s_uTriggeringGuid);
		if ( pxPlayer == NULL )
		{
			return;
		}
		const Asura_Vector_3 xDistance = m_xClientData.m_xPosition - pxPlayer->GetPosition();
		if ( xDistance.MagnitudeSquared() < ( m_xClientData.m_fTriggerRadius * m_xClientData.m_fTriggerRadius ) )
		{
			//Player is in the volume, trigger		
			if(!m_bPlayerInVolume)
			{
				m_bPlayerInVolume = true;
				OnTrigger();				
			}			
		}
		else
		{
			//Player left volume, untrigger
			if(m_bPlayerInVolume)
			{
				m_bPlayerInVolume = false;
				OnUnTrigger();
			}
		}
	}
	PARENT::Update();
}

void Asura_ServerEntity_DebugWaypointTrigger::Create( Asura_Guid uGuid, Asura_Chunk_Stream& xStream )
{
	Asura_ServerEntity_DebugWaypointTrigger* pxTrigger = new Asura_ServerEntity_DebugWaypointTrigger( uGuid );
	if( pxTrigger )
	{
		pxTrigger->ReadFromChunkStream(xStream);
	}
}

void Asura_ServerEntity_DebugWaypointTrigger::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	// Do parent first to match how original (non-streamed) data worked
	PARENT::ReadFromChunkStream( xStream );

	int iVersion;
	int iDummy;
	int iTemp;
	xStream >> iVersion;
	Asura_Assert( iVersion <= CURRENT_VERSION, "Entity version mismatch" );

#ifdef ASURA_TOOLS
	// Handle loading older format data
	if ( iVersion < 10 )
	{
		if ( iVersion >= 1 )
		{
			xStream >> m_xClientData.m_fTimeToDisplayString;
			xStream >> m_xStringToDisplay[0];
		}
		if ( iVersion >= 2 )
		{
			float fIgnore;
			xStream >> fIgnore;
		}

		if ( iVersion >= 3 )
		{
			xStream >> m_xClientData.m_fR;
			xStream >> m_xClientData.m_fG;
			xStream >> m_xClientData.m_fB;
		}
		if ( iVersion >= 4 )
		{
			xStream >> iDummy;
		}
		if ( iVersion >= 5 )
		{
			xStream >> m_xClientData.m_xPosition;
		}
		if ( iVersion >= 6 )
		{
			xStream >> m_xTitleString;
			xStream >> m_xStringToDisplay[1];
			xStream >> m_xStringToDisplay[2];
			xStream >> m_xStringToDisplay[3];
			xStream >> m_xStringToDisplay[4];
			xStream >> m_xStringToDisplay[5];
			xStream >> m_xStringToDisplay[6];
			xStream >> m_xStringToDisplay[7];
			xStream >> iTemp;
			m_bAlwaysShowTitle = (iTemp != 0) ? true : false;
			xStream >> m_xClientData.m_xOrientation;
		}
		if ( iVersion >= 7 )
		{
			xStream >> m_xClientData.m_uShapeID;
		}
		if ( iVersion >= 8 )
		{
			xStream >> m_xClientData.m_fTriggerRadius;
		}
		if ( iVersion == 9 )
		{
			xStream >> iTemp;
			m_bUseRadiusTriggering = (iTemp != 0) ? true : false;
		}
	}
#endif //#ifdef ASURA_TOOLS

	// The second chunk data format
	if (iVersion >= 10 && iVersion <= 13)
	{
		xStream >> m_xClientData.m_fTimeToDisplayString;

		xStream >> m_xClientData.m_fR;
		xStream >> m_xClientData.m_fG;
		xStream >> m_xClientData.m_fB;

		xStream >> iTemp;
		m_bAlwaysShowTitle = (iTemp != 0) ? true : false;
		xStream >> iTemp;
		m_bUseRadiusTriggering = (iTemp != 0) ? true : false;

		xStream >> m_xClientData.m_xPosition;
		xStream >> m_xClientData.m_xOrientation;
	
		xStream >> m_xTitleString;
		xStream >> m_xStringToDisplay[0];
		xStream >> m_xStringToDisplay[1];
		xStream >> m_xStringToDisplay[2];
		xStream >> m_xStringToDisplay[3];
		xStream >> m_xStringToDisplay[4];
		xStream >> m_xStringToDisplay[5];
		xStream >> m_xStringToDisplay[6];
		xStream >> m_xStringToDisplay[7];
		
		xStream >> m_xClientData.m_uShapeID;
		xStream >> m_xClientData.m_fTriggerRadius;
		if (iVersion >= 11 )
		{
			xStream >> iTemp;
			m_bUseLookAtTest = (iTemp != 0) ? true : false;
		}
		if (iVersion == 12)
		{
			xStream >> iDummy; //was TitleLoS
		}
		if (iVersion >= 13)
		{
			xStream >> m_xClientData.m_uAnimID;
		}
	}

	// The third data format, using a bitfield for the saved flags
	if (iVersion >= 14)
	{
		xStream >> m_xClientData.m_fTimeToDisplayString;

		xStream >> m_xClientData.m_fR;
		xStream >> m_xClientData.m_fG;
		xStream >> m_xClientData.m_fB;

		xStream >> m_xClientData.m_xPosition;
		xStream >> m_xClientData.m_xOrientation;
	
		xStream >> m_xTitleString;
		xStream >> m_xStringToDisplay[0];
		xStream >> m_xStringToDisplay[1];
		xStream >> m_xStringToDisplay[2];
		xStream >> m_xStringToDisplay[3];
		xStream >> m_xStringToDisplay[4];
		xStream >> m_xStringToDisplay[5];
		xStream >> m_xStringToDisplay[6];
		xStream >> m_xStringToDisplay[7];
		
		xStream >> m_xClientData.m_uShapeID;
		xStream >> m_xClientData.m_uAnimID;
		xStream >> m_xClientData.m_fTriggerRadius;

		xStream >> m_uChunkedFlags;
	}

	if( iVersion < 15)
	{
		//deduce the skin flag based on whether an anim has been set. RAR 19/06/2008
		m_bSkin = m_xClientData.m_uAnimID ? true : false;
	}

	UpdateClientFlags();
	UpdateClientStrings();

	if (IsActive())
	{
		SetAlwaysUpdateClient(true);
	}
	else
	{
		SetNeverUpdateClient();
	}
}

void Asura_ServerEntity_DebugWaypointTrigger::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	// Do parent first to match how original (non-streamed) data worked
	PARENT::WriteToChunkStream( xStream );

	const int iVersion = CURRENT_VERSION;

	xStream << iVersion;

	xStream << m_xClientData.m_fTimeToDisplayString;

	xStream << m_xClientData.m_fR;
	xStream << m_xClientData.m_fG;
	xStream << m_xClientData.m_fB;

	xStream << m_xClientData.m_xPosition;
	xStream << m_xClientData.m_xOrientation;
	
	xStream << m_xTitleString;
	xStream << m_xStringToDisplay[0];
	xStream << m_xStringToDisplay[1];
	xStream << m_xStringToDisplay[2];
	xStream << m_xStringToDisplay[3];
	xStream << m_xStringToDisplay[4];
	xStream << m_xStringToDisplay[5];
	xStream << m_xStringToDisplay[6];
	xStream << m_xStringToDisplay[7];
	
	xStream << m_xClientData.m_uShapeID;
	xStream << m_xClientData.m_uAnimID;
	xStream << m_xClientData.m_fTriggerRadius;
	
	Asura_WriteToStream_Bitfield_Wrapper<u_int> xChunkedFlags(m_uChunkedFlags);
	xChunkedFlags.Add(m_bUseRadiusTriggering, 1);
	xChunkedFlags.Add(m_bAlwaysShowTitle, 1);
	xChunkedFlags.Add(m_bUseLookAtTest, 1);
	xChunkedFlags.Add(m_bUseLOSOcclusion, 1);
	xChunkedFlags.Add(m_bSkin, 1);
	xStream << xChunkedFlags;
}

#if defined ASURA_DEBUG_VARIABLE_SYSTEM

void Asura_ServerEntity_DebugWaypointTrigger::AddConsoleCommands( void )
{
	Asura_CommandConsole::AddCmd( COMMAND_DEBUGWAYPOINTTRIGGERENABLE, ConsoleCmd_DebugWaypointTriggerEnable, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddVar(L"Asura.DebugWaypointTriggers.RenderMessages", &g_bRenderDebugWaypointTriggerMessages, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddVar(L"Asura.DebugWaypointTriggers.RenderShapes", &g_bRenderDebugWaypointTriggerShapes, Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
}

bool Asura_ServerEntity_DebugWaypointTrigger::ConsoleCmd_DebugWaypointTriggerEnable( bool bEnable, bool bInterrogate, bool bHelp )
{
	//Enable/Disable displaying of the text
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( L"Sets whether debug waypoint trigger text should be displayed" );
		Asura_CommandConsole::Printf( L"Syntax: " COMMAND_DEBUGWAYPOINTTRIGGERENABLE L" <true | false>" );
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bEnableMessages = bEnable;
		}
		Asura_CommandConsole::Printf( L"Debug Waypoint Trigger: %ls.", s_bEnableMessages ? L"enabled" : L"disabled" );
	}
	return true;
}

#endif // ASURA_DEBUG_VARIABLE_SYSTEM

bool Asura_ServerEntity_DebugWaypointTrigger::GetRenderTriggerShapes( void )
{
	return g_bRenderDebugWaypointTriggerShapes;
}

void Asura_ServerEntity_DebugWaypointTrigger::SetAnim( Asura_Hash_ID uAnim )
{
	m_xClientData.m_uAnimID = uAnim;
}
void Asura_ServerEntity_DebugWaypointTrigger::SetSkin( Asura_Hash_ID uSkin )
{
	m_xClientData.m_uShapeID = uSkin;
	m_xClientData.m_bSkin = true;
	m_bSkin = true;
}

void Asura_ServerEntity_DebugWaypointTrigger::SetShape( Asura_Hash_ID uShape )
{
	m_xClientData.m_uShapeID = uShape;
	m_xClientData.m_uAnimID = ASURA_HASH_ID_UNSET;
	m_xClientData.m_bSkin = false;
	m_bSkin = false;
}

void Asura_ServerEntity_DebugWaypointTrigger::MakeCreateMessage(Asura_Guid uRequestingClient, bool bSendGuaranteed /*=false*/)
{
	UpdateClientFlags();

	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTCREATE, sizeof(Asura_Message_DebugWaypoint_Create), GetClassification(), &m_xClientData);
	if( pxMsg )
	{
		if (bSendGuaranteed)
		{
			pxMsg->UseGuaranteedDelivery();
		}

		Send( pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
}

void Asura_ServerEntity_DebugWaypointTrigger::MakeClientUpdateMessage()
{
	UpdateClientFlags();

	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE, sizeof(Asura_Message_DebugWaypoint_ClientUpdate), GetClassification(), &(m_xClientData.m_uFlags) );
	if( pxMsg )
	{
		Send( pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
}

void Asura_ServerEntity_DebugWaypointTrigger::UpdateClientFlags()
{
	m_xClientData.m_uFlags = 0;
	m_xClientData.m_bUseRadiusTriggering	= m_bUseRadiusTriggering;
	m_xClientData.m_bHidden					= m_bHidden;
	m_xClientData.m_bAlwaysShowTitle		= m_bAlwaysShowTitle;
	m_xClientData.m_bUseLookAtTest			= m_bUseLookAtTest;
	m_xClientData.m_bUseLOSOcclusion		= m_bUseLOSOcclusion;
	m_xClientData.m_bSkin					= m_bSkin;
	m_xClientData.m_bActive					= IsActive();
}

void Asura_ServerEntity_DebugWaypointTrigger::UpdateClientStrings()
{
	//Fill in text fields
	Asura_PrintfToBfr(m_xClientData.m_szTitle, "%s", eASURA_DEBUGWAYPOINTTRIGGER_TEXTLEN, true, m_xTitleString.GetString());
	for(int i=0;i<8;++i)
	{
		Asura_PrintfToBfr(m_xClientData.m_szText[i], "%s", eASURA_DEBUGWAYPOINTTRIGGER_TEXTLEN, true, m_xStringToDisplay[i].GetString());
	}			
};

//////////////////////////////////////
// Asura_DebugWaypointTrigger_Messages //
//////////////////////////////////////

// Debug messages now buffered, and rendered later
int Asura_DebugWaypointTrigger_Messages::s_iNumDebugMessages = 0;
Asura_DebugWaypointTrigger_Message	Asura_DebugWaypointTrigger_Messages::s_axDebugMessages[ASURA_DEBUGWAYPOINTTRIGGER_MAXMESSAGES];
int Asura_DebugWaypointTrigger_Messages::s_iFontHandle = 0;


void Asura_DebugWaypointTrigger_Messages::Reset()
{
	s_iNumDebugMessages = 0;
}

void Asura_DebugWaypointTrigger_Messages::Render( int iFontHandle )
{
	SetFont(iFontHandle);
	Render();
}

void Asura_DebugWaypointTrigger_Messages::Render()
{
	if (s_iNumDebugMessages>0)
	{	
		Asura_Font_System::SetFont( s_iFontHandle );

		const float fFontHeight	= Asura_Font_System::GetFontHeight();

		for (int i=0;i<s_iNumDebugMessages;i++)
		{
			Asura_DebugWaypointTrigger_Message *pxMsg = &s_axDebugMessages[i];
			Asura_Font_System::Print( pxMsg->xScreenPos[0], pxMsg->xScreenPos[1] + (pxMsg->fYOffset * fFontHeight), 1.0f, pxMsg->uColor, 0, pxMsg->szString);
		}

		Asura_Render::FlushDisplay();
		s_iNumDebugMessages = 0;
	}
}

void Asura_DebugWaypointTrigger_Messages::BufferDebugMessage(const Asura_Vector_3& xScreenPos, const float fYOffset, uint32 uColor, char *szString)
{
	if ( s_iNumDebugMessages < ASURA_DEBUGWAYPOINTTRIGGER_MAXMESSAGES )
	{
		Asura_DebugWaypointTrigger_Message *pxMsg = &s_axDebugMessages[s_iNumDebugMessages];
		pxMsg->xScreenPos[0] = xScreenPos[0];
		pxMsg->xScreenPos[1] = xScreenPos[1];
		pxMsg->fYOffset = fYOffset;
		pxMsg->uColor = uColor;
		pxMsg->szString = szString;
		s_iNumDebugMessages++;
	}	
}


#endif // defined ASURA_USE_DEBUG_MESSAGE_TRIGGER
