
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_Streaming_ZoneDataState.cpp
// Author : Andy Tate
// Date   : 6-Apr-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_ZoneDataState.h"

// Asura independent header-files...
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Core/Handlers/Asura_Handler_System.h"
#include "Streaming/Asura_Streaming_System.h"
#include "Streaming/Jobs/Asura_Streaming_Job_StreamZoneDataState.h"

// Asura platform header-files...

// Game independent header-files...

// Game platform header-files...

// Tools header-files...


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

DEBUGVAR float	dbg_fTimeBeforeLoading		= 1.5f;
DEBUGVAR float	dbg_fTimeBeforeDestroying	= 1.0f;


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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::ChangeState
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Marks the state to be changed to (to be changed later).
//
// Parameters   : The state to change to.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::ChangeState (const State eState)
{
	// Set the new state...
	m_ePendingState	= eState;
	m_bChangeState	= true;
}

/// End of function 'Asura_Streaming_ZoneDataState::ChangeState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::LoadData
// Author       : Andy Tate
// Date         : 7-Apr-2006
//
// Description  : Performs a load for the data associated with this zone.
//
// Parameters	: "true" if to be loaded blocking, "false" if it should be streamed.
//
// Return value : "true" if loaded successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_ZoneDataState::LoadData (const bool bBlocking)
{
	if(m_uParentHandlerHash == ASURA_HASH_ID_UNSET)
	{
		Asura_Assert(false, "No parent handler set - this is not correct for a zone.");
		return(false);
	}
	else
	{
		const char*	szFilename	= m_xFilename.GetString();

		// Are we performing a blocking load?
		if(bBlocking)
		{
			const Asura_Hash_ID	uZoneHash	= m_xZone.GetHashID();

			// Ensure we're using the correct handlers...
			Asura_Handler_System::CreateAllZoneData(m_uHandlerHash, m_uDataIndex, m_xName, m_uParentHandlerHash, uZoneHash, m_bAllowEnvironment);
			Asura_Handler_System::SetAllZoneData(m_uHandlerHash, m_uDataIndex, m_bAllowEnvironment);

			Asura_OutputToDebugger((">>> LoadData(): \"%s\" blocking\n", szFilename));

			// Load the file...
			if(Asura_ChunkLoading_System::LoadAsuraFile(szFilename))
			{
				const Asura_Streaming_Zone::DataState	eDataState	= m_xZone.GetDataState(*this);

				Asura_OutputToDebugger(("<<< Completed successfully.\n"));

				m_xZone.MarkLoadCompletion(eDataState);
				ChangeState(FULL);
				return(true);
			}
			else
			{
				Asura_OutputToDebugger(("<<< Failed.\n"));

				ChangeState(UNUSED);
				return(false);
			}
		}
		else
		{
			Asura_Streaming_JobQueue&	xJobQueue	= Asura_Streaming_System::GetJobQueue();

			// Add the job to the queue to be handled later...
			xJobQueue.AddJobToEnd(new Asura_Streaming_Job_CreateZoneSubsystemHandlers (*this, m_bAllowEnvironment));
			xJobQueue.AddJobToEnd(new Asura_Streaming_Job_LoadZoneDataState (*this));
			xJobQueue.AddJobToEnd(new Asura_Streaming_Job_LoadZoneDataStateComplete (*this));

			Asura_OutputToDebugger((">>> LoadData(): \"%s\" streaming\n", szFilename));

			ChangeState(LOADING);
			return(true);
		}
	}
}

