///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Streaming_Zone.cpp
// Author : Andy Tate
// Date   : 2-Feb-2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

// RJT NOTE: Required for GCC builds as the file list generation doesn't account for VC project exclusion from build
#ifdef ASURA_USE_STREAMING

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Streaming/Asura_Streaming_Zone.h"

// Asura independent header-files...
#include "ChunkSystem/Asura_Chunk_Entity.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "ChunkSystem/Asura_SaveBlock.h"
#include "Debugging/Asura_Logfile.h"
#include "Entity/Asura_Entity_System.h"
#include "Mesh/Asura_Env_System.h"
#include "Network/Asura_Player.h"
#include "SpecialFX/Asura_SpecialFX.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"
#include "Rendering/Asura_Module.h"
#include "Streaming/Asura_Streaming_Connection.h"
#include "Core/Handlers/Asura_Handler_System.h"
#include "Streaming/Asura_Streaming_System.h"
#include "Streaming/Asura_Streaming_ZoneDataState.h"
#include "Streaming/Asura_Streaming_ZoneEntities.h"

// Asura platform header-files...

#ifdef ASURA_USE_AXON
// Axon independed header-files...
#include "Core/Axon_System.h"
#endif

// Game independent header-files...

// Game platform header-files...

// Tools header-files...
#ifdef ASURA_TOOLS
#include "Streaming/Tools_Streaming_System.h"
#endif

////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

static const char* g_aszDirectoryModifiers[Asura_Streaming_Zone::MAX_DATA_STATES] =
{
	NULL,
#if !defined(ASURA_TOOLS)
	"LODs",
#endif	// !defined(ASURA_TOOLS)
};

static const char* g_aszNameModifiers[Asura_Streaming_Zone::MAX_DATA_STATES] =
{
	NULL,
#if !defined(ASURA_TOOLS)
	"Low_",
#endif	// !defined(ASURA_TOOLS)
};


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

bool	Asura_Streaming_Zone::s_bRenderLODs	= true;

Asura_Streaming_Zone::PreDestroyDataStateFunction* Asura_Streaming_Zone::s_pfnPreDestroyDataStateFunction = 0;


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::FindConnectionIndex
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Checks whether the specified zone is connected to this zone.
//
// Parameters   : Hash ID of the zone to check for
//
// Return value : The index of the connection found, -1 if no connection is found.
////////////////////////////////////////////////////////////////////////////////

int Asura_Streaming_Zone::FindConnectionIndex (Asura_Hash_ID uToZoneHashID) const
{
	// Check each connection in-turn...
	for(u_int u = 0; u < m_uNumConnections; u++)
	{
		if(uToZoneHashID == m_pxConnections[u].GetToZoneHashID())
		{
			return(static_cast<int>(u));
		}
	}

	// No connection found...
	return(-1);
}

/// End of function 'Asura_Streaming_Zone::FindConnectionIndex'. ///



////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneCache::Update
// Author       : Andy Tate
// Date         : 16-Jun-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneCache::Update (const Asura_Vector_3& xPosition)
{
	Asura_Module_Handler*	pxModuleHandler	= Asura_Module_System::GetHandler(m_uZoneHash);

	// Do we have a module-handler to check?
	m_iModuleIndex	= (pxModuleHandler == NULL)
		? -1
		: pxModuleHandler->GetModuleIndexFromPosition(xPosition, m_iModuleIndex);

	// Do we have a module index to use?
	if(m_iModuleIndex < 0)
	{
		int						iNumZones;

		Asura_Streaming_Zone**	ppxZones	= Asura_Streaming_System::MakeArrayOfZones(Asura_Streaming_System::ZONE_DATA, iNumZones);

		// No - time to check the zones...
		m_uZoneHash	= ASURA_HASH_ID_UNSET;

		if(ppxZones != NULL)
		{
			// Check each zone in-turn...
			for(int i = 0; (i < iNumZones) && (m_iModuleIndex < 0); i++)
			{
				const Asura_Hash_ID	uHash	= ppxZones[i]->GetHashID();

				// Don't bother checking our initial zone...
				if(uHash != m_uZoneHash)
				{
					Asura_Module_Handler*	pxModuleHandler	= Asura_Module_System::GetHandler(uHash);

					// Do we have a module-handler to check?
					if(pxModuleHandler != NULL)
					{
						m_iModuleIndex	= pxModuleHandler->GetModuleIndexFromPosition(xPosition);
						if (m_iModuleIndex>=0)
						{
							// MGH 23/6/2006: Only set the zone hash if we found a module.
							m_uZoneHash	= uHash;
						}
					}
				}
			}

			// Destroy our zone-list...
			delete[](ppxZones);
		}
	}
}

