/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include "g_local.h"
#include "g_gametypes.h"

//=================
//G_EndFrame_UpdateChaseCam
//
//  update chasecam follower stats & 1st person effects
//=================
void G_EndFrame_UpdateChaseCam( edict_t *ent )
{
	edict_t *targ;

	// not in chasecam
	if( !ent->r.client->chase.active ) {
		return;
	}

	// is our chase target gone?
	targ = &game.edicts[ent->r.client->chase.target];
	if( trap_GetClientState(PLAYERNUM(targ)) < CS_SPAWNED || targ->s.team < TEAM_PLAYERS ) 
	{
		// wait for timeout
		if( game.realtime < ent->r.client->chase.timeout )
			return;
		
		ent->r.client->chase.active = 0;
		G_ChaseCamMode(ent); // Chase someone, or go to spectator
		return;
	}
	ent->r.client->chase.timeout = game.realtime + 2500; // update timeout

	// cam controls
	if( ent->r.client->chase.keyNext == qtrue ) {	// change player
		ent->r.client->chase.keyNext = qfalse;
		ChaseNext(ent);
	}

	// free our psev buffer when in chasecam
	memset( ent->r.client->events, PSEV_NONE, sizeof(ent->r.client->events) );

	// copy target playerState to me
	ent->r.client->ps = targ->r.client->ps;

	// set up stat chasing for the chasing message
	ent->r.client->ps.stats[STAT_CHASING] = ENTNUM(targ); // player num can be 0, we use entnum
	ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_GENERIC;
	ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_INVENTORY;
	ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_SCOREBOARD;
	ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_CHALLENGER;
	ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_READY;
	if( targ->deadflag || ent->r.client->showscores || match.state >= MATCH_STATE_POSTMATCH ) 
		ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SCOREBOARD;	// show the scoreboard

	if( G_Gametype_hasChallengersQueue( game.gametype ) && ent->r.client->pers.queueTimeStamp )
		ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_CHALLENGER;

	if( match.state <= MATCH_STATE_WARMUP && match.ready[PLAYERNUM(ent)] )
		ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_READY;

	// chasecam uses PM_CHASECAM
	ent->r.client->ps.pmove.pm_type = PM_CHASECAM;
	ent->r.client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
	ent->r.client->ps.POVnum = ENTNUM(targ);
	ent->r.client->ps.fov = ent->r.client->pers.fov;

	VectorCopy( targ->s.origin, ent->s.origin );
	VectorCopy( targ->s.angles, ent->s.angles );
	VectorCopy( targ->r.client->v_angle, ent->r.client->v_angle );
	trap_LinkEntity(ent);
}

//====================
//G_EndServerFrames_UpdateChaseCam
//====================
void G_EndServerFrames_UpdateChaseCam( void )
{
	int i;
	edict_t	*ent;
	for( i = 0; i < game.maxclients; i++ )
	{
		ent = game.edicts + 1 + i;
		if( !ent->r.inuse || !ent->r.client )
			continue;

		if( trap_GetClientState(PLAYERNUM(ent)) < CS_SPAWNED || ent->s.team != TEAM_SPECTATOR ) {
			ent->r.client->chase.active = qfalse;
		}

		G_EndFrame_UpdateChaseCam(ent);
	}
}

//====================
//ChaseNext
//====================
void ChaseNext( edict_t *ent )
{
	int i;
	edict_t *e;

	if( ent->s.team != TEAM_SPECTATOR ) {
		if( ent->r.client->chase.active )
			ent->r.client->chase.active = qfalse;
	}
	if( !ent->r.client->chase.active )
		return;

	i = ent->r.client->chase.target;
	do {
		i++;
		if( i > game.maxclients )
			i = 1;
		e = game.edicts + i;
		if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS )
			continue;
		if( e->r.solid != SOLID_NOT )
			break;
	} while( i != ent->r.client->chase.target );

	ent->r.client->chase.target = i;
}

