/*
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.

*/
// cl_demo.c  -- demo recording

#include "client.h"



/*
====================
CL_WriteDemoMessage

Dumps the current net message, prefixed by the length
====================
*/
void CL_WriteDemoMessage( msg_t *msg )
{
	int		len, swlen;

	if( cls.demofile <= 0 ) {
		cls.demorecording = qfalse;
		return;
	}

	// the first eight bytes are just packet sequencing stuff
	len = msg->cursize-8;
	swlen = LittleLong( len );

	// skip bad packets
	if( swlen ) {
		FS_Write( &swlen, 4, cls.demofile );
		FS_Write( msg->data+8, len, cls.demofile );
	}
}

/*
====================
CL_Stop_f

stop recording a demo
====================
*/
void CL_Stop_f( void )
{
	int		len;
	qboolean	silent = qfalse;
	qboolean    cancel=qfalse;
	int arg;
	char	name[MAX_OSPATH];

	// look through all the args
	for(arg=1;arg<Cmd_Argc();arg++)
	{
		if(!Q_stricmp(Cmd_Argv(arg), "silent") )
			silent = qtrue;
		else if(!Q_stricmp(Cmd_Argv(arg), "cancel") )
			cancel = qtrue;
	}

	if( !cls.demorecording )
	{
		if( !silent )
			Com_Printf( "Not recording a demo.\n" );
		return;
	}

	// finish up
	len = -1;
	FS_Write( &len, 4, cls.demofile );
	FS_FCloseFile( cls.demofile );

	// cancel the demos
	if(cancel)
	{
		// remove the file that correspond to cls.demofile
		Q_snprintfz(name, sizeof(name),"%s/demos/%s.wd%d", FS_Gamedir(), cls.demofilename, PROTOCOL_VERSION );
		if( !silent )
			Com_Printf("Canceling demos: %s\n", name );
		FS_RemoveFile( name );
	}

	cls.demofile = 0; // file id
	memset(cls.demofilename, 0, sizeof(cls.demofilename)); // file name
	cls.demorecording = qfalse;
	if( !silent )
		Com_Printf( "Stopped demo.\n" );

}