/// End of function 'Asura_Streaming_ZoneCache::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::Asura_Streaming_Zone
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Constructor functions for the streaming zone class.
//
// Return value : N/A.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone::Asura_Streaming_Zone (void) :
m_pxHandler(NULL),
m_bAllowEnvironment(false),
m_uHashID(ASURA_HASH_ID_UNSET),
m_uNumConnections(0),
m_pxConnections(NULL),
m_pxEntities(NULL)
{
	// Reset the position...
	m_xPosition.Zero();

	// Initialise the data-states...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		m_apxDataState[u]	= NULL;
	}
}

/// End of function 'Asura_Streaming_Zone::Asura_Streaming_Zone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::~Asura_Streaming_Zone
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Destructor function for the streaming zone class.
//
// Return value : N/A.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone::~Asura_Streaming_Zone ()
{
	// Delete the data-states...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		// Do we have data?
		if(pxDataState != NULL)
		{
			Asura_Assert(pxDataState->IsEmpty() || pxDataState->IsNotPresent(), "Zone data-states should be empty before deletion.");
			delete(pxDataState);
		}
	}

	// ...and the connections to other zones...
	delete [] m_pxConnections;

	// ...and the entities...
	delete(m_pxEntities);
}

/// End of function 'Asura_Streaming_Zone::~Asura_Streaming_Zone'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::Initialise
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Initialises the data for this zone.
//
// Parameters   : A pointer to the filename to use for this zone and the
//				  corresponding hash-ID.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::Initialise (Asura_Streaming_ZoneHandler& xHandler,
									   const char* szFilename,
									   const Asura_Hash_ID uHash,
									   const bool bAllowEnvironment)
{
	// Validation...
	Asura_Assert((m_uHashID == ASURA_HASH_ID_UNSET), "This zone has already been initialised once.");

	// Initialise our data...
	m_pxHandler			= &xHandler;
	m_xFilename			= szFilename;
	m_uHashID			= uHash;
	m_bAllowEnvironment	= bAllowEnvironment;

#if defined(ASURA_TOOLS)
	// Create this data-state...
	CreateDataState(HIGH_DETAIL_DATA);
#endif	// defined(ASURA_TOOLS)
}

/// End of function 'Asura_Streaming_Zone::Initialise'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::CreateDataState
// Author       : Andy Tate
// Date         : 7-Apr-2006
//
// Description  : Creates the specified data state for update puproses.
//
// Parameters   : The data-state to create.
//
// Return value : A pointer to the zone data-state created.
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_ZoneDataState* Asura_Streaming_Zone::CreateDataState (const DataState eDataState)
{
	// Do we already have data for this state?
	if(m_apxDataState[eDataState] == NULL)
	{
		Asura_String					xFullPath(m_xFilename);

		Asura_Streaming_ZoneDataState*	pxDataState;
		Asura_String					xDirectory;
		Asura_String					xFilename;
		Asura_String					xExtension;
		Asura_String					xName;
		Asura_Hash_ID					uHash;

		// No  - split the filename in order to extract the data...
		xFullPath.SplitFilename(&xDirectory, &xFilename, &xExtension);

		// Do we have a directory modifier?
		if(g_aszDirectoryModifiers[eDataState] != NULL)
		{
			// Yes - re-create the full-path, and the name...
			xFullPath	 = xDirectory;
			xFullPath	+= "\\";
			xFullPath	+= g_aszDirectoryModifiers[eDataState];
			xFullPath	+= "\\";
			xFullPath	+= xFilename;
			xFullPath	+= ".";
			xFullPath	+= xExtension;
		}

		Project_ModifyPathForNewlyCreatedZone(eDataState, xFullPath);

		// Do we have any modifiers to apply?
		if(g_aszNameModifiers[eDataState] == NULL)
		{
			// No - use the filename for the name...
			xName	= xFilename;
		}
		else
		{
			// Yes - apply the modifier...
			xName	 = g_aszNameModifiers[eDataState];
			xName	+= xFilename;
		}

		// Allocate the new data-state...
		uHash						= Asura_GetHashID(xName.GetString());
		pxDataState					= new Asura_Streaming_ZoneDataState (*this, uHash, xName, xFullPath, m_bAllowEnvironment);
		m_apxDataState[eDataState]	= pxDataState;

		if(eDataState == ENTITY_DATA)
		{
			delete(m_pxEntities);
			m_pxEntities	= new Asura_Streaming_ZoneEntities;
		}

		Asura_Assertf((!pxDataState->IsUnused() || (eDataState != HIGH_DETAIL_DATA)), ("Cannot find \"%s\" to load.", xFullPath.GetString()));
		return(pxDataState);
	}
	else
	{
		// Yes - that's bad...
		Asura_Assert(false, "Cannot create a data-state since already have one.");
		return(NULL);
	}
}

