//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "prediction.h"
#include "igamemovement.h"
#include "prediction_private.h"
#include "ivrenderview.h"
#include "iinput.h"
#include "usercmd.h"
#include <vgui_controls/Controls.h>
#include <vgui/ISurface.h>
#include <vgui/IScheme.h>
#include "hud.h"
#include "IClientVehicle.h"
#include "in_buttons.h"
#include "con_nprint.h"
#include "hud_pdump.h"
#include "datacache/imdlcache.h"

#ifdef HL2_CLIENT_DLL
#include "c_basehlplayer.h"
#endif

#include "tier0/vprof.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL;

#if !defined( NO_ENTITY_PREDICTION )

ConVar	cl_predictweapons	( "cl_predictweapons","1", FCVAR_USERINFO, "Perform client side prediction of weapon effects." );
ConVar	cl_lagcompensation	( "cl_lagcompensation","1", FCVAR_USERINFO, "Perform server side lag compensation of weapon firing events." );
ConVar	cl_showerror		( "cl_showerror", "0", 0, "Show prediction errors, 2 for above plus detailed field deltas." );

static ConVar	cl_idealpitchscale	( "cl_idealpitchscale", "0.8", FCVAR_ARCHIVE );
static ConVar	cl_predictionlist	( "cl_predictionlist", "0", FCVAR_CHEAT, "Show which entities are predicting\n" );

static ConVar	cl_predictionentitydump( "cl_pdump", "-1", FCVAR_CHEAT, "Dump info about this entity to screen." );
static ConVar	cl_predictionentitydumpbyclass( "cl_pclass", "", FCVAR_CHEAT, "Dump entity by prediction classname." );
static ConVar	cl_pred_optimize( "cl_pred_optimize", "2", 0, "Optimize for not copying data if didn't receive a network update (1), and also for not repredicting if there were no errors (2)." );

#endif

extern IGameMovement *g_pGameMovement;
extern CMoveData *g_pMoveData;

void COM_Log( char *pszFile, char *fmt, ...);
typedescription_t *FindFieldByName( const char *fieldname, datamap_t *dmap );

#if !defined( NO_ENTITY_PREDICTION )
//-----------------------------------------------------------------------------
// Purpose: For debugging, find predictable by classname
// Input  : *classname - 
// Output : static C_BaseEntity
//-----------------------------------------------------------------------------
static C_BaseEntity *FindPredictableByGameClass( const char *classname )
{
	// Walk backward due to deletion from UtlVector
	int c = predictables->GetPredictableCount();
	int i;
	for ( i = 0; i < c; i++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		// Don't do anything to truly predicted things (like player and weapons )
		if ( !FClassnameIs( ent, classname ) )
			continue;

		return ent;
	}

	return NULL;
}
#endif


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrediction::CPrediction( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	m_bInPrediction = false;
	m_bFirstTimePredicted = false;

	m_nIncomingPacketNumber = 0;
	m_flIdealPitch = 0.0f;

	m_nPreviousStartFrame = -1;

	m_nCommandsPredicted = 0;
	m_nServerCommandsAcknowledged = 0;
	m_bPreviousAckHadErrors = false;
#endif
}

CPrediction::~CPrediction( void )
{
}

void CPrediction::Init( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	m_bOldCLPredictValue = cl_predict->GetInt();
#endif
}

void CPrediction::Shutdown( void )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------