//====================
//ChasePrev
//====================
void ChasePrev( edict_t *ent )
{
	int i;
	edict_t *e;

	if( ent->s.team != TEAM_SPECTATOR ) {
		if( ent->r.client->chase.active )
			ent->r.client->chase.active = qfalse;
	}
	if( !ent->r.client->chase.active )
		return;

	i = ent->r.client->chase.target;
	do {
		i--;
		if( i < 1 )
			i = game.maxclients;
		e = game.edicts + i;
		if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS )
			continue;
		if( e->r.solid != SOLID_NOT )
			break;
	} while( i != ent->r.client->chase.target );

	ent->r.client->chase.target = i;
}

//====================
//G_ChaseCamMode
//====================
void G_ChaseCamMode( edict_t *ent )
{
	int i;
	edict_t *e;
	gclient_t	*client;

	client = ent->r.client;

	// Is already in chasecam
	if( client->chase.active )
		return;

	// join spectator team
	if( ent->s.team != TEAM_SPECTATOR ) {
		G_Teams_JoinTeam( ent, TEAM_SPECTATOR );
		G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->pers.netname, S_COLOR_WHITE,
			GS_TeamName(ent->s.team), S_COLOR_WHITE );
	}

	// validate any old chase target
	if( client->chase.target ) {
		if( client->chase.target < 1 || client->chase.target >= game.maxclients )
			client->chase.target = 0;
		else {
			e = &game.edicts[client->chase.target];
			if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS ) {
				client->chase.target = 0;
			}
		}
	}
	// locate a chase target
	if( !client->chase.target ) {
		for( i = 1; i <= game.maxclients; i++ ) {
			e = game.edicts + i;
			if( trap_GetClientState(PLAYERNUM(e)) == CS_SPAWNED && e->r.solid != SOLID_NOT ) {
				client->chase.target = ENTNUM(e);
				break;
			}
		}
	}

	if( client->chase.target ) {
		client->chase.active = qtrue;
		G_UpdatePlayerMatchMsg( ent ); // shut up the match message
		return;
	}

	//failed: stay as observer
	client->chase.active = qfalse;
	client->ps.pmove.pm_type = PM_SPECTATOR;
	client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
	G_CenterPrintMsg( ent, "No one to chase" );
}

//====================
//Cmd_ChaseCam_f
//====================
void Cmd_ChaseCam_f( edict_t *ent )
{
	G_ChaseCamMode( ent );
	G_Teams_LeaveChallengersQueue( ent );
}

//====================
//G_GoSpectator
//====================
void G_SpectatorMode( edict_t *ent )
{
	// join spectator team
	if( ent->s.team != TEAM_SPECTATOR ) {
		G_Teams_JoinTeam( ent, TEAM_SPECTATOR );
		G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->pers.netname, S_COLOR_WHITE,
			GS_TeamName(ent->s.team), S_COLOR_WHITE );
	}

	// was in chasecam
	if( ent->r.client->chase.active ) {
		ent->r.client->chase.active = qfalse;
	}
	ent->r.client->ps.pmove.pm_type = PM_SPECTATOR;
	ent->r.client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
}

//====================
//Cmd_Spec_f
//====================
void Cmd_Spec_f( edict_t *ent )
{
	if( ent->s.team == TEAM_SPECTATOR && !ent->r.client->pers.queueTimeStamp ) {
		G_PrintMsg( ent, "You are allready a spectator.\n" );
		return;
	}

	G_SpectatorMode( ent );
	G_Teams_LeaveChallengersQueue( ent );
}

//====================
//Cmd_SwitchChaseCamMode_f - Used by cgame for switching mode when clicking the mouse button
//====================
void Cmd_SwitchChaseCamMode_f( edict_t *ent ) {
	if( ent->r.client->chase.active )
		G_SpectatorMode( ent );
	else
		G_ChaseCamMode( ent );
}