/*
====================
CL_Record_f

record <demoname>

Begins recording a demo from the current position
====================
*/
void CL_Record_f (void)
{
	int		i;
	char	name[MAX_OSPATH];
	char	buf_data[MAX_MSGLEN];
	msg_t	buf;
	int		len;
	entity_state_t	*ent;
	entity_state_t	nullstate;
	int			length;
	qboolean	silent = qfalse;

	if( cls.state != CA_ACTIVE ) {
		Com_Printf( "You must be in a level to record.\n" );
		return;
	}

	if( Cmd_Argc() < 2 ) {
		Com_Printf( "record <demoname>\n" );
		return;
	}
	
	if( Cmd_Argc() > 2 && !Q_stricmp(Cmd_Argv(2), "silent") ) {
		silent = qtrue;
	}

	if( cls.demoplaying ) {
		if( !silent )
			Com_Printf( "You can't record from another demo.\n" );
		return;
	}

	if( cls.demorecording ) {
		if( !silent )
			Com_Printf( "Already recording.\n" );
		return;
	}

	//
	// open the demo file
	//
	Q_snprintfz( name, sizeof(name), "demos/%s", Cmd_Argv(1) );
	COM_DefaultExtension( name, va(".wd%d",PROTOCOL_VERSION) , sizeof(name) );

	length = FS_FOpenFile( name, &cls.demofile, FS_WRITE );
	if( length == -1 ) {
		Com_Printf( "ERROR: couldn't create the demo file.\n" );
		cls.demofile = 0;
		return;
	}

	// store the name in case we need it later
	Q_snprintfz( cls.demofilename, sizeof(cls.demofilename), Cmd_Argv(1) );

	if( !silent )
		Com_Printf( "recording to %s.\n", name );
	cls.demorecording = qtrue;

	// don't start saving messages until a non-delta compressed message is received
	cls.demowaiting = qtrue;

	//
	// write out messages to hold the startup information
	//
	MSG_Init( &buf, (qbyte *)buf_data, sizeof(buf_data) );

	// send the serverdata
	MSG_WriteByte( &buf, svc_serverdata );
	MSG_WriteLong( &buf, PROTOCOL_VERSION );
	MSG_WriteLong( &buf, 0x10000 + cl.servercount );
	MSG_WriteString( &buf, cl.gamedir );
	MSG_WriteShort( &buf, cl.playernum );
	MSG_WriteString( &buf, cl.servermessage );
	MSG_WriteByte( &buf, 0 );	// no sv_bitflags (no battleye)
	MSG_WriteShort( &buf, -1 ); // demos don't request configstrings to the server

	len = LittleLong( buf.cursize );
	FS_Write( &len, 4, cls.demofile );
	FS_Write( buf.data, buf.cursize, cls.demofile );
	buf.cursize = 0;

	// configstrings
	for( i = 0; i < MAX_CONFIGSTRINGS; i++ )
	{
		if( cl.configstrings[i][0] )
		{
			MSG_WriteByte( &buf, svc_servercs );
			//MSG_WriteByte( &buf, svc_servercmd );
			MSG_WriteString( &buf, va("cs %i \"%s\"", i, cl.configstrings[i]) );

			if( buf.cursize > buf.maxsize / 2 )
			{	// write it out
				len = LittleLong( buf.cursize );
				FS_Write( &len, 4, cls.demofile );
				FS_Write( buf.data, buf.cursize, cls.demofile );
				buf.cursize = 0;
			}
		}
	}

	// baselines
	memset( &nullstate, 0, sizeof(nullstate) );
	for( i = 0; i < MAX_EDICTS; i++ )
	{
		ent = &cl_baselines[i];
		if( !ent->modelindex && !ent->sound && !ent->effects )
			continue;

		MSG_WriteByte( &buf, svc_spawnbaseline );		
		MSG_WriteDeltaEntity( &nullstate, &cl_baselines[i], &buf, qtrue, qtrue );

		if( buf.cursize > buf.maxsize / 2 )
		{	// write it out
			len = LittleLong( buf.cursize );
			FS_Write( &len, 4, cls.demofile );
			FS_Write( buf.data, buf.cursize, cls.demofile );
			buf.cursize = 0;
		}
	}

	MSG_WriteByte( &buf, svc_servercs );
	//MSG_WriteByte( &buf, svc_servercmd );
	MSG_WriteString( &buf, "precache" );

	// write it to the demo file
	len = LittleLong( buf.cursize );
	FS_Write( &len, 4, cls.demofile );
	FS_Write( buf.data, buf.cursize, cls.demofile );

	// the rest of the demo file will be individual frames
}


//================================================================
//
//	WARSOW : CLIENT SIDE DEMO PLAYBACK
//
//================================================================


// demo file
char	demoName[MAX_QPATH];
int		demofilehandle;
int		demofilelen;

// demo message
qbyte		msgbuf[MAX_MSGLEN];
msg_t		demomsg;

#ifdef DEMOCAM // -- PLX
// A boolean to test if demo is paused -- PLX
qboolean	demopause = qfalse;
#endif

/*
=================
CL_BeginDemoAviDump
=================
*/
void CL_BeginDemoAviDump( void ) {
	cls.demoavi = qtrue;
	cls.demoavi_frame = 0;
	R_BeginAviDemo ();
}

/*
=================
CL_StopDemoAviDump
=================
*/
void CL_StopDemoAviDump( void ) {
	cls.demoavi = qfalse;
	cls.demoavi_frame = 0;
	R_StopAviDemo ();
}

/*
=================
CL_DemoCompleted

Close the demo file and disable demo state. Called from disconnection proccess
=================
*/
void CL_DemoCompleted( void ) {

	if( cls.demoavi )
		CL_StopDemoAviDump();

	if( demofilehandle )
	{
		FS_FCloseFile( demofilehandle );
		demofilehandle = 0;
		demofilelen = 0;
	}

	cls.demoplaying = qfalse;

	Com_SetDemoPlaying( qfalse );

	Com_Printf( "Demo completed\n" );

	if( Cvar_VariableValue("sv_demo_and_quit") )
		Cbuf_ExecuteText( EXEC_NOW, "quit" );
}