/// End of function 'Asura_Streaming_Zone::CreateDataState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::DestroyDataState
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Destroys the specified data-state for this zone (if any exists).
//
// Parameters	: The type of data-state to destroy.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::DestroyDataState (const DataState eDataState,
											 const bool bBlocking)
{
	// MGH 23/11/2006: Give projects a chance to do stuff before we start destroying
	// the data, for example, to save persistent entity data in the zones.
	if (s_pfnPreDestroyDataStateFunction)
	{
		s_pfnPreDestroyDataStateFunction(m_uHashID, eDataState, bBlocking);
	}

	Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[eDataState];

	// Is this the high-detail one?
	if(eDataState == ENTITY_DATA)
	{
		// Yes - better bin the entities too then...
		delete(m_pxEntities);
		m_pxEntities	= new Asura_Streaming_ZoneEntities;
	}

	// Destroy the data-state...
	if(pxDataState != NULL)
	{
		if(bBlocking)
		{
			// Wait until we've finished loading/destroying data...
			while(!pxDataState->IsNotPresent() &&
				!pxDataState->IsUnused() &&
				!pxDataState->IsEmpty() &&
				!pxDataState->IsFull() &&
				!pxDataState->IsTimeToLoad() &&
				!pxDataState->IsTimeToDestroy())
			{
				pxDataState->Update();
			}

			// Begin destruction...
			if(pxDataState->CommenceDataDestruction(bBlocking))
			{
				// Wait until we've finished with the data...
				while(!pxDataState->IsNotPresent() &&
					!pxDataState->IsUnused() &&
					!pxDataState->IsEmpty() &&
					!pxDataState->IsTimeToLoad())
				{
					pxDataState->Update();
				}
			}
		}

		if(pxDataState->IsEmpty() || pxDataState->IsNotPresent() || pxDataState->IsUnused())
		{
			// Destruction is complete - time to erase...
			delete(pxDataState);
			m_apxDataState[eDataState]	= NULL;
		}
		else
		{
			// Begin destruction...
			pxDataState->CommenceDataDestruction(bBlocking);
		}
	}
}

/// End of function 'Asura_Streaming_Zone::DestroyDataState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::DestroySavedEntities
// Author       : Anthony Gregson
// Date         : 3-May-2006
//
// Description  : Destroys the entities attached to this zone, leaving those that won't be saved by a quicksave.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::DestroySavedEntities (void)
{
	// Do we have any existing entities?
	if(m_pxEntities != NULL)
	{
		// AJG - 03/05/06 - Copied from entity stuff
#ifndef ASURA_ALLOW_PLAYER_ENTITY_DELETE
		/// Richard May - 19/04/2004 15:31:44 - Due to legacy reasons, players are not counted 
		/// as real entities, and are not supposed to be deleted here. As in certain projects
		/// (currently Rogue), the client and player are the same class instance, the players
		/// need to be removed from the client list before the reset...
		Asura_Entity_Client* apxSavedClients[4];
		u_int uNumSavedClients = 0;

		Asura_Hashed_List_It<Asura_Entity_Player> xIterator(Asura_Entity_Player::GetList());
		while( !xIterator.Done() )
		{
			Asura_Entity_Player* pxPlayer = xIterator.GetCurrent();
			xIterator.Next();

			Asura_Entity_Client* pxClient = m_pxEntities->FindClientEntity(pxPlayer->GetGuid());
			Asura_Entity* pxClientAsEntity = pxClient;
			if( pxPlayer && pxClientAsEntity 
				&& (pxPlayer == pxClientAsEntity) )
			{
				Asura_Assert(uNumSavedClients < 4, "More players here than expected, we're going to lose this one. Get a coder to change the number allowed.");
				if (uNumSavedClients < 4)
				{
					m_pxEntities->RemoveEntityFromList(*pxClient);
					apxSavedClients[uNumSavedClients++] = pxClient;
				}
			}
		}
#endif //#ifndef ASURA_ALLOW_PLAYER_ENTITY_DELETE

		// Remove the entities...
		m_pxEntities->DestroySavedEntities();

#ifndef ASURA_ALLOW_PLAYER_ENTITY_DELETE
		/// Richard May - 19/04/2004 15:33:03 - ...and added again after
		for (u_int u=0; u<uNumSavedClients; u++)
		{
			m_pxEntities->AddEntityToList(*apxSavedClients[u]);
		}
#endif //#ifndef ASURA_ALLOW_PLAYER_ENTITY_DELETE
	}
}

