/*
   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 "tv_local.h"

#include "tv_client.h"

/*
   ==================
   CL_ValidateConfigstring
   ==================
 */
qboolean CL_ValidateConfigstring( char *string )
{
	char *p;
	qboolean opened = qfalse;
	int parity = 0;

	if( !string || !string[0] )
		return qfalse;

	p = string;
	while( *p )
	{
		if( *p == '\"' )
		{
			if( opened )
			{
				parity--;
				opened = qfalse;
			}
			else
			{
				parity++;
				opened = qtrue;
			}
		}
		p++;
	}

	if( parity != 0 )
		return qfalse;

	return qtrue;
}

/*
   ==================
   CL_DeltaEntity

   Parses deltas from the given base and adds the resulting entity
   to the current frame
   ==================
 */
void CL_DeltaEntity( msg_t *msg, frame_t *frame, int newnum, entity_state_t *old, unsigned bits )
{
	entity_state_t *state;

	state = &frame->parsedEntities[frame->numEntities & ( MAX_PARSE_ENTITIES-1 )];
	frame->numEntities++;
	CL_ParseDelta( msg, old, state, newnum, bits );
}

/*
   ===================
   CL_ParsePlayerstate
   ===================
 */
void CL_ParsePlayerstate( msg_t *msg, player_state_t *oldstate, player_state_t *state )
{
	int flags;
	int i, b;
	int statbits;

	// clear to old value before delta parsing
	if( oldstate )
		memcpy( state, oldstate, sizeof( *state ) );
	else
		memset( state, 0, sizeof( *state ) );

	flags = (qbyte)MSG_ReadByte( msg );
	if( flags & PS_MOREBITS1 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		flags |= b<<8;
	}
	if( flags & PS_MOREBITS2 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		flags |= b<<16;
	}
	if( flags & PS_MOREBITS3 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		flags |= b<<24;
	}

	//
	// parse the pmove_state_t
	//
	if( flags & PS_M_TYPE )
		state->pmove.pm_type = (qbyte)MSG_ReadByte( msg );

	if( flags & PS_M_ORIGIN0 )
		state->pmove.origin[0] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );
	if( flags & PS_M_ORIGIN1 )
		state->pmove.origin[1] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );
	if( flags & PS_M_ORIGIN2 )
		state->pmove.origin[2] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );

	if( flags & PS_M_VELOCITY0 )
		state->pmove.velocity[0] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );
	if( flags & PS_M_VELOCITY1 )
		state->pmove.velocity[1] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );
	if( flags & PS_M_VELOCITY2 )
		state->pmove.velocity[2] = ( (float)MSG_ReadInt3( msg )*( 1.0/PM_VECTOR_SNAP ) );

	if( flags & PS_M_TIME )
		state->pmove.pm_time = (qbyte)MSG_ReadByte( msg );

	if( flags & PS_M_FLAGS )
		state->pmove.pm_flags = MSG_ReadShort( msg );

	if( flags & PS_M_DELTA_ANGLES0 )
		state->pmove.delta_angles[0] = MSG_ReadShort( msg );
	if( flags & PS_M_DELTA_ANGLES1 )
		state->pmove.delta_angles[1] = MSG_ReadShort( msg );
	if( flags & PS_M_DELTA_ANGLES2 )
		state->pmove.delta_angles[2] = MSG_ReadShort( msg );

	if( flags & PS_EVENT )
		state->event = MSG_ReadShort( msg );

	if( flags & PS_VIEWANGLES )
	{
		state->viewangles[0] = MSG_ReadAngle16( msg );
		state->viewangles[1] = MSG_ReadAngle16( msg );
		state->viewangles[2] = MSG_ReadAngle16( msg );
	}

	if( flags & PS_M_GRAVITY )
		state->pmove.gravity = MSG_ReadShort( msg );

	if( flags & PS_FOV )
		state->fov = (qbyte)MSG_ReadByte( msg );

	if( flags & PS_POVNUM )
		state->POVnum = (qbyte)MSG_ReadByte( msg );

	if( flags & PS_VIEWHEIGHT )
		state->viewheight = MSG_ReadChar( msg );

	if( flags & PS_PMOVESTATS )
	{
		for( i = 0; i < PM_STAT_SIZE; i++ )
			state->pmove.stats[i] = MSG_ReadShort( msg );
	}

	if( flags & PS_WEAPONLIST )
	{
		// parse weaponlist
		statbits = MSG_ReadShort( msg );
		for( i = 0; i < MAX_WEAPLIST_STATS; i++ )
		{
			if( statbits & ( 1<<i ) )
			{
				state->weaponlist[i][0] = MSG_ReadByte( msg );
				state->weaponlist[i][1] = MSG_ReadByte( msg );
				state->weaponlist[i][2] = MSG_ReadByte( msg );
			}
		}
	}

	if( flags & PS_PLRKEYS )
		state->plrkeys = MSG_ReadByte( msg );

	// parse stats
	statbits = MSG_ReadLong( msg );
	for( i = 0; i < PS_MAX_STATS; i++ )
	{
		if( statbits & ( 1<<i ) )
			state->stats[i] = MSG_ReadShort( msg );
	}
}

