/**
*
* \file Asura_ServerEntity_AdvancedVolumeTrigger.cpp
*
* Created by Andrew Weinkove - 29/01/2003
*
* Copyright (C) 2003 Rebellion, All Rights Reserved.
*
* A volume trigger with trigger and untrigger functionality that
* will check entities contained in project-specific lists.
* AdvancedVolumeTrigger is a slightly grand title, the old volume triggers
* aren't actually any real use for anything other than checking the camera
* position in single-player single-machine games. This class attempts to 
* remove these limitations without breaking any old code, and to eventually
* make the old volume triggers redundant.
*
**/

#include "Core/Asura.h"

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

// Engine independent.
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Debugging/Asura_Assert.h"
#include "Entity/Asura_Entity_Classification.h"
#ifdef ASURA_SUPPORT_ENTITYWATCH
#include "Entity/Asura_EntityWatch_System.h"
#endif

#ifdef ASURA_USE_STREAMING
#include "Streaming/Asura_Streaming_System.h"
#endif

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

const int Asura_ServerEntity_AdvancedVolumeTrigger::s_iMaxNumberOfLists = 32;

Asura_ServerEntity_AdvancedVolumeTrigger::Asura_ServerEntity_AdvancedVolumeTrigger( const Asura_Bounding_Box& xBox, bool bInitiallyActive )
: Asura_ServerEntity_Trigger(bInitiallyActive)
{
	Classification         = AsuraEntityClass_AdvancedVolumeTrigger;
	m_uFlags 			   = 0;
	m_iListFlags           = 0;
    m_uSpecificGuid        = ASURA_GUID_UNREGISTERED;

	m_iNumberOfVolumes = 1;
	m_pxVolumes = new Asura_OrientedVolume[1];
	m_pxVolumes[0].m_xBox = xBox;
	m_pxVolumes[0].m_xOrient = Asura_Maths::IdentityQuat;
}



Asura_ServerEntity_AdvancedVolumeTrigger::Asura_ServerEntity_AdvancedVolumeTrigger(Asura_Guid uGuid)
:Asura_ServerEntity_Trigger(uGuid)
{
	Classification			= AsuraEntityClass_AdvancedVolumeTrigger;
	m_uFlags 				= 0;
	m_iListFlags			= 0;
	m_uSpecificGuid			= ASURA_GUID_UNREGISTERED;

	m_iNumberOfVolumes		= 1;
	m_pxVolumes = new Asura_OrientedVolume[1];
	m_pxVolumes[0].m_xBox.Zero();
	m_pxVolumes[0].m_xOrient = Asura_Maths::IdentityQuat;
}

