/*
   Copyright (C) 2002-2003 Victor Luchits

   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 "cg_local.h"

//==================
//CG_SC_Print
//==================
static void CG_SC_Print( void )
{
	CG_Printf( "%s", trap_Cmd_Argv( 1 ) );
}

//==================
//CG_SC_ChatPrint
//==================
static void CG_SC_ChatPrint( void )
{
	const char *text = trap_Cmd_Argv( 1 );
	const char *stripped = COM_RemoveColorTokens( text );
	const cvar_t *filter = (cgs.tv ? cg_chatFilterTV : cg_chatFilter);

	if( !Q_strnicmp( stripped, "[TEAM]", strlen( "[TEAM]" ) ) || !Q_strnicmp( stripped, "[SPEC]", strlen( "[SPEC]" ) ) )
	{
		if( filter->integer & 2 )
			return;
	}
	else
	{
		if( filter->integer & 1 )
			return;
	}

	CG_Printf( "%s", text );
	if( cg_chatBeep->integer )
		trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxChat ), CHAN_AUTO, 1.0f );
}

//==================
//CG_SC_TVChatPrint
//==================
static void CG_SC_TVChatPrint( void )
{
	const char *text = trap_Cmd_Argv( 1 );
	const cvar_t *filter = (cgs.tv ? cg_chatFilterTV : cg_chatFilter);

	if( filter->integer & 4 )
		return;

	CG_Printf( S_COLOR_RED "[TV]" S_COLOR_WHITE "%s", text );
	if( cg_chatBeep->integer )
		trap_S_StartGlobalSound( CG_MediaSfx( cgs.media.sfxChat ), CHAN_AUTO, 1.0f );
}

//==================
//CG_SC_CenterPrint
//==================
static void CG_SC_CenterPrint( void )
{
	CG_CenterPrint( trap_Cmd_Argv( 1 ) );
}

//================
//CG_ConfigString
//================
void CG_ConfigString( int i, char *s )
{
	char olds[MAX_QPATH];
	int len;

	// wsw : jal : warn if configstring overflow
	len = strlen( s );
	if( len >= MAX_QPATH )
		CG_Printf( "%sWARNING:%s Configstring %i overflowed\n", S_COLOR_YELLOW, S_COLOR_WHITE, i );

	if( i < 0 || i >= MAX_CONFIGSTRINGS )
	{
		CG_Error( "configstring > MAX_CONFIGSTRINGS" );
	}

	Q_strncpyz( olds, cgs.configStrings[i], sizeof( olds ) );
	Q_strncpyz( cgs.configStrings[i], s, sizeof( cgs.configStrings[i] ) );

	// do something apropriate
	if( i == CS_MAPNAME )
	{
		CG_RegisterLevelShot();
		CG_RegisterLevelMinimap();
	}
	else if( i == CS_SERVERSETTINGS )
	{
		CG_UpdateServerSettings();
	}
	else if( i >= CS_MODELS && i < CS_MODELS+MAX_MODELS )
	{
		if( cgs.configStrings[i][0] == '$' )
		{                               // indexed pmodel
			cgs.pModelsIndex[i-CS_MODELS] = CG_RegisterPlayerModel( cgs.configStrings[i]+1 );
		}
		else
		{
			cgs.modelDraw[i-CS_MODELS] = CG_RegisterModel( cgs.configStrings[i] ); // skelmod
		}
	}
	else if( i >= CS_SOUNDS && i < CS_SOUNDS+MAX_SOUNDS )
	{
		if( cgs.configStrings[i][0] != '*' )
		{
			cgs.soundPrecache[i-CS_SOUNDS] = trap_S_RegisterSound( cgs.configStrings[i] );
		}
	}
	else if( i >= CS_IMAGES && i < CS_IMAGES+MAX_IMAGES )
	{
		cgs.imagePrecache[i-CS_IMAGES] = trap_R_RegisterPic( cgs.configStrings[i] );
	}
	else if( i >= CS_SKINFILES && i < CS_SKINFILES+MAX_SKINFILES )
	{
		cgs.skinPrecache[i-CS_SKINFILES] = trap_R_RegisterSkinFile( cgs.configStrings[i] );
	}
	else if( i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES )
	{
		CG_SetLightStyle( i - CS_LIGHTS );
	}
	else if( i >= CS_ITEMS && i < CS_ITEMS+MAX_ITEMS )
	{
		CG_ValidateItemDef( i - CS_ITEMS, cgs.configStrings[i] );
	}
	else if( i >= CS_PLAYERINFOS && i < CS_PLAYERINFOS+MAX_CLIENTS )
	{
		CG_LoadClientInfo( &cgs.clientInfo[i-CS_PLAYERINFOS], cgs.configStrings[i], i-CS_PLAYERINFOS );
	}
	else if( i >= CS_GAMECOMMANDS && i < CS_GAMECOMMANDS+MAX_GAMECOMMANDS )
	{
		if( !cgs.demoPlaying )
			trap_Cmd_AddCommand( cgs.configStrings[i], NULL );
	}
}

//================
//CG_SC_Scoreboard	// wsw : jal : scoreboard templates
//================
static void CG_SC_Scoreboard( void )
{
	SCR_UpdateScoreboardMessage( trap_Cmd_Argv( 1 ) );
}

//================
//CG_SC_PrintPlayerStats
//================
static void CG_SC_PrintPlayerStats( const char *s, void ( *pp ) )
{
	int playerNum;
	int i, shot_weak, hit_weak, shot_strong, hit_strong, hit_total, shot_total;
	int total_damage_given, total_damage_received, health_taken, armor_taken;
	gitem_t *item;
	void ( *print )( const char *format, ... ) = pp;

	playerNum = CG_ParseValue( &s );
	if( playerNum < 0 || playerNum >= MAX_CLIENTS )
		return;

	// print stats to console/file
	print( "Stats for %s" S_COLOR_WHITE ":\r\n\r\n", cgs.clientInfo[playerNum].name );
	print( "   Weapon             Weak               Strong\r\n" );
	print( "    hit/shot percent   hit/shot percent   hit/shot percent\r\n" );

	for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ )
	{
		if( i == WEAP_SHOCKWAVE )
			continue;

		item = GS_FindItemByTag( i );
		assert( item );

		shot_total = CG_ParseValue( &s );
		if( shot_total < 1 )  // only continue with registered shots
			continue;
		hit_total = CG_ParseValue( &s );

		shot_strong = CG_ParseValue( &s );
		hit_strong = (shot_strong != shot_total ? CG_ParseValue( &s ) : hit_total);

		shot_weak = shot_total - shot_strong;
		hit_weak = hit_total - hit_strong;

		// name
		print( "%s%2s" S_COLOR_WHITE ": ", item->color, item->short_name );

#define STATS_PERCENT(hit,total) ((total) == 0 ? 0 : ((hit) == (total) ? 100 : (float)(hit) * 100.0f / (float)(total)))

		// total
		print( S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i      " S_COLOR_YELLOW "%2.1f",
			hit_total, shot_total, STATS_PERCENT( hit_total, shot_total ) );

		// weak
		print( "    " S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i      " S_COLOR_YELLOW "%2.1f",
			hit_weak, shot_weak,  STATS_PERCENT( hit_weak, shot_weak ) );

		// strong
		print( "   " S_COLOR_GREEN "%3i" S_COLOR_WHITE "/" S_COLOR_CYAN "%3i      " S_COLOR_YELLOW "%2.1f",
			hit_strong, shot_strong, STATS_PERCENT( hit_strong, shot_strong ) );

		print( "\r\n" );
	}

	print( "\r\n" );

	total_damage_given = CG_ParseValue( &s );
	total_damage_received = CG_ParseValue( &s );

	print( S_COLOR_YELLOW "Damage given/received: " S_COLOR_WHITE "%i/%i " S_COLOR_YELLOW "ratio: %s%3.2f\r\n",
		total_damage_given, total_damage_received,
		( total_damage_given > total_damage_received ? S_COLOR_GREEN : S_COLOR_RED ),
		STATS_PERCENT( total_damage_given, total_damage_given + total_damage_received ) );

	health_taken = CG_ParseValue( &s );
	armor_taken = CG_ParseValue( &s );

	print( S_COLOR_YELLOW "Health/Armor taken : " S_COLOR_CYAN "%i" S_COLOR_WHITE "/" S_COLOR_CYAN "%i\r\n",
		health_taken, armor_taken );

#undef STATS_PERCENT
}

//================
//CG_SC_PlayerStats	// wsw : jal : scoreboard templates
//================
static void CG_SC_PlayerStats( void )
{
	const char *s;
	qboolean print;

	print = (atoi( trap_Cmd_Argv( 1 ) ) == 1 ? qtrue : qfalse);
	s = trap_Cmd_Argv( 2 );

	if( !print )
	{	// scoreboard message update
		SCR_UpdatePlayerStatsMessage( s );
		return;
	}

	CG_SC_PrintPlayerStats( s, CG_Printf );
}

//================
//CG_SC_PrintStatsToFile
//================
static int cg_statsFileHandle;
void CG_SC_PrintStatsToFile( const char *format, ... )
{
	va_list	argptr;
	char msg[1024];

	va_start( argptr, format );
	Q_vsnprintfz( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	trap_FS_Print( cg_statsFileHandle, msg );
}

//================
//CG_SC_AutoRecord
//================
static void CG_SC_AutoRecord( void )
{
	static qboolean autorecording = qfalse;
	time_t long_time;
	struct tm *newtime;
	char name[MAX_STRING_CHARS];
	char mapname[MAX_CONFIGSTRING_CHARS];
	const char *cleanplayername, *cleanplayername2, *action;
	qboolean spectator;

	// filter out autorecord commands when playing a demo
	if( cgs.demoPlaying )
		return;

	if( cg.frame.playerState.pmove.pm_type == PM_SPECTATOR || cg.frame.playerState.pmove.pm_type == PM_CHASECAM )
		spectator = qtrue;
	else
		spectator = qfalse;

	// get date from system
	time( &long_time );
	newtime = localtime( &long_time );

	// remove color tokens from player names (doh)
	cleanplayername = COM_RemoveColorTokens( cgs.clientInfo[cg.view.POVent-1].name );

	// remove junk chars from player names for files
	cleanplayername2 = COM_RemoveJunkChars( cleanplayername );

	// lowercase mapname
	Q_strncpyz( mapname, cgs.configStrings[CS_MAPNAME], sizeof( mapname ) );
	Q_strlwr( mapname );

	// make file name
	// duel_year-month-day_hour-min_map_player
	Q_snprintfz( name, sizeof( name ), "%s_%04d-%02d-%02d_%02d-%02d_%s_%s",
	             GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ),
	             newtime->tm_year + 1900, newtime->tm_mon+1, newtime->tm_mday,
	             newtime->tm_hour, newtime->tm_min,
	             mapname,
	             cleanplayername2
	);

	action = trap_Cmd_Argv( 1 );
	if( !Q_stricmp( action, "start" ) )
	{
		if( cg_autoaction_demo->integer && ( !spectator || cg_autoaction_spectator->integer ) )
		{
			trap_Cmd_ExecuteText( EXEC_NOW, "stop silent" );
			trap_Cmd_ExecuteText( EXEC_NOW, va( "record autorecord/%s/%s silent",
			                                    GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ), name ) );
			autorecording = qtrue;
		}
	}
	else if( !Q_stricmp( action, "stop" ) )
	{
		if( autorecording )
		{
			trap_Cmd_ExecuteText( EXEC_NOW, "stop silent" );
			autorecording = qfalse;
		}

		if( cg_autoaction_screenshot->integer && ( !spectator || cg_autoaction_spectator->integer ) )
		{
			trap_Cmd_ExecuteText( EXEC_NOW, va( "screenshot autorecord/%s/%s silent",
			                                    GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ), name ) );
		}
	}
	else if( !Q_stricmp( action, "cancel" ) )
	{
		if( autorecording )
		{
			trap_Cmd_ExecuteText( EXEC_NOW, "stop cancel silent" );
			autorecording = qfalse;
		}
	}
	else if( !Q_stricmp( action, "stats" ) )
	{
		CG_SC_PrintPlayerStats( trap_Cmd_Argv( 2 ), CG_Printf );

		if( cg_autoaction_stats->integer && ( !spectator || cg_autoaction_spectator->integer ) )
		{
			const char *filename = 
				va( "stats/%s/%s.txt", GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ), name );

			if( trap_FS_FOpenFile( filename, &cg_statsFileHandle, FS_APPEND ) == -1 )
			{
				CG_Printf( "Couldn't write autorecorded stats, error opening file %s\n", filename );
				return;
			}

			CG_SC_PrintPlayerStats( trap_Cmd_Argv( 2 ), CG_SC_PrintStatsToFile );

			trap_FS_FCloseFile( cg_statsFileHandle );
		}
	}
	else if( !Q_stricmp( action, "altstart" ) )
	{
		if( cg_autoaction_demo->integer && ( !spectator || cg_autoaction_spectator->integer ) )
		{
			trap_Cmd_ExecuteText( EXEC_NOW, va( "record autorecord/%s/%s silent",
			                                    GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ), name ) );
			autorecording = qtrue;
		}
	}
	else if( developer->integer )
	{
		CG_Printf( "CG_SC_AutoRecord: Unknown argument: %s\n", action );
	}
}

//=======================================================

//==================
//CG_SC_ChannelAdd
//==================
static void CG_SC_ChannelAdd( void )
{
	int i, id;
	char menuparms[MAX_STRING_CHARS];
	char *name, *address;

	for( i = 1; i+2 < trap_Cmd_Argc(); i += 3 )
	{
		id = atoi( trap_Cmd_Argv( i ) );
		name = trap_Cmd_Argv( i+1 );
		address = trap_Cmd_Argv( i+2 );
		if( id <= 0 || !name[0] || !address[0] )
			continue;

		Q_snprintfz( menuparms, sizeof( menuparms ), "menu_tv_channel_add %i \"%s\" \"%s\"\n", id, name, address );
		trap_Cmd_ExecuteText( EXEC_APPEND, menuparms );
	}
}

//==================
//CG_SC_ChannelRemove
//==================
static void CG_SC_ChannelRemove( void )
{
	int i, id;

	for( i = 1; i < trap_Cmd_Argc(); i++ )
	{
		id = atoi( trap_Cmd_Argv( i ) );
		if( id <= 0 )
			continue;
		trap_Cmd_ExecuteText( EXEC_APPEND, va( "menu_tv_channel_remove %i\n", id ) );
	}
}

//=======================================================

static void CG_SC_MatchMessage( void )
{
	matchmessage_t mm;

	cg.matchmessage = NULL;

	mm = atoi( trap_Cmd_Argv( 1 ) );
	cg.matchmessage = GS_MatchMessageString( mm );
	if( !cg.matchmessage[0] )
		cg.matchmessage = NULL;
}

static void CG_CS_UpdateTeamInfo( void )
{
	char *ti;

	if( cg.teaminfo )
		CG_Free( cg.teaminfo );
	cg.teaminfo = NULL;

	ti = trap_Cmd_Argv( 1 );
	if( ti[0] )
		cg.teaminfo = CG_CopyString( ti );
}

//=======================================================

static qboolean demo_requested = qfalse;

void CG_Cmd_DemoGet_f( void )
{
	if( demo_requested )
	{
		CG_Printf( "Already requesting a demo\n" );
		return;
	}

	if( trap_Cmd_Argc() != 2 || ( atoi( trap_Cmd_Argv( 1 ) ) <= 0 && trap_Cmd_Argv( 1 )[0] != '.' ) )
	{
		CG_Printf( "Usage: demoget <number>\n" );
		CG_Printf( "Donwloads a demo from the server\n" );
		CG_Printf( "Use the demolist command to see list of demos on the server\n" );
		return;
	}

	trap_Cmd_ExecuteText( EXEC_NOW, va( "cmd demoget %s", trap_Cmd_Argv( 1 ) ) );

	demo_requested = qtrue;
}

static void CG_SC_DemoGet( void )
{
	if( cgs.demoPlaying )
	{
		// ignore download commands coming from demo files
		return;
	}

	if( !demo_requested )
	{
		CG_Printf( "Warning: demoget when not requested, ignored\n" );
		return;
	}

	demo_requested = qfalse;

	if( trap_Cmd_Argc() < 2 )
	{
		CG_Printf( "No such demo found\n" );
		return;
	}

	if( !COM_ValidateRelativeFilename( trap_Cmd_Argv( 1 ) ) )
	{
		CG_Printf( "Warning: demoget: Invalid filename, ignored\n" );
		return;
	}

	trap_DownloadRequest( va( "demos/server/%s.wd%i", trap_Cmd_Argv( 1 ), cgs.gameProtocol ), qfalse );
}


//==================
//CG_SC_MOTD
//==================
static void CG_SC_MOTD( void )
{
	char *motd;

	if( cg.motd )
		CG_Free( cg.motd );
	cg.motd = NULL;

	motd = trap_Cmd_Argv( 2 );
	if( !motd[0] )
		return;

	if( !strcmp( trap_Cmd_Argv( 1 ), "1" ) )
	{
		cg.motd = CG_CopyString( motd );
		cg.motd_time = cg.time + 50 *strlen( motd );
		if( cg.motd_time < cg.time + 5000 )
			cg.motd_time = cg.time + 5000;
	}

	CG_Printf( "\nMessage of the Day:\n%s", motd );
}

//=======================================================

//==================
//CG_SC_MenuCA
//==================
static void CG_SC_MenuCA( void )
{
	trap_Cmd_ExecuteText( EXEC_APPEND, "menu_ca\n" );
}

//=======================================================

typedef struct
{
	char *name;
	void ( *func )( void );
} svcmd_t;

svcmd_t cg_svcmds[] =
{
	{ "pr", CG_SC_Print },
	{ "ch", CG_SC_ChatPrint },
	{ "tvch", CG_SC_TVChatPrint },
	{ "cp", CG_SC_CenterPrint },
	{ "obry", CG_SC_Obituary },
	{ "scb", CG_SC_Scoreboard }, // wsw : jal : scoreboard templates
	{ "plstats", CG_SC_PlayerStats },
	{ "autr", CG_SC_AutoRecord }, // wsw : jal : autorecord command
	{ "mm", CG_SC_MatchMessage },
	{ "ti", CG_CS_UpdateTeamInfo },
	{ "demoget", CG_SC_DemoGet },
	{ "aw", CG_SC_Award },
	{ "cha", CG_SC_ChannelAdd },
	{ "chr", CG_SC_ChannelRemove },
	{ "mnca", CG_SC_MenuCA },
	{ "motd", CG_SC_MOTD },

	{ NULL }
};

/*
   ==================
   CG_GameCommand
   ==================
 */
void CG_GameCommand( char *command )
{
	char *s;
	svcmd_t *cmd;

	trap_Cmd_TokenizeString( command );

	s = trap_Cmd_Argv( 0 );
	for( cmd = cg_svcmds; cmd->name; cmd++ )
	{
		if( !strcmp( s, cmd->name ) )
		{
			cmd->func();
			return;
		}
	}

	CG_Printf( "Unknown game command: %s\n", s );
}
