
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_ConnectionState_BeginGameInitialisation.cpp
// Author : Andy Tate
// Date   : 25-Feb-2009
//
// Copyright (C) 2009 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Core/States/Connection/AVP_ConnectionState_BeginGameInitialisation.h"

// Asura independent header-files...
#include "Core/Asura_Multithread.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_DebugText.h"
#include "LoadAndSave/Asura_LoadAndSave.h"

// Game independent header-files...
#include "Core/AVP_Connection.h"
#include "Core/AVP_Core.h"
#include "Core/AVP_Game_Core.h"
#include "Core/AVP_GameStatus.h"
#include "Core/AVP_Settings.h"
#include "GUIMenu/AVP_GUIMenu_System.h"
#include "Network/AVP_Player.h"
#include "Network/AVP_Server.h"


////////////////////////////////////////////////////////////////////////////////
// Defines...
////////////////////////////////////////////////////////////////////////////////

#if defined(AVP_ASYNCHRONOUS_LOADING)
#define AVP_BEGININITIALISATION_EXECUTE(fn)	s_szInitialisationName = #fn; Asura_Thread_System::CreateThread(s_szInitialisationName, fn, this)
#else
#define AVP_BEGININITIALISATION_EXECUTE(fn)	s_szInitialisationName = #fn; fn()
#endif	//defined(AVP_ASYNCHRONOUS_LOADING)


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

DEBUGVAR float 	dbg_fRequestActorCreateDelayTime		= 1.0f;
DEBUGVAR float 	dbg_fServerRequestActorCreateDelayTime	= 3.0f;
DEBUGVAR float 	dbg_fMultiplayerEnterGameDelayTime		= 5.0f;		// A further delay to allow first full net message updates to arrive.
DEBUGVAR float 	dbg_fMultiplayerEnterGameTimeOut		= 120.0f;	// How long client waits before disconnecting themselves from an unresponsive server


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