Asura_ServerEntity_AdvancedVolumeTrigger::Asura_ServerEntity_AdvancedVolumeTrigger(Asura_Guid uGuid,const char*& chunkBuffer)
:Asura_ServerEntity_Trigger(uGuid,chunkBuffer)
{
	Classification = AsuraEntityClass_AdvancedVolumeTrigger;
	m_iNumberOfVolumes = 0;
	m_pxVolumes = 0;

	//check the version
	int iVersion;
	ASURA_CHUNK_EXTRACT( chunkBuffer, iVersion, int );
    Asura_Assert( iVersion <= ChunkData::CURRENT_VERSION,
        "Asura_ServerEntity_AdvancedVolumeTrigger - incompatible chunk version number" );

	
	//read in the rest
    if ( iVersion == ChunkData::CURRENT_VERSION || iVersion==2 || iVersion==3)
    {
	    ChunkData xChunkData;
	    ASURA_CHUNK_EXTRACT( chunkBuffer, xChunkData, ChunkData );

	    // Copy the data into this instance.
	    m_iListFlags    = xChunkData.m_iListFlags;
	    m_uFlags        = xChunkData.m_uFlags;
        m_uSpecificGuid = xChunkData.m_uSpecificGuid;
#if defined(ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)
		// ensure guid is transformed for streaming
		m_uSpecificGuid = Asura_Streaming_System::MakeCurrentMasterZoneGuid (m_uSpecificGuid);
#endif
		m_iNumberOfVolumes = xChunkData.m_iNumberOfVolumes;

		if(iVersion == 2)
		{
			m_bTriggerOnce = false;
		}

		//allocate volume array , and extract those
		m_pxVolumes = new Asura_OrientedVolume[m_iNumberOfVolumes];
		for(int i=0;i<m_iNumberOfVolumes;i++)
		{
			ASURA_CHUNK_EXTRACT( chunkBuffer, m_pxVolumes[i].m_xBox, Asura_Bounding_Box );
			ASURA_CHUNK_EXTRACT( chunkBuffer, m_pxVolumes[i].m_xOrient, Asura_Quat );
		}
    }
    else 
    {
		Asura_Bounding_Box xOldVolume;
    	if ( iVersion == 1 )
    	{
		    ChunkData_V1 xChunkData;
		    ASURA_CHUNK_EXTRACT( chunkBuffer, xChunkData, ChunkData_V1 );

		    // Copy the data into this instance.
		    xOldVolume      = xChunkData.m_xVolume;
		    m_iListFlags    = xChunkData.m_iListFlags;
		    m_uFlags        = xChunkData.m_uFlags;
    		m_uSpecificGuid = xChunkData.m_uSpecificGuid;
#if defined(ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)
			// ensure guid is transformed for streaming
			m_uSpecificGuid = Asura_Streaming_System::MakeCurrentMasterZoneGuid (m_uSpecificGuid);
#endif
    	}
    	else
    	{
		    ChunkData_V0 xChunkData;
		    ASURA_CHUNK_EXTRACT( chunkBuffer, xChunkData, ChunkData_V0 );

		    // Copy the data into this instance.
		    xOldVolume      = xChunkData.m_xVolume;
		    m_iListFlags    = xChunkData.m_iListFlags;
		    m_uFlags        = xChunkData.m_uFlags;
    	    m_uSpecificGuid = ASURA_GUID_UNREGISTERED;
#if defined(ASURA_USE_STREAMING) && !defined(ASURA_TOOLS)
			// ensure guid is transformed for streaming
			m_uSpecificGuid = Asura_Streaming_System::MakeCurrentMasterZoneGuid (m_uSpecificGuid);
#endif
    	}
		
		m_iNumberOfVolumes = 1;
		m_pxVolumes = new Asura_OrientedVolume[m_iNumberOfVolumes];
		m_pxVolumes[0].m_xBox = xOldVolume;
		m_pxVolumes[0].m_xOrient = Asura_Maths::IdentityQuat;
	}

	if(iVersion <= 3)
	{
		//unused flags will be junk in previous versions
		#ifdef ASURA_TOOLS
		//for tools try to clear all the affected flags
		m_uFlags &= 0x00000003;
		#else
		//outside of tools , just reset the new flag
		//(saves worrying about the endianess of the platform)
		m_bTriggerOnNotOccupied = false;
		#endif

	}
}

Asura_ServerEntity_AdvancedVolumeTrigger::~Asura_ServerEntity_AdvancedVolumeTrigger()
{
	delete [] m_pxVolumes;
}

bool Asura_ServerEntity_AdvancedVolumeTrigger::IsTriggered(void)
{
	if (m_bHasAlreadyTriggered) 
	{
		return false;
	}
	if (IsVolumeOccupied() != (m_bTriggerOnNotOccupied ? true : false))
	{
		m_bHasAlreadyTriggered = true;
		return true;
	}
	return false;
}

bool Asura_ServerEntity_AdvancedVolumeTrigger::IsUnTriggered(void)
{
	if (!m_bHasAlreadyTriggered)
	{
		return false;
	}
	if (IsVolumeOccupied() == (m_bTriggerOnNotOccupied ? true : false))
	{
		m_bHasAlreadyTriggered = false;
		return true;
	}
	return false;
}

bool Asura_ServerEntity_AdvancedVolumeTrigger::IsVolumeOccupied()
{
	// This function needs over-riding by a project-class for any functionality.
	return false;
}

bool Asura_ServerEntity_AdvancedVolumeTrigger::Contains(const Asura_Vector_3& xPosition) const
{
	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		if(m_pxVolumes[i].Contains(xPosition))
		{
			return true;
		}
	}
	return false;
}

void Asura_ServerEntity_AdvancedVolumeTrigger::OnUnTrigger()
{
	Asura_ServerEntity_Trigger::OnUnTrigger();
	if(m_bTriggerOnce)
	{
		//deactivate this trigger so it doesn't get triggered again
		OnDeactivation();
	}
}

int Asura_ServerEntity_AdvancedVolumeTrigger::GetChunkBufferSize()
{
	return Asura_ServerEntity_Trigger::GetChunkBufferSize() +
			sizeof(ChunkData) +
			sizeof(int) +
			sizeof(Asura_Bounding_Box)*m_iNumberOfVolumes+
			sizeof(Asura_Quat)*m_iNumberOfVolumes;
}