/// End of function 'Asura_Streaming_ZoneDataState::LoadData'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::DestroyData
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Destroys the data held by this zone.
//
// Parameters	: "true" if to be destroyed blocking, "false" if it should be streamed.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::DestroyData (const bool bBlocking)
{
	if(m_uParentHandlerHash != ASURA_HASH_ID_UNSET)
	{
		if(bBlocking)
		{
			Asura_Streaming_ZoneHandler&	xZoneHandler	= m_xZone.GetHandler();

			// Ensure we're destroying the correct handlers...
			Asura_OutputToDebugger(("\n>>> DestroyData(): \"%s\" blocking\n", m_xFilename.GetString()));
			Asura_Handler_System::SetAllZoneData(m_uHandlerHash, m_uDataIndex, m_bAllowEnvironment);
			Asura_Handler_System::DestroyAllZoneData(m_uHandlerHash, m_uDataIndex, m_bAllowEnvironment);
			Asura_OutputToDebugger(("<<< Completed successfully.\n"));

			// Destroy our zone-index...
			xZoneHandler.DeallocateFreeZoneIndex(*this);
			ChangeState(EMPTY);
		}
		else
		{
			Asura_Streaming_JobQueue&	xJobQueue	= Asura_Streaming_System::GetJobQueue();

			// Add the job to the queue to be handled later...
			xJobQueue.AddJobToEnd(new Asura_Streaming_Job_DestroyZoneSubsystemHandlers (*this, m_bAllowEnvironment));
			xJobQueue.AddJobToEnd(new Asura_Streaming_Job_DestroyZoneDataStateComplete (*this));

			Asura_OutputToDebugger((">>> DestroyData(): \"%s\" streaming\n", m_xFilename.GetString()));

			ChangeState(DESTROYING);
		}
	}
}

/// End of function 'Asura_Streaming_ZoneDataState::DestroyData'. ///



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


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

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState
// Author       : Andy Tate
// Date         : 6-Apr-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState (Asura_Streaming_Zone& xZone,
															  const Asura_Hash_ID uHash,
															  const char* szName,
															  const char* szFilename,
															  const bool bAllowEnvironment) :
	m_xZone(xZone),
	m_uHandlerHash(uHash),
	m_uParentHandlerHash(ASURA_HASH_ID_UNSET),
	m_xName(szName),
	m_xFilename(szFilename),
	m_bAllowEnvironment(bAllowEnvironment),
	m_bChangeState(false),
	m_fSecondsInState(0.0f)
{
	// Debugging...
	Asura_OutputToDebugger((">>> Asura_Streaming_ZoneDataState(): \"%s\"\n", szFilename));

	// Reset the variables...
	ResetDataIndex();

	// Do the file exist?
	bool bFileExists = (strlen(szFilename) == 0) || Asura_FileAccess::FileExists(szFilename);

	#ifdef ASURA_TOOLS
	if(!bFileExists)
	{
		//In tools , see if the file is available across the network. RAR 01/06/2006
		char szNetworkFilename[MAX_PATH];
		Asura_FileAccess::PlatformGetNetworkFilename(szFilename,szNetworkFilename,MAX_PATH);
		bFileExists = Asura_FileAccess::FileExists(szNetworkFilename);
	}
	#endif   //ASURA_TOOLS

	if(bFileExists)
	{
		m_eState	= EMPTY;
	}
	else
	{
		m_eState	= NOT_PRESENT;
	}
}

/// End of function 'Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState
// Author       : Andy Tate
// Date         : 6-Apr-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState (Asura_Streaming_Zone& xZone,
															  const Asura_Hash_ID uHash,
															  const Asura_String& xName,
															  const Asura_String& xFilename,
															  const bool bAllowEnvironment) :
	m_xZone(xZone),
	m_uHandlerHash(uHash),
	m_uParentHandlerHash(ASURA_HASH_ID_UNSET),
	m_xName(xName),
	m_xFilename(xFilename),
	m_bAllowEnvironment(bAllowEnvironment),
	m_bChangeState(false),
	m_fSecondsInState(0.0f)
{
	const char*	szFilename	= m_xFilename.GetString();

	// Debugging...
	Asura_OutputToDebugger((">>> Asura_Streaming_ZoneDataState(): \"%s\"\n", szFilename));

	// Reset the variables...
	ResetDataIndex();

	// Do the file exist?
	bool bFileExists = (strlen(szFilename) == 0) || Asura_FileAccess::FileExists(szFilename);

	#ifdef ASURA_TOOLS
	if(!bFileExists)
	{
		//In tools , see if the file is available across the network. RAR 01/06/2006
		char szNetworkFilename[MAX_PATH];
		Asura_FileAccess::PlatformGetNetworkFilename(szFilename,szNetworkFilename,MAX_PATH);
		bFileExists = Asura_FileAccess::FileExists(szNetworkFilename);
	}
	#endif   //ASURA_TOOLS
	
	if(bFileExists)
	{
		m_eState	= EMPTY;
	}
	else
	{
		m_eState	= NOT_PRESENT;
	}
}