const char*		AVP_ConnectionState_BeginGameInitialisation::s_szInitialisationName		= NULL;
volatile bool	AVP_ConnectionState_BeginGameInitialisation::s_bInitialisationComplete	= false;


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

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::InitialiseNewGame
// Author       : Andy Tate
// Date         : 25-Feb-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::CompleteInitialisation(void)
{
	// Set-up the species...
	if (AVP_Settings::IsMultiPlayer())
	{
		AVP_Settings::SetPlayerSpecies(AVP_Settings::AVP_SPECIES_UNKNOWN);
	}

	// Initialise the relevant time-outs...
	if (AVP_Settings::GetIsServer())
	{
		// If we're the server, and there are other machines connected, it's worth waiting a short time before requesting creating our actor to allow others to register their initialising state, otherwise we'll just start without them almost every time.
		if (Asura_Network::GetNumberOfMachines() > 1)
		{
			m_fRequestActorCreateDelay = dbg_fServerRequestActorCreateDelayTime;
		}
		else
		{
			AVP_Player* pxPlayer = AVP_Game_Core::GetPlayer();
			if ( pxPlayer )
			{
				pxPlayer->SendActorCreateMessage(false);
			}
			m_fRequestActorCreateDelay = 0.0f;
		}
	}
	else
	{
		m_fRequestActorCreateDelay = dbg_fRequestActorCreateDelayTime;
	}

	if (AVP_Settings::GetExternalNetworkActive())
	{
		m_fEnterGameDelay	= dbg_fMultiplayerEnterGameDelayTime;
		m_fEnterGameTimeOut	= dbg_fMultiplayerEnterGameTimeOut; 
	}
	else
	{
		m_fEnterGameDelay	= 0.0f;
		m_fEnterGameTimeOut = Asura_Maths::LargeFloat; 
	}

	// We're all done...
	s_bInitialisationComplete	= true;
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::InitialiseNewGame'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerInitialisation
// Author       : Andy Tate
// Date         : 25-Feb-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerInitialisation(void)
{
	// Ensure that our entities are initialised...
	AVP_Game_Core::Initialise();
	AVP_Game_Core::SaveLevelStartBlock();
	AVP_Game_Core::SaveCheckPointBlock();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerInitialisation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointLoad
// Author       : Andy Tate
// Date         : 25-Feb-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointLoad(void)
{
	// Ensure that our entities are initialised (and the state of the level is saved)...
	AVP_Game_Core::Initialise();
	AVP_Game_Core::SaveLevelStartBlock();

	// Reset everything before loading (and initialising the checkpoint data)...
	AVP_Game_Core::LoadCheckPointBlock();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointLoad'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointRestart
// Author       : Andy Tate
// Date         : 25-Feb-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointRestart(void)
{
	// Simply load the previously saved checkpoint data...
	AVP_Game_Core::LoadCheckPointBlock();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerCheckpointRestart'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerLevelRestart
// Author       : Andy Tate
// Date         : 6-May-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerLevelRestart(void)
{
	// Simply load the previously saved start-block...
	AVP_Game_Core::LoadLevelStartBlock();
	AVP_Game_Core::SaveCheckPointBlock();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformSinglePlayerLevelRestart'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerInitialisation
// Author       : Andy Tate
// Date         : 25-Feb-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerInitialisation(void)
{
	// Ensure that our entities are initialised...
	AVP_Game_Core::Initialise();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerInitialisation'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerClientRestart
// Author       : Andy Tate
// Date         : 6-May-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerClientRestart(void)
{
	// Reset everything before continuing...
	Asura_LoadAndSave::ResetBeforeLoad();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerClientRestart'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerServerRestart
// Author       : Andy Tate
// Date         : 6-May-2009
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerServerRestart(void)
{
	// Ensure that our entities are initialised...
	AVP_Game_Core::Initialise();

	// Tidy everything up...
	CompleteInitialisation();
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::PerformMultiplayerServerRestart'. ///



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


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

////////////////////////////////////////////////////////////////////////////////
// Function	: AVP_ConnectionState_BeginGameInitialisation::Enter
// Author	: Anthony Gregson
// Date		: 22/11/07
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::Enter(const u_int uPreviousState)
{
#if defined(ASURA_MEMORYMANAGEMENT_ENABLED)
	//Asura_MemoryManagement_System::DumpMallocSummary();
#endif	//defined(ASURA_MEMORYMANAGEMENT_ENABLED)

	AVP_Core::SetShouldUpdateSoundSystem(false);
	AVP_Game_Core::SetReceivedInitialiseCompleteMessage(false);	// reset this flag here so we're not relying on the thread setting it for us.

	// Actually perform/begin the initialisation...
	s_bInitialisationComplete	= false;

	// Decide what we need to do to perform the initialisation...
	if(AVP_Settings::IsSinglePlayer())
	{
		if(AVP_GameStatus::GetLoadCheckpoint())
		{
			AVP_BEGININITIALISATION_EXECUTE(PerformSinglePlayerCheckpointLoad);
		}
		else if(AVP_GameStatus::GetRestoreCheckpoint())
		{
			AVP_BEGININITIALISATION_EXECUTE(PerformSinglePlayerCheckpointRestart);
		}
		else if(AVP_GameStatus::GetRestartLevel())
		{
			AVP_BEGININITIALISATION_EXECUTE(PerformSinglePlayerLevelRestart);
		}
		else
		{
			AVP_BEGININITIALISATION_EXECUTE(PerformSinglePlayerInitialisation);
		}
	}
	else
	{
		if (uPreviousState == AVP_Connection::STATE_WAITFORGUID)
		{
			AVP_BEGININITIALISATION_EXECUTE(PerformMultiplayerInitialisation);
		}
		else
		{
			if(AVP_Settings::GetIsServer())
			{
				AVP_BEGININITIALISATION_EXECUTE(PerformMultiplayerServerRestart);
			}
			else
			{
				AVP_BEGININITIALISATION_EXECUTE(PerformMultiplayerClientRestart);
			}
		}
	}
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::Enter'. ///



////////////////////////////////////////////////////////////////////////////////
// Function	: AVP_ConnectionState_BeginGameInitialisation::Update
// Author	: Anthony Gregson
// Date		: 22/11/07
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::Update()
{
	if(s_bInitialisationComplete)
	{
		if (!GetParentConnection().ValidateNetworkConnection())
		{
			// If the above failed it will have called for a state change, so don't do any further processing here.
			return;
		}

		if (m_fRequestActorCreateDelay > 0.0f)
		{
			m_fRequestActorCreateDelay -= Asura_Timers::GetFrameTime();
			if (m_fRequestActorCreateDelay <= 0.0f)
			{
				AVP_Player* pxPlayer = AVP_Game_Core::GetPlayer();
				if ( pxPlayer )
				{
					pxPlayer->SendActorCreateMessage(false);
				}
				m_fRequestActorCreateDelay = dbg_fServerRequestActorCreateDelayTime; 
			}
		}
	}

	// Update the network every frame regardless of completion...
	Asura_Network::Update();
	if (AVP_Settings::GetIsServer() && AVP_Game_Core::GetReceivedInitialiseCompleteMessage())
	{
		// Actually start sending client updates now.
		Asura_Network::EndOfFrameUpdate();
	}
	else
	{
		Asura_Network::FlushBufferedMessages();
	}

	if(s_bInitialisationComplete)
	{
		if(Asura_Network::IsServer())
		{
			AVP_Server::UpdateClientInitialisation();
		}

#if defined(ASURA_USE_GUI_MENU_SYSTEM)
		if( AVP_Game_Core::GetReceivedInitialiseCompleteMessage() )
#endif
		{
			m_fEnterGameDelay -= Asura_Timers::GetFrameTime();
			if (m_fEnterGameDelay <= 0.0f)
			{
				GetParentMachine().ChangeState(AVP_Connection::STATE_ENDGAMEINITIALISATION);
			}
		}
		else
		{
			m_fEnterGameTimeOut -= Asura_Timers::GetFrameTime();
			if (m_fEnterGameTimeOut <= 0.0f)
			{
				GetParentConnection().Terminate(ASURA_NDR_TIMEOUT);
			}
		}
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function	: AVP_ConnectionState_BeginGameInitialisation::Render
// Author	: Anthony Gregson
// Date		: 22/11/07
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::Render(void) const
{
#if !defined(ASURA_TOOLS)
	AVP_Core::BeginRender(false);
	{
		AVP_GUIMenu_System::Render(true);
#if defined(ASURA_USE_DEBUG_TEXT)
		Asura_DebugText::AddTextBlock(50, 20, 0xFFFFFFFF, 0, "BeginGameInitialisation: %.02f secs", GetParentMachine().GetTimeInCurrentState());
		Asura_DebugText::AddTextBlock(50, 21, 0xFFFFFFFF, 0, "-> %s()", s_szInitialisationName);
#endif	// defined(ASURA_USE_DEBUG_TEXT)
	}
	AVP_Core::EndRender();
#endif	// defined(ASURA_TOOLS)
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::Render'. ///


////////////////////////////////////////////////////////////////////////////////
// Function	: AVP_ConnectionState_BeginGameInitialisation::Exit
// Author	: Anthony Gregson
// Date		: 22/11/07
////////////////////////////////////////////////////////////////////////////////

void AVP_ConnectionState_BeginGameInitialisation::Exit(const u_int uNextState)
{
	AVP_Core::SetShouldUpdateSoundSystem(true);
}

/// End of function 'AVP_ConnectionState_BeginGameInitialisation::Exit'. ///


/// End of file. ///