/// End of function 'Asura_Streaming_Zone::DestroySavedEntities'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::MarkLoadCompletion
// Author       : Andy Tate
// Date         : 7-Jun-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::MarkLoadCompletion (const DataState eDataState)
{
	// Do we have any special tasks to perform?
	switch(eDataState)
	{
#if !defined(ASURA_TOOLS)
		case LOW_DETAIL_DATA:
			{
				const Asura_Hash_ID		uHash		= m_apxDataState[eDataState]->GetHandlerHash();
				Asura_Module_Handler*	pxHandler	= Asura_Module_System::GetHandler(uHash);

				// Ensure we always render everything in this module handler...
				if(pxHandler != NULL)
				{
					pxHandler->SetUseModLinks(false);
#ifdef _DEBUG
					Asura_Assertf(pxHandler->HasNonSceneryModules() == false, ("Error: low detail zone (%x) not flagged as scenery", u_int(uHash)));
#endif //_DEBUG
				}
				break;
			}
#endif	// !defined(ASURA_TOOLS)

		case HIGH_DETAIL_DATA:
			{
				// These initialisations are only for high detail zones.

				// Need to initialise all our server entities from their blueprints.
				for ( Asura_Classified_List_It< Asura_Entity_Server > xServerEntityIt( m_pxEntities->GetServerEntityList() ); !xServerEntityIt.Done(); xServerEntityIt.Next() )
				{
					xServerEntityIt.GetCurrent()->InitFromBlueprint();
				}

				// Need to initialise the PFX system.
				Asura_PFX_System::PostLoadInitialise();

				// Need to PostLoadInitialise our server entities.
				for ( Asura_Classified_List_It< Asura_Entity_Server > xServerEntityIt( m_pxEntities->GetServerEntityList() ); !xServerEntityIt.Done(); xServerEntityIt.Next() )
				{
					xServerEntityIt.GetCurrent()->PostLoadInitialise();
				}

#ifdef ASURA_USE_AXON
				// Need to initialise the Axon system.
				Axon_System::PostLoadInitialise();
#endif
			}

		default:
			{
				break;
			}
	}		// End of switch.

	// Project specific initialisation...
	Project_PostLoadInitialise(eDataState);
}

/// End of function 'Asura_Streaming_Zone::MarkLoadCompletion'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::AddConnection
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Adds a connection from "this" zone to the specified one.
//
// Parameters   : Name of zone to add connection to.
//                (tools) filename of asura file to load
//
// Return value : "true" if added successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::AddConnection (const char* szToZoneName,const char* szFilename)
{
	// Do we already have a connection to this zone?
	if(HasConnection(Asura_GetHashID(szToZoneName)))
	{
		// Yes - cannot add this connection...
		return(false);
	}
	else
	{
		Asura_Streaming_Connection*			pxNewConnections	= new Asura_Streaming_Connection [m_uNumConnections + 1];
		//RAR 06/03/2006 - Will need to fill in the relative offset in some way later.
		const Asura_Vector_3				xRelativeOffset		= Asura_Maths::ZeroVector3;

		const Asura_Streaming_Connection	xNewConnection (szToZoneName, xRelativeOffset);


		// Copy the existing connections...
		for(u_int u = 0; u < m_uNumConnections; u++)
		{
			pxNewConnections[u]	= m_pxConnections[u];
		}

		// Initialise the new connection...
		pxNewConnections[m_uNumConnections++]	= xNewConnection;
#ifdef ASURA_TOOLS
		pxNewConnections[m_uNumConnections-1].SetToolsFilename(szFilename);
#endif

		// Replace the old array with the new...
		delete[](m_pxConnections);
		m_pxConnections	= pxNewConnections;

		// Return success...
		return(true);
	}
}

/// End of function 'Asura_Streaming_Zone::AddConnection'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::DeleteConnection
// Author       : Andy Tate
// Date         : 2-Feb-2006
//
// Description  : Deletes a connection from "this" zone to the specified one.
//
// Parameters   : Index of connection to delete
//
// Return value : "true" if deleted successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::DeleteConnection (u_int uIndex)
{
	// Do we have a connection to this zone?
	if(uIndex < GetNumConnection())
	{
		Asura_Streaming_Connection*	pxNewConnections	= (m_uNumConnections == 1)
			? NULL
			: new Asura_Streaming_Connection [m_uNumConnections - 1];

		// Yes - Remove the connection...
		for(u_int u = 0; u < m_uNumConnections; u++)
		{
			if(u < uIndex)
			{
				pxNewConnections[u]	= m_pxConnections[u];
			}
			else if(u > uIndex)
			{
				pxNewConnections[u - 1]	= m_pxConnections[u];
			}
		}

		// Replace the old array with the new...
		delete[](m_pxConnections);
		m_pxConnections	= pxNewConnections;
		m_uNumConnections--;

		// Return success...
		return(true);
	}
	else
	{
		// No - cannot delete it...
		return(false);
	}
}