/*
   =============
   CL_ParseDeltaMatchState
   =============
 */
void CL_ParseDeltaMatchState( msg_t *msg, frame_t *oldframe, frame_t *newframe )
{
	match_state_t *matchstate;
	int bitmask;

	// start from old state or 0 if none
	matchstate = &newframe->match;
	if( oldframe )
	{
		*matchstate = oldframe->match;
	}
	else
	{
		memset( matchstate, 0, sizeof( match_state_t ) );
	}

	bitmask = MSG_ReadByte( msg );

	if( bitmask & MATCHSTATE_FLAG_STATE )
		matchstate->state = MSG_ReadByte( msg );

	if( bitmask & MATCHSTATE_FLAG_ROUNDSTATE )
		matchstate->roundstate = MSG_ReadByte( msg );

	if( bitmask & MATCHSTATE_FLAG_TIMELIMIT )
	{
		int timelimitmask = MSG_ReadLong( msg );
		if( timelimitmask & MATCHSTATE_EXTENDEDTIME_BIT )
		{
			matchstate->extendedtime = qtrue;
			timelimitmask &= ~MATCHSTATE_EXTENDEDTIME_BIT;
		}
		else
		{
			matchstate->extendedtime = qfalse;
		}
		matchstate->timelimit = timelimitmask;
	}

	if( bitmask & MATCHSTATE_FLAG_CLOCK_MSECS )
		matchstate->clock_msecs = MSG_ReadByte( msg );

	if( bitmask & MATCHSTATE_FLAG_CLOCK_SECS )
		matchstate->clock_secs = MSG_ReadByte( msg );

	if( bitmask & MATCHSTATE_FLAG_CLOCK_MINUTES )
		matchstate->clock_mins = MSG_ReadShort( msg );
}

/*
   =================
   CL_ParseEntityBits

   Returns the entity number and the header bits
   =================
 */
int CL_ParseEntityBits( msg_t *msg, unsigned *bits )
{
	unsigned b, total;
	int number;

	total = (qbyte)MSG_ReadByte( msg );
	if( total & U_MOREBITS1 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		total |= ( b<<8 )&0x0000FF00;
	}
	if( total & U_MOREBITS2 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		total |= ( b<<16 )&0x00FF0000;
	}
	if( total & U_MOREBITS3 )
	{
		b = (qbyte)MSG_ReadByte( msg );
		total |= ( b<<24 )&0xFF000000;
	}

	if( total & U_NUMBER16 )
		number = MSG_ReadShort( msg );
	else
		number = (qbyte)MSG_ReadByte( msg );

	*bits = total;

	return number;
}

/*
   ==================
   CL_ParseDelta

   Can go from either a baseline or a previous packet_entity
   ==================
 */