void CPrediction::CheckError( int commands_acknowledged )
{
#if !defined( NO_ENTITY_PREDICTION )
	C_BasePlayer	*player;
	Vector		origin;
	Vector		delta;
	float		len;
	static int	pos = 0;

	// Not in the game yet
	if ( !engine->IsInGame() )
		return;

	// Not running prediction
	if ( !cl_predict->GetInt() )
		return;

	player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
		return;
	
	// Not predictable yet (flush entity packet?)
	if ( !player->IsIntermediateDataAllocated() )
		return;

	origin = player->GetNetworkOrigin();
		
	const void *slot = player->GetPredictedFrame( commands_acknowledged - 1 );
	if ( !slot )
		return;

	// Find the origin field in the database
	typedescription_t *td = FindFieldByName( "m_vecNetworkOrigin", player->GetPredDescMap() );
	Assert( td );
	if ( !td )
		return;

	Vector predicted_origin;

	memcpy( (Vector *)&predicted_origin, (Vector *)( (byte *)slot + td->fieldOffset[ PC_DATA_PACKED ] ), sizeof( Vector ) );
	
	// Compare what the server returned with what we had predicted it to be
	VectorSubtract ( predicted_origin, origin, delta );

	len = VectorLength( delta );
	if (len > MAX_PREDICTION_ERROR )
	{	
		// A teleport or something, clear out error
		len = 0;
	}
	else
	{
		if ( len > MIN_PREDICTION_EPSILON )
		{
			player->NotePredictionError( delta );

			if ( cl_showerror.GetInt() >= 1 )
			{
				con_nprint_t np;
				np.fixed_width_font = true;
				np.color[0] = 1.0f;
				np.color[1] = 0.95f;
				np.color[2] = 0.7f;
				np.index = 20 + ( ++pos % 20 );
				np.time_to_live = 2.0f;

				engine->Con_NXPrintf( &np, "pred error %6.3f units (%6.3f %6.3f %6.3f)", len, delta.x, delta.y, delta.z );
			}
		}
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrediction::ShutdownPredictables( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	// Transfer intermediate data from other predictables
	int c = predictables->GetPredictableCount();
	int i;

	int shutdown_count = 0;
	int release_count = 0;

	for ( i = c - 1; i >= 0 ; i-- )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		// Shutdown predictables
		if ( ent->GetPredictable() )
		{
			ent->ShutdownPredictable();
			shutdown_count++;
		}
		// Otherwise, release client created entities
		else
		{
			ent->Release();
			release_count++;
		}
	}

	if ( ( release_count > 0 ) || 
		 ( shutdown_count > 0 ) )
	{
		Msg( "Shutdown %i predictable entities and %i client-created entities\n",
			shutdown_count,
			release_count );
	}

	// All gone now...
	Assert( predictables->GetPredictableCount() == 0 );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrediction::ReinitPredictables( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	// Go through all entities and init any eligible ones
	int i;
	int c = ClientEntityList().GetHighestEntityIndex();
	for ( i = 0; i <= c; i++ )
	{
		C_BaseEntity *e = ClientEntityList().GetBaseEntity( i );
		if ( !e )
			continue;
		
		if ( e->GetPredictable() )
			continue;

		e->CheckInitPredictable( "ReinitPredictables" );
	}

	Msg( "Reinitialized %i predictable entities\n",
		predictables->GetPredictableCount() );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrediction::OnReceivedUncompressedPacket( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	m_nCommandsPredicted = 0;
	m_nServerCommandsAcknowledged = 0;
	m_nPreviousStartFrame = -1;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : commands_acknowledged - 
//			current_world_update_packet - 
// Output : void CPrediction::PreEntityPacketReceived
//-----------------------------------------------------------------------------
void CPrediction::PreEntityPacketReceived ( int commands_acknowledged, int current_world_update_packet )
{
#if !defined( NO_ENTITY_PREDICTION )
#if defined( _DEBUG )
	char sz[ 32 ];
	Q_snprintf( sz, sizeof( sz ), "preentitypacket%d", commands_acknowledged );
	PREDICTION_TRACKVALUECHANGESCOPE( sz );
#endif
	VPROF( "CPrediction::PreEntityPacketReceived" );

	// Cache off incoming packet #
	m_nIncomingPacketNumber = current_world_update_packet;

	// Don't screw up memory of current player from history buffers if not filling in history buffers
	//  during prediction!!!
	if ( !cl_predict->GetInt() )
	{
		ShutdownPredictables();
		return;
	}

	C_BasePlayer *current = C_BasePlayer::GetLocalPlayer();
	// No local player object?
	if ( !current )
		return;

	// Transfer intermediate data from other predictables
	int c = predictables->GetPredictableCount();
	int i;
	for ( i = 0; i < c; i++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		if ( !ent->GetPredictable() )
			continue;

		ent->PreEntityPacketReceived( commands_acknowledged );
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called for every packet received( could be multiple times per frame)
//-----------------------------------------------------------------------------
void CPrediction::PostEntityPacketReceived( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	PREDICTION_TRACKVALUECHANGESCOPE( "postentitypacket" );
	VPROF( "CPrediction::PostEntityPacketReceived" );

	// Don't screw up memory of current player from history buffers if not filling in history buffers
	//  during prediction!!!
	if ( !cl_predict->GetInt() )
		return;

	C_BasePlayer *current = C_BasePlayer::GetLocalPlayer();
	// No local player object?
	if ( !current )
		return;

	// Transfer intermediate data from other predictables
	int c = predictables->GetPredictableCount();
	int i;
	for ( i = 0; i < c; i++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		if ( !ent->GetPredictable() )
			continue;

		ent->PostEntityPacketReceived();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *ent - 
// Output : static bool
//-----------------------------------------------------------------------------
bool CPrediction::ShouldDumpEntity( C_BaseEntity *ent )
{
#if !defined( NO_ENTITY_PREDICTION )
	int dump_entity = cl_predictionentitydump.GetInt();
	if ( dump_entity != -1 )
	{
		bool dump = false;
		if ( ent->entindex() == -1 )
		{
			dump = ( dump_entity == ent->entindex() ) ? true : false;
		}
		else
		{
			dump = ( ent->entindex() == dump_entity ) ? true : false;
		}

		if ( !dump )
		{
			return false;
		}
	}
	else
	{
		if ( cl_predictionentitydumpbyclass.GetString()[ 0 ] == 0 )
			return false;

		if ( !FClassnameIs( ent, cl_predictionentitydumpbyclass.GetString() ) )
			return false;
	}
	return true;
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called at the end of the frame if any packets were received
// Input  : error_check - 
//			last_predicted - 
//-----------------------------------------------------------------------------
void CPrediction::PostNetworkDataReceived( int commands_acknowledged )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::PostNetworkDataReceived" );

	bool error_check = ( commands_acknowledged > 0 ) ? true : false;
#if defined( _DEBUG )
	char sz[ 32 ];
	Q_snprintf( sz, sizeof( sz ), "postnetworkdata%d", commands_acknowledged );
	PREDICTION_TRACKVALUECHANGESCOPE( sz );
#endif
#ifndef _XBOX
	CPDumpPanel *dump = GetPDumpPanel();
#endif
	//Msg( "%i/%i ack %i commands/slot\n",
	//	gpGlobals->framecount,
	//	gpGlobals->tickcount,
	//	commands_acknowledged - 1 );

	m_nServerCommandsAcknowledged += commands_acknowledged;
	m_bPreviousAckHadErrors = false;

	bool entityDumped = false;

	C_BasePlayer *current = C_BasePlayer::GetLocalPlayer();
	// No local player object?
	if ( !current )
		return;

	// Don't screw up memory of current player from history buffers if not filling in history buffers
	//  during prediction!!!
	if ( cl_predict->GetInt() )
	{
		int showlist = cl_predictionlist.GetInt();
		int totalsize = 0;
		int totalsize_intermediate = 0;

		con_nprint_t np;
		np.fixed_width_font = true;
		np.color[0] = 0.8f;
		np.color[1] = 1.0f;
		np.color[2] = 1.0f;
		np.time_to_live = 2.0f;

		// Transfer intermediate data from other predictables
		int c = predictables->GetPredictableCount();
		int i;
		for ( i = 0; i < c; i++ )
		{
			C_BaseEntity *ent = predictables->GetPredictable( i );
			if ( !ent )
				continue;

			if ( ent->GetPredictable() )
			{
				if ( ent->PostNetworkDataReceived( m_nServerCommandsAcknowledged ) )
				{
					m_bPreviousAckHadErrors = true;
				}
			}

			if ( showlist )
			{
				char sz[ 32 ];
				if ( ent->entindex() == -1 )
				{
					Q_snprintf( sz, sizeof( sz ), "handle %u", (unsigned int)ent->GetClientHandle().ToInt() );
				}
				else
				{
					Q_snprintf( sz, sizeof( sz ), "%i", ent->entindex() );
				}

				np.index = i;

				if ( showlist >= 2 )
				{
					int size = GetClassMap().GetClassSize( ent->GetClassname() );
					int intermediate_size = ent->GetIntermediateDataSize() * ( MULTIPLAYER_BACKUP + 1 );

					engine->Con_NXPrintf( &np, "%15s %30s (%5i / %5i bytes): %15s", 
						sz, 
						ent->GetClassname(),
						size,
						intermediate_size,
						ent->GetPredictable() ? "predicted" : "client created" );

					totalsize += size;
					totalsize_intermediate += intermediate_size;
				}
				else
				{
					engine->Con_NXPrintf( &np, "%15s %30s: %15s", 
						sz, 
						ent->GetClassname(),
						ent->GetPredictable() ? "predicted" : "client created" );
				}
			}
#ifndef _XBOX
			if ( error_check && 
				!entityDumped &&
				dump &&
				ShouldDumpEntity( ent ) )
			{
				entityDumped = true;
				dump->DumpEntity( ent, m_nServerCommandsAcknowledged );
			}
#endif
		}

		if ( showlist >= 2 )
		{
			np.index = i++;
			char sz1[32];
			char sz2[32];

			Q_strncpy( sz1, Q_pretifymem( (float)totalsize ), sizeof( sz1 ) );
			Q_strncpy( sz2, Q_pretifymem( (float)totalsize_intermediate ), sizeof( sz2 ) );

			engine->Con_NXPrintf( &np, "%15s %27s (%s / %s)  %14s", 
				"totals:", 
				"",
				sz1,
				sz2,
				"" );
		}

		// Zero out rest of list
		if ( showlist )
		{
			while ( i < 20 )
			{
				engine->Con_NPrintf( i, "" );
				i++;
			}
		}

		if ( error_check )
		{
			CheckError( m_nServerCommandsAcknowledged );
		}
	}

	// Can also look at regular entities
#ifndef _XBOX
	int dumpentindex = cl_predictionentitydump.GetInt();
	if ( dump && error_check && !entityDumped && dumpentindex != -1 )
	{
		int last_entity = ClientEntityList().GetHighestEntityIndex();
		if ( dumpentindex >= 0 && dumpentindex <= last_entity )
		{
			C_BaseEntity *ent = ClientEntityList().GetBaseEntity( dumpentindex );
			if ( ent )
			{
				dump->DumpEntity( ent, m_nServerCommandsAcknowledged );
				entityDumped = true;
			}
		}
	}
#endif
	if ( cl_predict->GetBool() != m_bOldCLPredictValue )
	{
		if ( !m_bOldCLPredictValue )
		{
			ReinitPredictables();
		}

		m_nCommandsPredicted = 0;
		m_nServerCommandsAcknowledged = 0;
		m_nPreviousStartFrame = -1;
	}

	m_bOldCLPredictValue = cl_predict->GetInt();

#ifndef _XBOX
	if ( dump && error_check && !entityDumped )
	{
		dump->Clear();
	}
#endif
#endif

}

//-----------------------------------------------------------------------------
// Purpose: Prepare for running prediction code
// Input  : *ucmd - 
//			*from - 
//			*pHelper - 
//			&moveInput - 
//-----------------------------------------------------------------------------
void CPrediction::SetupMove( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move ) 
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::SetupMove" );

	move->m_bFirstRunOfFunctions = IsFirstTimePredicted();
	
	move->m_nPlayerHandle = player->GetClientHandle();
	move->m_vecVelocity		= player->GetAbsVelocity();
	move->SetAbsOrigin( player->GetNetworkOrigin() );
	move->m_vecOldAngles	= move->m_vecAngles;
	move->m_nOldButtons		= player->m_Local.m_nOldButtons;
	move->m_flClientMaxSpeed = player->m_flMaxspeed;

	move->m_vecAngles		= ucmd->viewangles;
	move->m_vecViewAngles	= ucmd->viewangles;
	move->m_nImpulseCommand = ucmd->impulse;	
	move->m_nButtons		= ucmd->buttons;

	CBaseEntity *pMoveParent = player->GetMoveParent();
	if (!pMoveParent)
	{
		move->m_vecAbsViewAngles = move->m_vecViewAngles;
	}
	else
	{
		matrix3x4_t viewToParent, viewToWorld;
		AngleMatrix( move->m_vecViewAngles, viewToParent );
		ConcatTransforms( pMoveParent->EntityToWorldTransform(), viewToParent, viewToWorld );
		MatrixAngles( viewToWorld, move->m_vecAbsViewAngles );
	}


	// Ingore buttons for movement if at controls
	if (player->GetFlags() & FL_ATCONTROLS)
	{
		move->m_flForwardMove		= 0;
		move->m_flSideMove			= 0;
		move->m_flUpMove			= 0;
	}
	else
	{
		move->m_flForwardMove		= ucmd->forwardmove;
		move->m_flSideMove			= ucmd->sidemove;
		move->m_flUpMove			= ucmd->upmove;
	}
		
	IClientVehicle *pVehicle = player->GetVehicle();
	if (pVehicle)
	{
		pVehicle->SetupMove( player, ucmd, pHelper, move ); 
	}

	// Copy constraint information
	if ( player->m_hConstraintEntity )
		move->m_vecConstraintCenter = player->m_hConstraintEntity->GetAbsOrigin();
	else
		move->m_vecConstraintCenter = player->m_vecConstraintCenter;

	move->m_flConstraintRadius = player->m_flConstraintRadius;
	move->m_flConstraintWidth = player->m_flConstraintWidth;
	move->m_flConstraintSpeedFactor = player->m_flConstraintSpeedFactor;

#ifdef HL2_CLIENT_DLL
	// Convert to HL2 data.
	C_BaseHLPlayer *pHLPlayer = static_cast<C_BaseHLPlayer*>( player );
	Assert( pHLPlayer );

	CHLMoveData *pHLMove = static_cast<CHLMoveData*>( move );
	Assert( pHLMove );

	pHLMove->m_bIsSprinting = pHLPlayer->IsSprinting();
#endif
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Finish running prediction code
// Input  : &move - 
//			*to - 
//-----------------------------------------------------------------------------
void CPrediction::FinishMove( C_BasePlayer *player, CUserCmd *ucmd, CMoveData *move )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::FinishMove" );

	player->m_RefEHandle = move->m_nPlayerHandle;

	player->m_vecVelocity = move->m_vecVelocity;

	player->m_vecNetworkOrigin = move->GetAbsOrigin();
	
	player->m_Local.m_nOldButtons = move->m_nButtons;


	player->m_flMaxspeed = move->m_flClientMaxSpeed;
	
	m_hLastGround = player->GetGroundEntity();
 
	player->SetLocalOrigin( move->GetAbsOrigin() );

	IClientVehicle *pVehicle = player->GetVehicle();
	if (pVehicle)
	{
		pVehicle->FinishMove( player, ucmd, move ); 
	}

	// Sanity checks
	if ( player->m_hConstraintEntity )
		Assert( move->m_vecConstraintCenter == player->m_hConstraintEntity->GetAbsOrigin() );
	else
		Assert( move->m_vecConstraintCenter == player->m_vecConstraintCenter );
	Assert( move->m_flConstraintRadius == player->m_flConstraintRadius );
	Assert( move->m_flConstraintWidth == player->m_flConstraintWidth );
	Assert( move->m_flConstraintSpeedFactor == player->m_flConstraintSpeedFactor );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called before any movement processing
// Input  : *player - 
//			*cmd - 
//-----------------------------------------------------------------------------
void CPrediction::StartCommand( C_BasePlayer *player, CUserCmd *cmd )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::StartCommand" );

	CPredictableId::ResetInstanceCounters();

	player->m_pCurrentCommand = cmd;
	C_BaseEntity::SetPredictionRandomSeed( cmd );
	C_BaseEntity::SetPredictionPlayer( player );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called after any movement processing
// Input  : *player - 
//-----------------------------------------------------------------------------
void CPrediction::FinishCommand( C_BasePlayer *player )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::FinishCommand" );

	player->m_pCurrentCommand = NULL;
	C_BaseEntity::SetPredictionRandomSeed( NULL );
	C_BaseEntity::SetPredictionPlayer( NULL );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called before player thinks
// Input  : *player - 
//			thinktime - 
//-----------------------------------------------------------------------------
void CPrediction::RunPreThink( C_BasePlayer *player )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RunPreThink" );

	// Run think functions on the player
	if ( !player->PhysicsRunThink() )
		return;

	// Called every frame to let game rules do any specific think logic for the player
	// FIXME:  Do we need to set up a client side version of the gamerules???
	// g_pGameRules->PlayerThink( player );

	player->PreThink();
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Runs the PLAYER's thinking code if time.  There is some play in the exact time the think
//  function will be called, because it is called before any movement is done
//  in a frame.  Not used for pushmove objects, because they must be exact.
//  Returns false if the entity removed itself.
// Input  : *ent - 
//			frametime - 
//			clienttimebase - 
// Output : void CPlayerMove::RunThink
//-----------------------------------------------------------------------------
void CPrediction::RunThink (C_BasePlayer *player, double frametime )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RunThink" );

	int thinktick = player->GetNextThinkTick();

	if ( thinktick <= 0 || thinktick > player->m_nTickBase )
		return;
	
	player->SetNextThink( TICK_NEVER_THINK );

	// Think
	player->Think();
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Called after player movement
// Input  : *player - 
//			thinktime - 
//			frametime - 
//-----------------------------------------------------------------------------
void CPrediction::RunPostThink( C_BasePlayer *player )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RunPostThink" );

	// Run post-think
	player->PostThink();
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Predicts a single movement command for player
// Input  : *moveHelper - 
//			*player - 
//			*u - 
//-----------------------------------------------------------------------------
void CPrediction::RunCommand( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper *moveHelper )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RunCommand" );
#if defined( _DEBUG )
	char sz[ 32 ];
	Q_snprintf( sz, sizeof( sz ), "runcommand%04d", ucmd->command_number );
	PREDICTION_TRACKVALUECHANGESCOPE( sz );
#endif
	StartCommand( player, ucmd );

	// Set globals appropriately
	gpGlobals->curtime		= player->m_nTickBase * TICK_INTERVAL;
	gpGlobals->frametime	= TICK_INTERVAL;

	g_pGameMovement->StartTrackPredictionErrors( player );

// TODO
// TODO:  Check for impulse predicted?

	// Do weapon selection
	if ( ucmd->weaponselect != 0 )
	{
		C_BaseCombatWeapon *weapon = dynamic_cast< C_BaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) );
		if ( weapon )
		{
			player->SelectItem( weapon->GetName(), ucmd->weaponsubtype );
		}
	}

	// Latch in impulse.
	IClientVehicle *pVehicle = player->GetVehicle();
	if ( ucmd->impulse )
	{
		// Discard impulse commands unless the vehicle allows them.
		// FIXME: UsingStandardWeapons seems like a bad filter for this. 
		// The flashlight is an impulse command, for example.
		if ( !pVehicle || player->UsingStandardWeaponsInVehicle() )
		{
			player->m_nImpulse = ucmd->impulse;
		}
	}

	// Get button states
	player->UpdateButtonState( ucmd->buttons );

// TODO
//	CheckMovingGround( player, ucmd->frametime );

// TODO
//	g_pMoveData->m_vecOldAngles = player->pl.v_angle;

	// Copy from command to player unless game .dll has set angle using fixangle
	// if ( !player->pl.fixangle )
	{
		player->SetLocalViewAngles( ucmd->viewangles );
	}

	// Call standard client pre-think
	RunPreThink( player );

	// Call Think if one is set
	RunThink( player, TICK_INTERVAL );

	// Setup input.
	{
	
		SetupMove( player, ucmd, moveHelper, g_pMoveData );
	}

	// RUN MOVEMENT
	if ( !pVehicle )
	{
		Assert( g_pGameMovement );
		g_pGameMovement->ProcessMovement( player, g_pMoveData );
	}
	else
	{
		pVehicle->ProcessMovement( player, g_pMoveData );
	}

	FinishMove( player, ucmd, g_pMoveData );

	RunPostThink( player );

	g_pGameMovement->FinishTrackPredictionErrors( player );

	FinishCommand( player );

	player->m_nTickBase++;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: In the forward direction, creates rays straight down and determines the
//  height of the 'floor' hit for each forward test.  Then, if the samples show that the
//  player is about to enter an up/down slope, sets *idealpitch to look up or down that slope
//  as appropriate
//-----------------------------------------------------------------------------
void CPrediction::SetIdealPitch ( C_BasePlayer *player, const Vector& origin, const QAngle& angles, const Vector& viewheight )
{
#if !defined( NO_ENTITY_PREDICTION )
	Vector	forward;
	Vector	top, bottom;
	float	floor_height[MAX_FORWARD];
	int		i, j;
	int		step, dir, steps;
	trace_t tr;

	if ( player->GetGroundEntity() == NULL )
		return;
	
	// Don't do this on the 360..
	if ( IsX360() )
		return;

	AngleVectors( angles, &forward );
	forward[2] = 0;

	// Now move forward by 36, 48, 60, etc. units from the eye position and drop lines straight down
	//  160 or so units to see what's below
	for (i=0 ; i<MAX_FORWARD ; i++)
	{
		VectorMA( origin, (i+3)*12, forward, top );
		
		top[2] += viewheight[ 2 ];

		VectorCopy( top, bottom );

		bottom[2] -= 160;

		UTIL_TraceLine( top, bottom, MASK_SOLID, NULL, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );

		// looking at a wall, leave ideal the way it was
		if ( tr.allsolid )
			return;	

		// near a dropoff/ledge
		if ( tr.fraction == 1 )
			return;	
		
		floor_height[i] = top[2] + tr.fraction*( bottom[2] - top[2] );
	}
	
	dir = 0;
	steps = 0;
	for (j=1 ; j<i ; j++)
	{
		step = floor_height[j] - floor_height[j-1];
		if (step > -ON_EPSILON && step < ON_EPSILON)
			continue;

		if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
			return;		// mixed changes

		steps++;	
		dir = step;
	}
	
	if (!dir)
	{
		m_flIdealPitch = 0;
		return;
	}
	
	if (steps < 2)
		return;
	m_flIdealPitch = -dir * cl_idealpitchscale.GetFloat();
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Walk backward through predictables looking for ClientCreated entities
//  such as projectiles which were
// 1) not actually ack'd by the server or
// 2) were ack'd and made dormant and can now safely be removed
// Input  : last_command_packet - 
//-----------------------------------------------------------------------------
void CPrediction::RemoveStalePredictedEntities( int sequence_number )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RemoveStalePredictedEntities" );

	int oldest_allowable_command = sequence_number;

	// Walk backward due to deletion from UtlVector
	int c = predictables->GetPredictableCount();
	int i;
	for ( i = c - 1; i >= 0; i-- )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		// Don't do anything to truly predicted things (like player and weapons )
		if ( ent->GetPredictable() )
			continue;

		// What's left should be things like projectiles that are just waiting to be "linked"
		//  to their server counterpart and deleted
		Assert( ent->IsClientCreated() );
		if ( !ent->IsClientCreated() )
			continue;

		// Snag the PredictionContext
		PredictionContext *ctx = ent->m_pPredictionContext;
		if ( !ctx )
		{
			continue;
		}

		// If it was ack'd then the server sent us the entity.
		// Leave it unless it wasn't made dormant this frame, in
		//  which case it can be removed now
		if ( ent->m_PredictableID.GetAcknowledged() )
		{
			// Hasn't become dormant yet!!!
			if ( !ent->IsDormantPredictable() )
			{
				Assert( 0 );
				continue;
			}

			// Still gets to live till next frame
			if ( ent->BecameDormantThisPacket() )
				continue;

			C_BaseEntity *serverEntity = ctx->m_hServerEntity;
			if ( serverEntity )
			{
				// Notify that it's going to go away
				serverEntity->OnPredictedEntityRemove( true, ent );
			}
		}
		else
		{
			// Check context to see if it's too old?
			int command_entity_creation_happened = ctx->m_nCreationCommandNumber;
			// Give it more time to live...not time to kill it yet
			if ( command_entity_creation_happened > oldest_allowable_command )
				continue;

			// If the client predicted the KILLME flag it's possible
			//  that entity had such a short life that it actually
			//  never was sent to us.  In that case, just let it die a silent death
			if ( !ent->IsEFlagSet( EFL_KILLME ) )
			{
				if ( cl_showerror.GetInt() != 0 )
				{
					// It's bogus, server doesn't have a match, destroy it:
					Msg( "Removing unack'ed predicted entity:  %s created %s(%i) id == %s : %p\n",
						ent->GetClassname(),
						ctx->m_pszCreationModule,
						ctx->m_nCreationLineNumber,
						ent->m_PredictableID.Describe(),
						ent );
				}
			}

			// FIXME:  Do we need an OnPredictedEntityRemove call with an "it's not valid"
			// flag of some kind
		}

		// This will remove it from predictables list and will also free the entity, etc.
		ent->Release();
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrediction::RestoreOriginalEntityState( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RestoreOriginalEntityState" );
	PREDICTION_TRACKVALUECHANGESCOPE( "restore" );

	Assert( C_BaseEntity::IsAbsRecomputationsEnabled() );

	// Transfer intermediate data from other predictables
	int pc = predictables->GetPredictableCount();
	int p;
	for ( p = 0; p < pc; p++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( p );
		if ( !ent )
			continue;

		if ( ent->GetPredictable() )
		{
			ent->RestoreData( "RestoreOriginalEntityState", C_BaseEntity::SLOT_ORIGINALDATA, PC_EVERYTHING );
		}
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : current_command - 
//			curtime - 
//			*cmd - 
//			*tcmd - 
//			*localPlayer - 
//-----------------------------------------------------------------------------
void CPrediction::RunSimulation( int current_command, float curtime, CUserCmd *cmd, C_BasePlayer *localPlayer )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RunSimulation" );

	Assert( localPlayer );
	C_CommandContext *ctx = localPlayer->GetCommandContext();
	Assert( ctx );
	
	ctx->needsprocessing = true;
	ctx->cmd = *cmd;
	ctx->command_number = current_command;

	IPredictionSystem::SuppressEvents( !IsFirstTimePredicted() );

	int i;

	// Make sure simulation occurs at most once per entity per usercmd
	for ( i = 0; i < predictables->GetPredictableCount(); i++ )
	{
		C_BaseEntity *entity = predictables->GetPredictable( i );
		if ( entity )
		{
			entity->m_nSimulationTick = -1;
		}
	}

	// Don't used cached numpredictables since entities can be created mid-prediction by the player
	for ( i = 0; i < predictables->GetPredictableCount(); i++ )
	{
		// Always reset
		gpGlobals->curtime		= curtime;
		gpGlobals->frametime	= TICK_INTERVAL;

		C_BaseEntity *entity = predictables->GetPredictable( i );

		if ( !entity )
			continue;

		bool islocal = ( localPlayer == entity ) ? true : false;

		// Local player simulates first, if this assert fires then the predictables list isn't sorted 
		//  correctly (or we started predicting C_World???)
		if ( islocal )
		{
			Assert( i == 0 );
		}

		// Player can't be this so cull other entities here
		if ( entity->GetFlags() & FL_STATICPROP )
		{
			continue;
		}

		// Player is not actually in the m_SimulatedByThisPlayer list, of course
		if ( entity->IsPlayerSimulated() )
		{
			continue;
		}

		if ( AddDataChangeEvent( entity, DATA_UPDATE_DATATABLE_CHANGED, &entity->m_DataChangeEventRef ) )
		{
			entity->OnPreDataChanged( DATA_UPDATE_DATATABLE_CHANGED );
		}

		// Certain entities can be created locally and if so created, should be 
		//  simulated until a network update arrives
		if ( entity->IsClientCreated() )
		{
			// Only simulate these on new usercmds
			if ( !IsFirstTimePredicted() )
				continue;

			entity->PhysicsSimulate();
		}
		else
		{
			entity->PhysicsSimulate();
		}

		// Don't update last networked data here!!!
		entity->OnLatchInterpolatedVariables( LATCH_SIMULATION_VAR | LATCH_ANIMATION_VAR | INTERPOLATE_OMIT_UPDATE_LAST_NETWORKED );
	}

	// Always reset after running command
	IPredictionSystem::SuppressEvents( false );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrediction::Untouch( void )
{
#if !defined( NO_ENTITY_PREDICTION )
	int numpredictables = predictables->GetPredictableCount();

	// Loop through all entities again, checking their untouch if flagged to do so
	int i;
	for ( i = 0; i < numpredictables; i++ )
	{
		C_BaseEntity *entity = predictables->GetPredictable( i );
		if ( !entity )
			continue;

		if ( !entity->GetCheckUntouch() )
			continue;

		entity->PhysicsCheckForEntityUntouch();
	}
#endif
}

#if !defined( NO_ENTITY_PREDICTION )
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void InvalidateEFlagsRecursive( C_BaseEntity *pEnt, int nDirtyFlags, int nChildFlags = 0 )
{
	pEnt->AddEFlags( nDirtyFlags );
	nDirtyFlags |= nChildFlags;
	for (CBaseEntity *pChild = pEnt->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer())
	{
		InvalidateEFlagsRecursive( pChild, nDirtyFlags );
	}
}
#endif

void CPrediction::StorePredictionResults( int predicted_frame )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::StorePredictionResults" );
	PREDICTION_TRACKVALUECHANGESCOPE( "save" );

	int i;
	int numpredictables = predictables->GetPredictableCount();

	// Now save off all of the results
	for ( i = 0; i < numpredictables; i++ )
	{
		C_BaseEntity *entity = predictables->GetPredictable( i );
		if ( !entity )
			continue;

		// Certain entities can be created locally and if so created, should be 
		//  simulated until a network update arrives
		if ( !entity->GetPredictable() )
			continue;

		// FIXME: The lack of this call inexplicably actually creates prediction errors
		InvalidateEFlagsRecursive( entity, EFL_DIRTY_ABSTRANSFORM | EFL_DIRTY_ABSVELOCITY | EFL_DIRTY_ABSANGVELOCITY );
  
		entity->SaveData( "StorePredictionResults", predicted_frame, PC_EVERYTHING );
	}
#endif
}



//-----------------------------------------------------------------------------
// Purpose: 
// Input  : slots_to_remove - 
//			previous_last_slot - 
//-----------------------------------------------------------------------------
void CPrediction::ShiftIntermediateDataForward( int slots_to_remove, int number_of_commands_run )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::ShiftIntermediateDataForward" );
	PREDICTION_TRACKVALUECHANGESCOPE( "shift" );

	C_BasePlayer *current = C_BasePlayer::GetLocalPlayer();
	// No local player object?
	if ( !current )
		return;

	// Don't screw up memory of current player from history buffers if not filling in history buffers
	//  during prediction!!!
	if ( !cl_predict->GetInt() )
		return;

	int c = predictables->GetPredictableCount();
	int i;
	for ( i = 0; i < c; i++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		if ( !ent->GetPredictable() )
			continue;

		ent->ShiftIntermediateDataForward( slots_to_remove, number_of_commands_run );
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : predicted_frame - 
//-----------------------------------------------------------------------------
void CPrediction::RestoreEntityToPredictedFrame( int predicted_frame )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::RestoreEntityToPredictedFrame" );
	PREDICTION_TRACKVALUECHANGESCOPE( "restoretopred" );

	C_BasePlayer *current = C_BasePlayer::GetLocalPlayer();
	// No local player object?
	if ( !current )
		return;

	// Don't screw up memory of current player from history buffers if not filling in history buffers
	//  during prediction!!!
	if ( !cl_predict->GetInt() )
		return;

	int c = predictables->GetPredictableCount();
	int i;
	for ( i = 0; i < c; i++ )
	{
		C_BaseEntity *ent = predictables->GetPredictable( i );
		if ( !ent )
			continue;

		if ( !ent->GetPredictable() )
			continue;

		ent->RestoreData( "RestoreEntityToPredictedFrame", predicted_frame, PC_EVERYTHING );
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: Computes starting destination for intermediate prediction data results and
//  does any fixups required by network optimization
// Input  : received_new_world_update - 
//			incoming_acknowledged - 
// Output : int
//-----------------------------------------------------------------------------
int CPrediction::ComputeFirstCommandToExecute( bool received_new_world_update, int incoming_acknowledged, int outgoing_command )
{
	int destination_slot = 1;
#if !defined( NO_ENTITY_PREDICTION )
	int skipahead = 0;

	// If we didn't receive a new update ( or we received an update that didn't ack any new CUserCmds -- 
	//  so for the player it should be just like receiving no update ), just jump right up to the very 
	//  last command we created for this very frame since we probably wouldn't have had any errors without 
	//  being notified by the server of such a case.
	// NOTE:  received_new_world_update only gets set to false if cl_pred_optimize >= 1
	if ( !received_new_world_update || !m_nServerCommandsAcknowledged )
	{
		// this is where we would normally start
		int start = incoming_acknowledged + 1;
		// outgoing_command is where we really want to start
		skipahead = max( 0, ( outgoing_command - start ) );
		// Don't start past the last predicted command, though, or we'll get prediction errors
		skipahead = min( skipahead, m_nCommandsPredicted  );

		// Always restore since otherwise we might start prediction using an "interpolated" value instead of a purely predicted value
		RestoreEntityToPredictedFrame( skipahead - 1 );

		//Msg( "%i/%i no world, skip to %i restore from slot %i\n", 
		//	gpGlobals->framecount,
		//	gpGlobals->tickcount,
		//	skipahead,
		//	skipahead - 1 );
	}
	else
	{
		// Otherwise, there is a second optimization, wherein if we did receive an update, but no
		//  values differed (or were outside their epsilon) and the server actually acknowledged running
		//  one or more commands, then we can revert the entity to the predicted state from last frame, 
		//  shift the # of commands worth of intermediate state off of front the intermediate state array, and
		//  only predict the usercmd from the latest render frame.
		if ( cl_pred_optimize.GetInt() >= 2 && 
			!m_bPreviousAckHadErrors && 
			m_nCommandsPredicted > 0 && 
			m_nServerCommandsAcknowledged <= m_nCommandsPredicted )
		{
			// Copy all of the previously predicted data back into entity so we can skip repredicting it
			// This is the final slot that we previously predicted
			RestoreEntityToPredictedFrame( m_nCommandsPredicted - 1 );

			// Shift intermediate state blocks down by # of commands ack'd
			ShiftIntermediateDataForward( m_nServerCommandsAcknowledged, m_nCommandsPredicted );
			
			// Only predict new commands (note, this should be the same number that we could compute
			//  above based on outgoing_command - incoming_acknowledged - 1
			skipahead = ( m_nCommandsPredicted - m_nServerCommandsAcknowledged );

			//Msg( "%i/%i optimize2, skip to %i restore from slot %i\n", 
			//	gpGlobals->framecount,
			//	gpGlobals->tickcount,
			//	skipahead,
			//	m_nCommandsPredicted - 1 );
		}
		else
		{
			if ( m_bPreviousAckHadErrors )
			{
				C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
				
				// If an entity gets a prediction error, then we want to clear out its interpolated variables
				// so we don't mix different samples at the same timestamps. We subtract 1 tick interval here because
				// if we don't, we'll have 3 interpolation entries with the same timestamp as this predicted
				// frame, so we won't be able to interpolate (which leads to jerky movement in the player when
				// ANY entity like your gun gets a prediction error).
				float flPrev = gpGlobals->curtime;
				gpGlobals->curtime = pLocalPlayer->GetTimeBase() - TICK_INTERVAL;
				
				for ( int i = 0; i < predictables->GetPredictableCount(); i++ )
				{
					C_BaseEntity *entity = predictables->GetPredictable( i );
					if ( entity )
					{
						entity->ResetLatched();
					}
				}

				gpGlobals->curtime = flPrev;
			}
		}
	}

	destination_slot += skipahead;

	// Always reset these values now that we handled them
	m_nCommandsPredicted			= 0;
	m_bPreviousAckHadErrors			= false;
	m_nServerCommandsAcknowledged	= 0;
#endif
	return destination_slot;
}

//-----------------------------------------------------------------------------
// Actually does the prediction work, returns false if an error occurred
//-----------------------------------------------------------------------------
bool CPrediction::PerformPrediction( bool received_new_world_update, C_BasePlayer *localPlayer, 
									int incoming_acknowledged, int outgoing_command )
{
	MDLCACHE_CRITICAL_SECTION();
#if !defined( NO_ENTITY_PREDICTION )
	VPROF( "CPrediction::PerformPrediction" );

	// This makes sure , tahe we are allwoed to sample the world when it may not be ready to be sampled
	Assert( C_BaseEntity::IsAbsQueriesValid() );
	Assert( C_BaseEntity::IsAbsRecomputationsEnabled() );

	m_bInPrediction = true;

	// undo interpolation changes for entities we stand on
	C_BaseEntity *entity = localPlayer->GetGroundEntity();

	while ( entity && entity->entindex() > 0)
	{
		entity->MoveToLastReceivedPosition();
		// undo changes for moveparents too
		entity = entity->GetMoveParent();
	}

	// Start at command after last one server has processed and 
	//  go until we get to targettime or we run out of new commands
	int i = ComputeFirstCommandToExecute( received_new_world_update, incoming_acknowledged, outgoing_command );

	//Msg( "%i/%i tickbase %i\n",
	//	gpGlobals->framecount,
	//	gpGlobals->tickcount,
	//	localPlayer->m_nTickBase );

	//for ( int k = 1; k < i; k++ )
	//{
	//	Msg( "%i/%i Skip final tick %i into slot %i\n", 
	//		gpGlobals->framecount, gpGlobals->tickcount,
	//		localPlayer->m_nTickBase - i + k + 1,
	//		k - 1 );
	//}

	Assert( i >= 1 );
	while ( true )
	{
		// Incoming_acknowledged is the last usercmd the server acknowledged having acted upon
		int current_command		= incoming_acknowledged + i;
		
		// We've caught up to the current command.
		if ( current_command > outgoing_command )
			break;

		if ( i >= MULTIPLAYER_BACKUP )
			break;

		CUserCmd *cmd = input->GetUserCmd( current_command );
		
		if ( !cmd )
		{
			break;	
		}


		// Is this the first time predicting this
		m_bFirstTimePredicted = !cmd->hasbeenpredicted;

		// Set globals appropriately
		float curtime		= ( localPlayer->m_nTickBase ) * TICK_INTERVAL;

		RunSimulation( current_command, curtime, cmd, localPlayer );

		gpGlobals->curtime		= curtime;
		gpGlobals->frametime	= TICK_INTERVAL;

		// Call untouch on any entities no longer predicted to be touching
		Untouch();

		// Store intermediate data into appropriate slot
		StorePredictionResults( i - 1 ); // Note that I starts at 1

		m_nCommandsPredicted = i;

		if ( current_command == outgoing_command )
		{
			localPlayer->m_nFinalPredictedTick = localPlayer->m_nTickBase;
		}
		/*
		if ( 0 )
		{
			localPlayer->m_nFinalPredictedTick = localPlayer->m_nTickBase;
			Msg( "%i/%i Latch final tick %i start == %i into slot %i\n", 
				gpGlobals->framecount, gpGlobals->tickcount,
				localPlayer->m_nFinalPredictedTick,
				localPlayer->m_nFinalPredictedTick - i,
				i - 1 );
		}
		*/

		/*
		Msg( "%i/%i Predicted command %i tickbase == %i first %s\n", 
			gpGlobals->framecount, gpGlobals->tickcount,
			m_nCommandsPredicted,
			localPlayer->m_nTickBase,
			m_bFirstTimePredicted ? "yes" : "no" );
		*/

		// Mark that we issued any needed sounds, of not done already
		cmd->hasbeenpredicted = true;

		// Copy the state over.
		i++;
	}

//	Msg( "%i : predicted %i commands forward, %i ack'd last frame, had errors %s\n", 
//		gpGlobals->tickcount, 
//		m_nCommandsPredicted, 
//		m_nServerCommandsAcknowledged,
//		m_bPreviousAckHadErrors ? "true" : "false" );


	m_bInPrediction = false;

	
	// Somehow we looped past the end of the list (severe lag), don't predict at all
	if ( i > MULTIPLAYER_BACKUP )
	{
		return false;
	}
#endif
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : startframe - 
//			validframe - 
//			incoming_acknowledged - 
//			outgoing_command - 
//-----------------------------------------------------------------------------
void CPrediction::Update( int startframe, bool validframe, 
						 int incoming_acknowledged, int outgoing_command )
{
#if !defined( NO_ENTITY_PREDICTION )
	VPROF_BUDGET( "CPrediction::Update", VPROF_BUDGETGROUP_PREDICTION );

	bool received_new_world_update = true;

	// Still starting at same frame, so make sure we don't do extra prediction ,etc.
	if ( ( m_nPreviousStartFrame == startframe ) && 
		cl_pred_optimize.GetBool() &&
		cl_predict->GetInt() )
	{
		received_new_world_update = false;
	}

	m_nPreviousStartFrame = startframe;

	// Save off current timer values, etc.
	CGlobalVarsBase saveVars(true);
	saveVars = *gpGlobals;

	_Update( received_new_world_update, validframe, incoming_acknowledged, outgoing_command );

	// Restore current timer values, etc.
	*gpGlobals = saveVars;
#endif
}

//-----------------------------------------------------------------------------
// Do the dirty deed of predicting the local player
//-----------------------------------------------------------------------------
void CPrediction::_Update( bool received_new_world_update, bool validframe, 
						 int incoming_acknowledged, int outgoing_command )
{
#if !defined( NO_ENTITY_PREDICTION )
	C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer();
	if ( !localPlayer )
		return;

	// Always using current view angles no matter what
	// NOTE: ViewAngles are always interpreted as being *relative* to the player
	QAngle viewangles;
	engine->GetViewAngles( viewangles );
	localPlayer->SetLocalAngles( viewangles );

	if ( !validframe )
	{
		return;
	}

	// If we are not doing prediction, copy authoritative value into velocity and angle.
	if ( !cl_predict->GetInt() )
	{
		// When not predicting, we at least must make sure the player
		// view angles match the view angles...
		localPlayer->SetLocalViewAngles( viewangles );
		return;
	}

	// This is cheesy, but if we have entities that are parented to attachments on other entities, then 
	// it'll wind up needing to get a bone transform.
	{
		C_BaseAnimating::InvalidateBoneCaches();
		C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );

		// Remove any purely client predicted entities that were left "dangling" because the 
		//  server didn't acknowledge them or which can now safely be removed
		RemoveStalePredictedEntities( incoming_acknowledged );

		// Restore objects back to "pristine" state from last network/world state update
		if ( received_new_world_update )
		{
			RestoreOriginalEntityState();
		}

		if ( !PerformPrediction( received_new_world_update, localPlayer, incoming_acknowledged, outgoing_command ) )
			return;
	}

	// Overwrite predicted angles with the actual view angles
	localPlayer->SetLocalAngles( viewangles );

	// This allows us to sample the world when it may not be ready to be sampled
	Assert( C_BaseEntity::IsAbsQueriesValid() );
	
	// FIXME: What about hierarchy here?!?
	SetIdealPitch( localPlayer, localPlayer->GetLocalOrigin(), localPlayer->GetLocalAngles(), localPlayer->m_vecViewOffset );
#endif
}



//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPrediction::IsFirstTimePredicted( void ) const
{
#if !defined( NO_ENTITY_PREDICTION )
	return m_bFirstTimePredicted;
#else
	return false;
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : org - 
//-----------------------------------------------------------------------------
void CPrediction::GetViewOrigin( Vector& org )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
	{
		org.Init();
	}
	else 
	{
		org = player->GetLocalOrigin();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : org - 
//-----------------------------------------------------------------------------
void CPrediction::SetViewOrigin( Vector& org )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
		return;

	player->SetLocalOrigin( org );
	player->m_vecNetworkOrigin = org;

	player->m_iv_vecOrigin.Reset();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : ang - 
//-----------------------------------------------------------------------------
void CPrediction::GetViewAngles( QAngle& ang )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
	{
		ang.Init();
	}
	else 
	{
		ang = player->GetLocalAngles();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : ang - 
//-----------------------------------------------------------------------------
void CPrediction::SetViewAngles( QAngle& ang )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
		return;

	player->SetViewAngles( ang );
	player->m_iv_angRotation.Reset();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : ang - 
//-----------------------------------------------------------------------------
void CPrediction::GetLocalViewAngles( QAngle& ang )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
	{
		ang.Init();
	}
	else 
	{
		ang = player->pl.v_angle;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : ang - 
//-----------------------------------------------------------------------------
void CPrediction::SetLocalViewAngles( QAngle& ang )
{
	C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
	if ( !player )
		return;

	player->SetLocalViewAngles( ang );
}

#if !defined( NO_ENTITY_PREDICTION )
//-----------------------------------------------------------------------------
// Purpose: For determining that predicted creation entities are un-acked and should
//  be deleted
// Output : int
//-----------------------------------------------------------------------------
int CPrediction::GetIncomingPacketNumber( void ) const
{
	return m_nIncomingPacketNumber;
}
#endif

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPrediction::InPrediction( void ) const
{
#if !defined( NO_ENTITY_PREDICTION )
	return m_bInPrediction;
#else
	return false;
#endif
}