/// End of function 'Asura_Streaming_ZoneDataState::Asura_Streaming_ZoneDataState'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::~Asura_Streaming_ZoneDataState
// Author       : Andy Tate
// Date         : 7-Apr-2006
////////////////////////////////////////////////////////////////////////////////

Asura_Streaming_ZoneDataState::~Asura_Streaming_ZoneDataState ()
{
	// Debugging...
	Asura_OutputToDebugger((">>> ~Asura_Streaming_ZoneDataState(): \"%s\"\n", m_xFilename.GetString()));

	// Is it the correct time?
	switch(m_eState)
	{
		case FULL:
		case DESTRUCTION_PENDING:
		case TIME_TO_DESTROY:
		{
			DestroyData(true);
			break;
		}

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

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::SetSubsystemHandlers
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Sets the sub-system handlers for this data.
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::SetSubsystemHandlers (void)
{
	Asura_Handler_System::SetAllZoneData(m_uHandlerHash, m_uDataIndex, m_bAllowEnvironment);
}

/// End of function 'Asura_Streaming_ZoneDataState::SetSubsystemHandlers'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::RequestLoad
// Author       : Andy Tate
// Date         : 10-Apr-2006
//
// Description  : Depending upon the current state, this checks what we're currently
//				  doing and begins loading (if appropriate)
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::RequestLoad (void)
{
	switch(m_eState)
	{
		case LOAD_PENDING:
		case TIME_TO_LOAD:
		case LOADING:
		case FULL:
		{
			// Already loading or have loaded - do nothing...
			break;
		}

		case DESTRUCTION_PENDING:
		{
			// We're awaiting destruction - revert to being full...
			ChangeState(FULL);
			break;
		}

		case TIME_TO_DESTROY:
		case DESTROYING:
		{
			// Already destroying the data - there's nothing we can do now...
			//Asura_Assert(false, "Attempting to load a zone during it's destruction.");
			break;
		}

		case EMPTY:
		{
			// Empty but not deleted - begin loading again...
			ChangeState(LOAD_PENDING);
			break;
		}

		default:
		{
			Asura_Assert(false, "Unexpected state type for zone-data state.");
			break;
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_ZoneDataState::RequestLoad'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::CommenceDataEmptyCreation
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Creates empty data for use by this zone/
//
// Parameters	: The data-index to use and the hash-ID of the parent handlers.
//
// Return value : "true" if created successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_ZoneDataState::CommenceDataEmptyCreation (const Asura_Hash_ID uParentHandlerHash,
															   const bool bBlocking)
{
	// Is it the correct time?
	switch(m_eState)
	{
		case EMPTY:
		case LOAD_PENDING:
		case TIME_TO_LOAD:
		case NOT_PRESENT:
		{
			Asura_Streaming_ZoneHandler&	xZoneHandler	= m_xZone.GetHandler();

			// Can we find a free zone-index?
			if(xZoneHandler.AllocateFreeZoneIndex(*this))
			{
				const Asura_Streaming_Zone::DataState	eDataState	= m_xZone.GetDataState(*this);
				const Asura_Hash_ID						uZoneHash	= m_xZone.GetHashID();

				// Store the variables...
				m_uParentHandlerHash	= uParentHandlerHash;

				// Ensure we're using the correct handlers...
				Asura_Handler_System::CreateAllZoneData(m_uHandlerHash, m_uDataIndex, m_xName, m_uParentHandlerHash, uZoneHash, m_bAllowEnvironment);
				Asura_Handler_System::SetAllZoneData(m_uHandlerHash, m_uDataIndex, m_bAllowEnvironment);

				m_xZone.MarkLoadCompletion(eDataState);
				ChangeState(FULL);
				return(true);
			}
			else
			{
				Asura_Assert(false, "Failed to allocate a zone-index.");
				ChangeState(EMPTY);
				return(false);
			}
		}

		default:
		{
			Asura_Assert(false, "Incorrect state when commencing load.");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_ZoneDataState::CommenceDataEmptyCreation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::CommenceDataLoad
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Loads the high-detail data for this zone.
//
// Parameters	: The data-index to use and the hash-ID of the parent handlers.
//
// Return value : "true" if loaded successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_ZoneDataState::CommenceDataLoad (const Asura_Hash_ID uParentHandlerHash,
													  const bool bBlocking)
{
	// Is it the correct time?
	switch(m_eState)
	{
		case EMPTY:
		case LOAD_PENDING:
		case TIME_TO_LOAD:
		{
			Asura_Streaming_ZoneHandler&	xZoneHandler	= m_xZone.GetHandler();

			// Can we find a free zone-index?
			if(xZoneHandler.AllocateFreeZoneIndex(*this))
			{
				// Store the variables...
				m_uParentHandlerHash	= uParentHandlerHash;

				// Change to the loading state...
				return(LoadData(bBlocking));
			}
			else
			{
				ChangeState(EMPTY);
				return(false);
			}
		}

		default:
		{
			Asura_Assert(false, "Incorrect state when commencing load.");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_ZoneDataState::CommenceDataLoad'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::MarkLoadCompletion
// Author       : Andy Tate
// Date         : 4-May-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::MarkLoadCompletion (void)
{
	// Is it the correct time?
	if((m_eState == LOADING) || (m_ePendingState == LOADING))
	{
		const Asura_Streaming_Zone::DataState	eDataState	= m_xZone.GetDataState(*this);

		Asura_OutputToDebugger(("<<< MarkLoadCompletion(): \"%s\"\n", m_xFilename.GetString()));
		Asura_Streaming_System::MarkZoneLoaded();
		m_xZone.MarkLoadCompletion(eDataState);
		ChangeState(FULL);
	}
	else
	{
		Asura_Assert(false, "Incorrect state when marking completion of a load.");
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::RequestDestruction
// Author       : Andy Tate
// Date         : 10-Apr-2006
//
// Description  : Depending upon the current state, this checks what we're currently
//				  doing and begins loading (if appropriate)
//
// Return value : None.
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::RequestDestruction (void)
{
	switch(m_eState)
	{
		case LOAD_PENDING:
		{
			// We've not started loading yet - revert to being empty...
			ChangeState(EMPTY);
			break;
		}

		case TIME_TO_LOAD:
		case LOADING:
		{
			// Loading in progress - there's nothing we can do now...
			break;
		}

		case FULL:
		{
			// Data is full - begin destruction...
			ChangeState(DESTRUCTION_PENDING);
			break;
		}

		case DESTRUCTION_PENDING:
		case TIME_TO_DESTROY:
		case DESTROYING:
		case EMPTY:
		{
			// We're already awaiting destruction - do nothing...
			break;
		}

		default:
		{
			Asura_Assert(false, "Unexpected state type for zone-data state.");
			break;
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_ZoneDataState::RequestDestruction'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::CommenceDataDestruction
// Author       : Andy Tate
// Date         : 7-Apr-2006
//
// Description  : Begins destroying data for this zone.
//
// Return value : "true" if destroyed successfully, "false" if failed.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_ZoneDataState::CommenceDataDestruction (const bool bBlocking)
{
	// Is it the correct time?
	switch(m_eState)
	{
		case NOT_PRESENT:
		case EMPTY:
		case TIME_TO_LOAD:
		{
			return(true);
		}

		case FULL:
		case DESTRUCTION_PENDING:
		case TIME_TO_DESTROY:
		{
			// Change to the destroying state...
			DestroyData(bBlocking);
			return(true);
		}
		
		default:
		{
			Asura_Assert(false, "Incorrect state when commencing destruction.");
			return(false);
		}
	}		// End of switch.
}

/// End of function 'Asura_Streaming_ZoneDataState::CommenceDataDestruction'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::MarkDestructionCompletion
// Author       : Andy Tate
// Date         : 4-May-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_Streaming_ZoneDataState::MarkDestructionCompletion (void)
{
	// Is it the correct time?
	if((m_eState == DESTROYING) || (m_ePendingState == DESTROYING))
	{
		Asura_Streaming_ZoneHandler&	xZoneHandler	= m_xZone.GetHandler();

		// Attempt to deallocate the zone-index...
		if(xZoneHandler.DeallocateFreeZoneIndex(*this))
		{
			Asura_OutputToDebugger(("<<< MarkDestructionCompletion(): \"%s\"\n", m_xFilename.GetString()));
			ChangeState(EMPTY);
		}
		else
		{
			Asura_Assert(false, "Failed to deallocate zone-index.");
		}
	}
	else
	{
		Asura_Assert(false, "Incorrect state when marking completion of a destruction.");
	}
}

/// End of function 'Asura_Streaming_ZoneDataState::MarkDestructionCompletion'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Streaming_ZoneDataState::Update
// Author       : Andy Tate
// Date         : 6-Apr-2006
//
// Description  : Handles the state changes needed by this zone.
//
// Return value : "true" if we're to continue updating, "false" if we're done.
////////////////////////////////////////////////////////////////////////////////

bool Asura_Streaming_ZoneDataState::Update (void)
{
	// Do we need to change state?
	if(m_bChangeState)
	{
		// Yes - do so...
		m_eState			= m_ePendingState;
		m_bChangeState		= false;
		m_fSecondsInState	= 0.0f;
	}
	else
	{
		// No - increase the time we've been in this state...
		m_fSecondsInState	+= Asura_Timers::GetFrameTime();
	}

	// What state are we in?
	switch(m_eState)
	{
		case UNUSED:
		{
			// If we're unused we no longer need to update...
			return(false);
		}

		case EMPTY:
		case NOT_PRESENT:
		{
			// Do nothing - we'll be told externally when think about loading...
			return(true);
		}

		case LOAD_PENDING:
		{
			// Have we been waiting for long enough?
			if(m_fSecondsInState >= dbg_fTimeBeforeLoading)
			{
				ChangeState(TIME_TO_LOAD);
				return(true);
			}
			else
			{
				return(true);
			}
		}

		case TIME_TO_LOAD:
		{
			// Do nothing - we'll be told externally when to start loading...
			return(true);
		}

		case LOADING:
		{
			// When blocking this is over instantly...
			return(true);
		}

		case FULL:
		{
			// Do nothing - we'll be told externally when begin deletion...
			return(true);
		}

		case DESTRUCTION_PENDING:
		{
			// Have we been waiting for long enough?
			if(m_fSecondsInState >= dbg_fTimeBeforeDestroying)
			{
				ChangeState(TIME_TO_DESTROY);
				return(true);
			}
			else
			{
				return(true);
			}
		}

		case TIME_TO_DESTROY:
		{
			// Do nothing - we'll be told externally when to start destruction...
			return(true);
		}

		case DESTROYING:
		{
			// When blocking this is over instantly...
			return(true);
		}

		default:
		{
			Asura_Assert(false, "Unexpected state type for zone-data state.");
			return(false);
		}
	}	// End of switch.
}

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

#endif	// ASURA_USE_STREAMING

/// End of file. ///