/// End of function 'Asura_Streaming_Zone::DeleteConnection'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::ContainsPosition
// Author       : Andy Tate
// Date         : 5-Apr-2006
//
// Description  : Checks whether the specified position is contained within
//				  this streaming-zone.
//
// Parameters   : A reference to the position to check.
//
// Return value : "true" if the position is within this zone, "false" if not.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::ContainsPosition (const Asura_Vector_3& xPosition,
											 const DataState eDataState,
											 int* piHintModuleIndex,
											 const bool bCheckScenery) const
{
	const Asura_Hash_ID		uHash			= (m_apxDataState[eDataState] == NULL)
		? ASURA_HASH_ID_UNSET
		: m_apxDataState[eDataState]->GetHandlerHash();
	Asura_Module_Handler*	pxModuleHandler	= Asura_Module_System::GetHandler(uHash);

	// Do we have any modules here?
	if(pxModuleHandler == NULL)
	{
		// No - the position cannot be here then...
		return(false);
	}
	else
	{
		const int					iHintModuleIndex	= (piHintModuleIndex == NULL)
			? -1
			: *piHintModuleIndex;
		const int					iModuleIndex		= pxModuleHandler->GetModuleIndexFromPosition(xPosition, iHintModuleIndex, bCheckScenery);
		const Asura_CollisionMesh*	pxCollisionMesh		= (iModuleIndex >= 0)
			? pxModuleHandler->GetCollisionMeshForModule(iModuleIndex)
			: NULL;

		// Yes - does one of them contain our position?
		if(pxCollisionMesh == NULL)
		{
			return(false);
		}
		else if(piHintModuleIndex == NULL)
		{
			return(true);
		}
		else
		{
			*piHintModuleIndex	= iModuleIndex;
			return(true);
		}
	}
}

/// End of function 'Asura_Streaming_Zone::ContainsPosition'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::UpdateState
// Author       : Andy Tate
// Date         : 6-Apr-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::UpdateState (const DataState eDataState,
										const bool bBlocking)
{
	Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[eDataState];

	// Do we have a data-state?
	if(pxDataState == NULL)
	{
		return(false);
	}
	else if(pxDataState->Update())
	{
		return(true);
	}
	else
	{
		DestroyDataState(eDataState, bBlocking);
		return(false);
	}
}

/// End of function 'Asura_Streaming_Zone::UpdateState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::UpdateEntities
// Author       : Andy Tate
// Date         : 18-Apr-2006
//
// Description  : Updates all of the entities for this zone, setting the
//				  appropriate sub-system handlers (if necessary).
//
// Parameters   : "true" if we need to set the sub-system handlers, "false" if 
//				  they have been set externally.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::UpdateEntities (const bool bSetSubsystemHandlers)
{
	// Do we have any entities to update?
	if(m_pxEntities != NULL)
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[ENTITY_DATA];

		// Does this zone have high-detail data?
		if((pxDataState != NULL) && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if(bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			// ...then update...
			m_pxEntities->Update();
		}
	}
}

/// End of function 'Asura_Streaming_Zone::UpdateEntities'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::PreRenderUpdate
// Author       : Andy Tate
// Date         : 4-Apr-2006
//
// Description  : Updates this zone for use by the renderers.
//
// Parameters   : "true" if we need to set the sub-system handlers, "false" if 
//				  they have been set externally.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::PreRenderUpdate (const bool bSetSubsystemHandlers)
{
	Asura_Hash_ID uHash	= ASURA_HASH_ID_UNSET;

	// Render the data-states...
	for (u_int u = 0; (u < MAX_DATA_STATES) && (uHash == ASURA_HASH_ID_UNSET); u++)
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		// Does this zone have high-detail data?
		if((pxDataState != NULL) && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if(bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			uHash = pxDataState->GetHandlerHash();

			// Update the systems...
			Asura_Environment_System::Update();		// ARTTODO - What's this doing?
		}
	}
}