void Asura_ServerEntity_AdvancedVolumeTrigger::FillInChunkBuffer(char*& chunkBuffer)
{
	/*
	This entity type can be used in template files , so any changes to FillInChunkBuffer 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
	*/
	
	//fill in base class
	Asura_ServerEntity_Trigger::FillInChunkBuffer( chunkBuffer );

	int iVersion = ChunkData::CURRENT_VERSION;
	ASURA_CHUNK_FILLANDVERIFY( chunkBuffer, iVersion, int );

	ChunkData xChunkData;
	xChunkData.m_iListFlags    = m_iListFlags;
	// Bitmask, not bitfield
	ASURA_CHUNK_VERIFY(xChunkData.m_iListFlags, int);
	xChunkData.m_uFlags        = m_uFlags;
	ASURA_CHUNK_BITFIELD_VERIFY(xChunkData.m_uFlags, unsigned int);
    xChunkData.m_uSpecificGuid = m_uSpecificGuid;
	ASURA_CHUNK_VERIFY(xChunkData.m_uSpecificGuid, Asura_Guid);
	xChunkData.m_iNumberOfVolumes = m_iNumberOfVolumes;
	ASURA_CHUNK_VERIFY(xChunkData.m_iNumberOfVolumes, int);

	ASURA_CHUNK_FILL( chunkBuffer, xChunkData, ChunkData );

	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		ASURA_CHUNK_ARRAY_FILLANDVERIFY(chunkBuffer,m_pxVolumes[i].m_xBox, Asura_Bounding_Box,float, 6);
		ASURA_CHUNK_ARRAY_FILLANDVERIFY(chunkBuffer,m_pxVolumes[i].m_xOrient, Asura_Quat,float, 4);
	}
}

void Asura_ServerEntity_AdvancedVolumeTrigger::WriteToChunkStream (Asura_Chunk_Stream& xStream) const
{
	const int									iVersion = ChunkData::CURRENT_VERSION;

	Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (m_uFlags);

	/*
	This entity type can be used in template files , so any changes to FillInChunkBuffer 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
	*/

	// Create the bitfield...

	xFlagsBitfield.Add	(m_bHasAlreadyTriggered,	1);
	xFlagsBitfield.Add	(m_bTriggerOnce,			1);
	xFlagsBitfield.Add	(m_bTriggerOnNotOccupied,	1);

	
	// fill in base class
	Asura_ServerEntity_Trigger::WriteToChunkStream	(xStream);

	xStream << iVersion;

	xStream << m_iListFlags;
	xStream << xFlagsBitfield;
	xStream << m_uSpecificGuid;
	xStream << m_iNumberOfVolumes;

	for (int i = 0; i < m_iNumberOfVolumes; i++)
	{
		xStream << m_pxVolumes [i].m_xBox;
		xStream << m_pxVolumes [i].m_xOrient;
	}
}

#ifdef ASURA_TOOLS
void Asura_ServerEntity_AdvancedVolumeTrigger::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	Asura_ServerEntity_Trigger::ApplyChangesForTemplateInstance(pxInstance);

	m_uSpecificGuid = pxInstance->ConvertGuid(m_uSpecificGuid);

	for(int i=0;i<m_iNumberOfVolumes;i++)
	{
		Asura_Vector_3 xOldCentre = m_pxVolumes[i].m_xBox.CalculateCentre();
		Asura_Vector_3 xNewCentre = pxInstance->TransformPosition(xOldCentre);
		m_pxVolumes[i].m_xBox.Displace(xNewCentre-xOldCentre);

		m_pxVolumes[i].m_xOrient = pxInstance->TransformQuat(m_pxVolumes[i].m_xOrient);
	}

}
#endif // ASURA_TOOLS


#ifdef ASURA_SUPPORT_ENTITYWATCH
void Asura_ServerEntity_AdvancedVolumeTrigger::DisplayEntityWatchText()
{
	Asura_ServerEntity_Trigger::DisplayEntityWatchText();
	Asura_EntityWatch_System::PrintText(18,"Triggered : %s",m_bHasAlreadyTriggered ? "Yes" : "No");
}
#endif


void Asura_ServerEntity_AdvancedVolumeTrigger::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	int iVersion;
	Asura_ReadFromStream_Bitfield_Wrapper<u_int>	xBitfield(m_uFlags);

	PARENT::ReadFromChunkStream(xStream);

	xStream >> iVersion;
	xStream >> m_iListFlags;
	xStream >> xBitfield;
	xStream >> m_uSpecificGuid;
	xStream >> m_iNumberOfVolumes;

	delete[] m_pxVolumes;
	m_pxVolumes = new Asura_OrientedVolume[m_iNumberOfVolumes];

	for (int i = 0; i < m_iNumberOfVolumes; i++)
	{
		xStream >> m_pxVolumes [i].m_xBox;
		xStream >> m_pxVolumes [i].m_xOrient;
	}
}