/*
=================
CL_ReadDemoMessage

Read a packet from the demo file and send it to the messages parser
=================
*/
void CL_ReadDemoMessage( void ) {

	int	msglen;

	if ( !demofilehandle ) {
		CL_Disconnect( NULL );
		return;
	}

	if ( demofilelen <= 0 ) {
		CL_Disconnect( NULL );
		return;
	}

	msglen = 0;

	// get the next message
	FS_Read( &msglen, 4, demofilehandle );
	demofilelen -= 4;
	
	msglen = LittleLong( msglen );
	if( msglen == -1 )
	{
		CL_Disconnect( NULL );
		return;
	}

	if( msglen > MAX_MSGLEN )
		Com_Error( ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN" );
	
	if( demofilelen <= 0 )
	{
		CL_Disconnect( NULL );
		return;
	}

	FS_Read( msgbuf, msglen, demofilehandle );
	demofilelen -= msglen;

	demomsg.allowoverflow = qtrue;
	demomsg.maxsize = sizeof(msgbuf);
	demomsg.data = msgbuf;
	demomsg.cursize = msglen;
	demomsg.readcount = 0;

	CL_ParseServerMessage( &demomsg );
}

/*
=================
CL_ReadDemoPackets

See if it's time to read a new demo packet
=================
*/
void CL_ReadDemoPackets( void ) {

	cls.demoplay_lastsnaptime -= cls.demoplay_framemsecs;
	cls.demoplay_framemsecs = 0;
	if( cls.demoplay_lastsnaptime > 0 )
		return;

#ifdef DEMOCAM // -- PLX
	if (!demopause) CL_ReadDemoMessage();
#else
	CL_ReadDemoMessage();
#endif

}

/*
====================
CL_StartDemo_f
====================
*/
void CL_StartDemo_f( char *arg ) 
{
	char		name[MAX_OSPATH];
	int			i;

	if( !arg || !arg[0] ) {
		Com_Printf( "demo <demoname>\n" );
		return;
	}
	// have to copy the argument now, since next actions will lose it
	Q_snprintfz( name, sizeof(name), "demos/%s", arg );

	// make sure a local server is killed
	Cbuf_ExecuteText( EXEC_NOW, "killserver\n" );
	CL_Disconnect( NULL );

	// open the demo file
	COM_DefaultExtension( name, va(".wd%d",PROTOCOL_VERSION), sizeof(name) );
	demofilelen = FS_FOpenFile( name, &demofilehandle, FS_READ );
	if( !demofilehandle || demofilelen < 1 ) {
		Com_Printf( "No valid demo file found\n" );
		FS_FCloseFile( demofilehandle );
		return;
	}

	Q_strncpyz( demoName, Cmd_Argv(1), sizeof( demoName ) );
	Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );

	CL_SetClientState( CA_CONNECTING );
	Com_SetDemoPlaying( qtrue );
	cls.demoplaying = qtrue;
	while( cls.state < CA_ACTIVE )
		CL_ReadDemoMessage(); // read first messages

	cls.demoplay_lastsnaptime = 0;
	cls.demoplay_framemsecs = 0;

	// set up for timedemo settings
	cl.timedemo_start = 0;
	cl.timedemo_frames = 0;
	for( i = 0; i < 100; i++ )
		cl.timedemo_counts[i] = 0;
}

/*
====================
CL_PlayDemo_f

demo <demoname>
====================
*/
void CL_PlayDemo_f( void ) 
{
	if( Cmd_Argc() != 2 ) {
		Com_Printf( "demo <demoname>\n" );
		return;
	}
	CL_StartDemo_f( Cmd_Argv(1) );
}

#ifdef DEMOCAM
/*
====================
CL_PauseDemo_f				Can be used in democam -- PLX

demo <demoname>
====================
*/
void CL_PauseDemo_f( void ) 
{
	demopause = !demopause;
}
#endif

/*
====================
CL_PlayDemoToAvi_f

demoavi <demoname> (if no name suplied, toogles demoavi status)
====================
*/
void CL_PlayDemoToAvi_f( void ) 
{
	if( Cmd_Argc() != 2 ) {
		if( !cls.demoplaying ) {
			Com_Printf( "Usage: %sdemoavi <demoname>%s or %sdemoavi%s while playing a demo\n",
				S_COLOR_YELLOW, S_COLOR_WHITE, S_COLOR_YELLOW, S_COLOR_WHITE );
			return;
		}

		if( Cmd_Argc() < 2 ) {	// toogle demoavi mode
			if( !cls.demoavi ) {
				CL_BeginDemoAviDump();
			} else {
				CL_StopDemoAviDump();
			}
		}
		return;
	}

	if( cls.demoplaying ) {
		CL_Disconnect( NULL );
	}

	CL_StartDemo_f( Cmd_Argv(1) );
	CL_BeginDemoAviDump();
}

//[end]