void CL_ParseDelta( msg_t *msg, entity_state_t *from, entity_state_t *to, int number, unsigned bits )
{
	// set everything to the state we are delta'ing from
	*to = *from;

	to->number = number;

	if( bits & U_TYPE )
	{
		qbyte ttype;
		ttype = (qbyte)MSG_ReadByte( msg );
		to->type = ttype & 127;
		to->linearProjectile = ( ttype & EV_INVERSE );
	}

	if( bits & U_SOLID )
		to->solid = MSG_ReadShort( msg );

	if( bits & U_MODEL )
		to->modelindex = (qbyte)MSG_ReadByte( msg );
	if( bits & U_MODEL2 )
		to->modelindex2 = (qbyte)MSG_ReadByte( msg );

	if( bits & U_FRAME8 )
		to->frame = (qbyte)MSG_ReadByte( msg );
	if( bits & U_FRAME16 )
		to->frame = MSG_ReadShort( msg );

	if( ( bits & U_SKIN8 ) && ( bits & U_SKIN16 ) )  //used for laser colors
		to->skinnum = MSG_ReadLong( msg );
	else if( bits & U_SKIN8 )
		to->skinnum = MSG_ReadByte( msg );
	else if( bits & U_SKIN16 )
		to->skinnum = MSG_ReadShort( msg );

	if( ( bits & ( U_EFFECTS8|U_EFFECTS16 ) ) == ( U_EFFECTS8|U_EFFECTS16 ) )
		to->effects = MSG_ReadLong( msg );
	else if( bits & U_EFFECTS8 )
		to->effects = (qbyte)MSG_ReadByte( msg );
	else if( bits & U_EFFECTS16 )
		to->effects = MSG_ReadShort( msg );

	if( ( bits & ( U_RENDERFX8|U_RENDERFX16 ) ) == ( U_RENDERFX8|U_RENDERFX16 ) )
		to->renderfx = MSG_ReadLong( msg );
	else if( bits & U_RENDERFX8 )
		to->renderfx = (qbyte)MSG_ReadByte( msg );
	else if( bits & U_RENDERFX16 )
		to->renderfx = MSG_ReadShort( msg );

	if( to->linearProjectile )
	{
		if( bits & U_ORIGIN1 )
			to->linearProjectileVelocity[0] = MSG_ReadCoord( msg );
		if( bits & U_ORIGIN2 )
			to->linearProjectileVelocity[1] = MSG_ReadCoord( msg );
		if( bits & U_ORIGIN3 )
			to->linearProjectileVelocity[2] = MSG_ReadCoord( msg );
	}
	else
	{
		if( bits & U_ORIGIN1 )
			to->origin[0] = MSG_ReadCoord( msg );
		if( bits & U_ORIGIN2 )
			to->origin[1] = MSG_ReadCoord( msg );
		if( bits & U_ORIGIN3 )
			to->origin[2] = MSG_ReadCoord( msg );
	}

	if( ( bits & U_ANGLE1 ) && ( to->solid == SOLID_BMODEL ) )
		to->angles[0] = MSG_ReadAngle16( msg );
	else if( bits & U_ANGLE1 )
		to->angles[0] = MSG_ReadAngle( msg );

	if( ( bits & U_ANGLE2 ) && ( to->solid == SOLID_BMODEL ) )
		to->angles[1] = MSG_ReadAngle16( msg );
	else if( bits & U_ANGLE2 )
		to->angles[1] = MSG_ReadAngle( msg );

	if( ( bits & U_ANGLE3 ) && ( to->solid == SOLID_BMODEL ) )
		to->angles[2] = MSG_ReadAngle16( msg );
	else if( bits & U_ANGLE3 )
		to->angles[2] = MSG_ReadAngle( msg );

	if( bits & U_OTHERORIGIN )
		MSG_ReadPos( msg, to->old_origin );

	if( bits & U_SOUND )
		to->sound = (qbyte)MSG_ReadByte( msg );

	if( bits & U_EVENT )
	{
		int event;
		event = (qbyte)MSG_ReadByte( msg );
		if( event & EV_INVERSE )
		{
			to->eventParms[0] = (qbyte)MSG_ReadByte( msg );
		}
		else
		{
			to->eventParms[0] = 0;
		}
		to->events[0] = ( event & ~EV_INVERSE );
	}
	else
	{
		to->events[0] = 0;
		to->eventParms[0] = 0;
	}

	if( bits & U_EVENT2 )
	{
		int event;
		event = (qbyte)MSG_ReadByte( msg );
		if( event & EV_INVERSE )
		{
			to->eventParms[1] = (qbyte)MSG_ReadByte( msg );
		}
		else
		{
			to->eventParms[1] = 0;
		}
		to->events[1] = ( event & ~EV_INVERSE );
	}
	else
	{
		to->events[1] = 0;
		to->eventParms[1] = 0;
	}

	if( bits & U_WEAPON )
	{
		qbyte tweapon;
		tweapon = (qbyte)MSG_ReadByte( msg );
		to->weapon = tweapon & 127;
		to->teleported = ( tweapon & EV_INVERSE );
	}

	if( bits & U_SVFLAGS )
		to->svflags = MSG_ReadByte( msg );

	if( bits & U_LIGHT )
	{
		if( to->linearProjectile )
			to->linearProjectileTimeStamp = (unsigned int)MSG_ReadLong( msg );
		else
			to->light = MSG_ReadLong( msg );
	}

	if( bits & U_TEAM )
		to->team = (qbyte)MSG_ReadByte( msg );
}