/// End of function 'Asura_Streaming_Zone::PreRenderUpdate'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::PreModuleUpdateUpdate
// Author       : Andy Tate
// Date         : 4-Apr-2006
//
// Description  : Updates the modules in this zone for use before the .
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::PreModuleUpdateUpdate (void)
{
	Asura_Hash_ID uHash	= ASURA_HASH_ID_UNSET;

	// run through the data-states...
	for( u_int u=0; (u < MAX_DATA_STATES) && (uHash == ASURA_HASH_ID_UNSET); u++ )
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		// Does this zone have high-detail data?
		if( (pxDataState != NULL) && pxDataState->IsFull() )
		{
			Asura_Vector_3 xOldCameraPos = *Asura_Camera::GetPosition();

			uHash = pxDataState->GetHandlerHash();

			if( Project_ApplyCameraOffset() ) // returns true if an offset (or override) has been applied
			{	
				Asura_Module_Handler*	pxHandler	= Asura_Module_System::GetHandler(uHash);

				if( pxHandler )
				{
					// apply camera offset has set the camera position correctly so use that
					pxHandler->SetUseCameraPositionOverride( true, Asura_Camera::GetPosition() );
				}

				// restore old camera pos
				Asura_Camera::SetPosition( xOldCameraPos );
			}
		}	
	}
}

/// End of function 'Asura_Streaming_Zone::PreModuleUpdateUpdate'. ///




////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::RenderEntities
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders all of the entities for this zone, setting the appropriate
//				  sub-system handlers (if necessary).
//
// Parameters   : "true" if we need to set the sub-system handlers, "false" if 
//				  they have been set externally.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::RenderEntities (const bool bSetSubsystemHandlers)
{
	// Do we have any entities to render?
	if(m_pxEntities != NULL)
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[ENTITY_DATA];

		// Does this zone have high-detail data?
		if((pxDataState != NULL) && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if(bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			// ...then render...
			m_pxEntities->Render();
		}
	}
}

