bool Game::FindSpanningContexts(cl_entity_t* ent, float targettime, position_history_t** newer, position_history_t** older)
{
	assert(newer);
	assert(older);

	bool extrapolate = true;

	int imod = ent->current_position;
	int i0 = (imod - 0) & HISTORY_MASK;	// curpos (lerp end)
	int i1 = (imod - 1) & HISTORY_MASK;	// oldpos (lerp start)

	for (int i = 1; i < HISTORY_MAX - 1; i++)
	{
		position_history_t c1 = ent->ph[((imod - i) + 0) & HISTORY_MASK];
		position_history_t c2 = ent->ph[((imod - i) + 1) & HISTORY_MASK];

		if (c1.animtime == 0.f)
			break;

		if (c2.animtime >= targettime && c1.animtime <= targettime)
		{
			i0 = ((imod - i) + 1) & HISTORY_MASK;
			i1 = ((imod - i) + 0) & HISTORY_MASK;
			extrapolate = false;
			break;
		}
	}

	if (newer) *newer = &ent->ph[i0];
	if (older) *older = &ent->ph[i1];

	return extrapolate;
}

bool Game::BacktrackPlayer(cl_entity_s* pGameEntity, int lerp_msec, Vector& origin)
{
	static cvar_t* sv_unlag = g_ClientCvarsMap["sv_unlag"];
	static cvar_t* cl_lw = g_ClientCvarsMap["cl_lw"];
	static cvar_t* cl_lc = g_ClientCvarsMap["cl_lc"];
	static cvar_t* cl_updaterate = g_ClientCvarsMap["cl_updaterate"];
	static cvar_t* sv_maxunlag = g_ClientCvarsMap["sv_maxunlag"];
	static cvar_t* ex_interp = g_ClientCvarsMap["ex_interp"];
	static cvar_t* sv_unlagpush = g_ClientCvarsMap["sv_unlagpush"];

	// Player not wanting lag compensation
	if (!sv_unlag->value || !cl_lw->value || !cl_lc->value)
		return false;

	// Get true latency
	const double fakelatency = g_pMiscellaneous->m_bFakeLatencyActive ? cvars::misc.fakelatency_amount / 1000.0 : 0.0;

	double latency = /*client_state->frames[client_state->parsecountmod].latency + */fakelatency;

	if (latency > 1.5)
		latency = 1.5;
	
	double update_interval = 0.1;

	if (cl_updaterate->value > 10.f)
		update_interval = 1.0 / double(cl_updaterate->value);

	// Fixup delay based on message interval (cl_updaterate, default 20 so 50 msec)
	//latency -= update_interval;

	// Further fixup due to client side delay because packets arrive 1/2 through the frame loop, on average
	//latency -= (g_Local->m_flFrameTime/* * 0.5*/);

	// Absolute bounds on lag compensation
	double correct = min(LAG_COMPENSATION_DATA_TIME, latency);

	// See if server is applying a lower cap
	if (sv_maxunlag->value)
	{
		// Make sure it's not negative
		if (sv_maxunlag->value < 0.f)
			sv_maxunlag->value = 0.f;

		// Apply server cap
		correct = min(correct, sv_maxunlag->value);
	}

	// Get true timestamp
	const double realtime = client_state->time;

	// Figure out timestamp for which we are looking for data
	double targettime = realtime - correct;

	// Remove lag based on player interpolation, as well
	double interptime = (lerp_msec == -1) ? ex_interp->value : (lerp_msec / 1000.0);

	if (interptime > 0.1)
		interptime = 0.1;

	if (update_interval > interptime)
		interptime = update_interval;

	targettime -= interptime;

	// Server can apply a fudge, probably not needed, defaults to 0.0f
	targettime += sv_unlagpush->value;

	// Cap target to present time, of course
	targettime = min(realtime, targettime);

	position_history_t *newer, *older;

	FindSpanningContexts(pGameEntity, float(targettime), &newer, &older);

	if (!newer || !older)
		return false;

	float frac = 0.f;

	if (newer->animtime != older->animtime)
	{
		frac = float(targettime - older->animtime) / (newer->animtime - older->animtime);
		frac = std::clamp(frac, 0.f, 1.f);
	}

	Vector delta = newer->origin - older->origin;

	if (delta.LengthSqr() > LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR)
		return false;

	origin = older->origin + delta * frac;

	return true;
}