/// End of function 'Asura_Streaming_Zone::RenderEntities'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::RenderEnvironment
// Author       : Andy Tate
// Date         : 31-Mar-2006
//
// Description  : Renders the environment for this zone, setting the appropriate
//				  sub-system handlers (if necessary).
//
// Parameters   : "true" if we need to set the sub-system handlers, "false" if 
//				  they have been set externally.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::RenderEnvironment (const bool bSetSubsystemHandlers)
{
	const u_int				uMax	= (s_bRenderLODs)
		? MAX_DATA_STATES
		: 1;
	Asura_Hash_ID			uHash	= ASURA_HASH_ID_UNSET;

	Asura_Module_Handler*	pxHandler;

	// Render the data-states...
	for (u_int u = 0; (u < uMax) && (uHash == ASURA_HASH_ID_UNSET); u++)
	{
		Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		// Set the sub-system handlers (if necessary)...
		if((pxDataState != NULL) && pxDataState->IsFull())
		{
			if(bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			uHash	= pxDataState->GetHandlerHash();
		}
	}

	// Get the appropriate handler...
	pxHandler	= Asura_Module_System::GetHandler(uHash);

#ifdef ASURA_TOOLS
	//The in-game renderer for PC builds doen't currently create the render meshes
	//for other zones correctly. So prevent rendering of these for now.
	//RAR 11/04/2006
	if(uHash != Tools_Streaming_System::GetHashForThisLevel()) return;
#endif

	Asura_Vector_3 xOldCameraPos = *Asura_Camera::GetPosition();
	Project_ApplyCameraOffset();

	if(pxHandler != NULL)
	{
		pxHandler->Render();
	}

	Asura_Camera::SetPosition( xOldCameraPos );
}

/// End of function 'Asura_Streaming_Zone::RenderEnvironment'. ///



///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_Zone::ResetPeriodicClientUpdateList
// Author		: M.G.Harris
// Date			: 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_Zone::ResetPeriodicClientUpdateList()
{
	if (m_pxEntities)
	{
		m_pxEntities->ResetPeriodicClientUpdateList();
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_Zone::FinishPeriodicClientUpdates
// Author		: M.G.Harris
// Date			: 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_Zone::FinishPeriodicClientUpdates(const bool bSetSubsystemHandlers /*= true*/)
{
	if (m_pxEntities)
	{
		// Does this zone have high-detail data?
		Asura_Streaming_ZoneDataState* const pxDataState = m_apxDataState[ENTITY_DATA];
		if (pxDataState && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if (bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			m_pxEntities->FinishPeriodicClientUpdates();
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_Zone::DoSomePeriodicClientUpdates
// Author		: M.G.Harris
// Date			: 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_Zone::DoSomePeriodicClientUpdates(const bool bSetSubsystemHandlers /*= true*/)
{
	if (m_pxEntities)
	{
		// Does this zone have high-detail data?
		Asura_Streaming_ZoneDataState* const pxDataState = m_apxDataState[ENTITY_DATA];
		if (pxDataState && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if (bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			m_pxEntities->DoSomePeriodicClientUpdates();
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_Zone::ResetConstantClientUpdateList
// Author		: M.G.Harris
// Date			: 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_Zone::ResetConstantClientUpdateList()
{
	if (m_pxEntities)
	{
		m_pxEntities->ResetConstantClientUpdateList();
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function		: Asura_Streaming_Zone::DoConstantClientUpdates
// Author		: M.G.Harris
// Date			: 27-Apr-2006
///////////////////////////////////////////////////////////////////////////////
void Asura_Streaming_Zone::DoConstantClientUpdates(const bool bSetSubsystemHandlers /*= true*/)
{
	if (m_pxEntities)
	{
		// Does this zone have high-detail data?
		Asura_Streaming_ZoneDataState* const pxDataState = m_apxDataState[ENTITY_DATA];
		if (pxDataState && pxDataState->IsFull())
		{
			// Set the sub-system handlers (if necessary)...
			if (bSetSubsystemHandlers)
			{
				pxDataState->SetSubsystemHandlers();
			}

			m_pxEntities->DoConstantClientUpdates();
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::ReadFromChunkStream
// Author       : Richard Rice
// Date         : 06-Mar-2006
//
// Description  : Initialises streaming zone from load.
//
// Parameters   : Stream to load from and version number of chunk being loaded
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::ReadFromChunkStream (Asura_Chunk_Stream& xStream,int iChunkVersion)
{
	xStream >> m_xPosition;
	xStream >> m_uNumConnections;

	delete[](m_pxConnections);
	m_pxConnections = m_uNumConnections ? new Asura_Streaming_Connection[m_uNumConnections] : 0;

	for(u_int i=0;i<m_uNumConnections;i++)
	{
		m_pxConnections[i].ReadFromChunkStream(xStream,iChunkVersion);
	}

	//determine offsets involving this zone
	Asura_Streaming_System::CalculateOffsets(this);

}

/// End of function 'Asura_Streaming_Zone::ReadFromChunkStream'. ///


#ifdef ASURA_TOOLS

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::WriteToChunkStream
// Author       : Richard Rice
// Date         : 06-Mar-2006
//
// Description  : Saves out connections to chunk stream.
//
// Parameters   : Stream to load from
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_Zone::WriteToChunkStream (Asura_Chunk_Stream& xStream)	const
{
	Asura_Vector_3	xPosition(m_xPosition);

	xPosition	-= Tools_Level_Export::GetExportCentre();

	xStream << xPosition;
	xStream << m_uNumConnections;

	for(u_int i=0;i<m_uNumConnections;i++)
	{
		m_pxConnections[i].WriteToChunkStream(xStream);
	}
}

/// End of function 'Asura_Streaming_Zone::ReadFromChunkStream'. ///

#endif //ASURA_TOOLS


////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::IsDataUnused
// Author       : Andy Tate
// Date         : 24-May-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::IsDataUnused (void) const
{
	bool	bUnused	= true;

	// Check the data-states...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		bUnused	= bUnused && ((m_apxDataState[u] == NULL) || m_apxDataState[u]->IsUnused());
	}

	// Was the data empty?
	return(bUnused);
}

/// End of function 'Asura_Streaming_Zone::IsDataUnused'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::IsDataFull
// Author       : Andy Tate
// Date         : 7-Apr-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::IsDataFull (void) const
{
	bool	bFull	= true;

	// Check the data-states...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		const Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		bFull	= bFull && ((pxDataState == NULL) || pxDataState->IsNotPresent() || pxDataState->IsFull());
	}

	// Was the data full?
	return(bFull);
}

/// End of function 'Asura_Streaming_Zone::IsDataFull'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::IsDataEmpty
// Author       : Andy Tate
// Date         : 7-Apr-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::IsDataEmpty (void) const
{
	bool	bEmpty	= true;

	// Check the data-states...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		const Asura_Streaming_ZoneDataState*	pxDataState	= m_apxDataState[u];

		bEmpty	= bEmpty && ((pxDataState == NULL) || pxDataState->IsNotPresent() || pxDataState->IsEmpty());
	}

	// Was the data empty?
	return(bEmpty);
}

/// End of function 'Asura_Streaming_Zone::IsDataEmpty'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::IsConnectedTo
// Author       : Andy Tate
// Date         : 7-Apr-2006
//
// Description  : Checks whether a zone with the specified hash-ID is connected
//				  to this zone.
//
// Parameters   : The hash-ID of the zone to check for connection.
//
// Return value : "true" if this zone is connected, "false" if not.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::IsConnectedTo (const Asura_Hash_ID uToHash) const
{
	// Check each connection in-turn...
	for(u_int u = 0; u < m_uNumConnections; u++)
	{
		const Asura_Streaming_Connection&	xConnection	= m_pxConnections[u];

		// Is this our zone?
		if(uToHash == xConnection.GetToZoneHashID())
		{
			return(true);
		}
	}

	// No matches...
	return(false);
}

/// End of function 'Asura_Streaming_Zone::IsConnectedTo'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::IsConnectionAvailable
// Author       : Andy Tate
// Date         : 7-Apr-2006
//
// Description  : Checks whether a zone with the specified hash-ID is available
//				  from this zone, given that we're at the position specified.
//
// Parameters   : The hash-ID of the zone to check for connection and a reference
//				  to the position to use during the check.
//
// Return value : "true" if this zone is connected, "false" if not.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_Zone::IsConnectionAvailable (const Asura_Hash_ID uToHash,
												  const DataState eDataState,
												  const Asura_Vector_3* pxPosition) const
{
	// Check each connection in-turn...
	for(u_int u = 0; u < m_uNumConnections; u++)
	{
		const Asura_Streaming_Connection&	xConnection	= m_pxConnections[u];

		// Is this our zone?
		if(uToHash == xConnection.GetToZoneHashID())
		{
			const bool	bHighDetail	= (eDataState == HIGH_DETAIL_DATA);

			// Yes - do we need to do a positional check?
			if(xConnection.GetUseBoxForLoading())
			{
				// Yes - do we actually need to check?
				if(bHighDetail)
				{
					if(pxPosition == NULL)
					{
						return(false);
					}
					else
					{
						const Asura_Bounding_Box&	xBB	= xConnection.GetBoxForLoading();

						// Yes - lets do so...
						return(xBB.Contains(*pxPosition));
					}
				}
				else
				{
					return(true);
				}
			}
			else
			{
				// No - this connection is available then...
				if(bHighDetail)
				{
					return(!xConnection.IsLowDetail());
				}
				else
				{
					return(true);
				}
			}
		}
	}

	// No matches...
	return(false);
}

/// End of function 'Asura_Streaming_Zone::IsConnectionAvailable'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::GetDataState
// Author       : Andy Tate
// Date         : 11-May-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_Zone::DataState Asura_Streaming_Zone::GetDataState (const Asura_Streaming_ZoneDataState& xZoneDataState) const
{
	// Check each data-state in turn...
	for(u_int u = 0; u < MAX_DATA_STATES; u++)
	{
		// Is this our data-state?
		if(m_apxDataState[u] == &xZoneDataState)
		{
			return(static_cast<DataState>(u));
		}
	}

	// No matches...
	return(MAX_DATA_STATES);
}

/// End of function 'Asura_Streaming_Zone::GetDataState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_Zone::SaveEntitiesToBuffer
// Author       : Anthony Gregson
// Date         : 03/05/06
//
// Description  : Saves entities in this zone to a buffer (as long as they're set as can be quicksaved)
////////////////////////////////////////////////////////////////////////////////
DEBUGVAR bool dbg_bLogZoneEntitySaveDetails = false;
static const char* ENTITY_SAVE_LOGFILE = "logfiles\\entity_save.txt";
void Asura_Streaming_Zone::SaveEntitiesToBuffer(Asura_SaveBlock_Base& xSaveBlock)
{
	if (m_pxEntities)
	{
		for( Asura_Classified_List_It<Asura_Entity_Server> xIterator(m_pxEntities->GetServerEntityList()); !xIterator.Done(); xIterator.Next() )
		{
			Asura_Entity_Server* pxEntity = xIterator.GetCurrent();
			
			// ignore entities marked for destruction
			if(pxEntity->IsDestroyed()) continue;
			if (!pxEntity->GetCanBeQuicksaved()) continue;

			// save this entity
			Asura_Chunk_Entity::Create( (Asura_Entity_Server*)pxEntity, xSaveBlock );
			
			if( dbg_bLogZoneEntitySaveDetails )
			{
#if ASURA_NETWORK_LOGGING
				const char* szBuffer = "Unknown";
				Asura_Network::GetEntityClassificationDesc(pxEntity->GetClassification(), &szBuffer);
				int iSize = pxEntity->GetChunkBufferSize();
				Asura_SimpleLogfileOutput(( ENTITY_SAVE_LOGFILE, "Classification %s (%5hu) - size %d bytes\n", szBuffer, pxEntity->GetClassification(), iSize ));
#else
				Asura_SimpleLogfileOutput(( ENTITY_SAVE_LOGFILE, "guid %9u - classification %5hu - size %5i bytes\n", pxEntity->GetGuid(), pxEntity->GetClassification(), pxEntity->GetChunkBufferSize() ));
#endif
			}

			#ifdef ASURA_TOOLS
			//and its tools data
			Asura_Chunk_Entity_ToolsData::Create((Asura_Entity_Server*)pxEntity,xSaveBlock);
			#endif
		}
	}
}

/// End of function 'Asura_Streaming_Zone::SaveEntitiesToBuffer'. ///

#endif	// ASURA_USE_STREAMING

/// End of file. ///
