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

//=============
//SV_WriteDeltaMatchStateToClient
//=============
void SV_WriteDeltaMatchStateToClient( client_frame_t *from, client_frame_t *to, msg_t *msg )
{
	match_state_t *ms, *oms;
	match_state_t dummy;
	int bitmask;

	ms = &to->matchstate;
	if( !from )
	{
		memset( &dummy, 0, sizeof( dummy ) );
		oms = &dummy;
	}
	else
		oms = &from->matchstate;

	bitmask = 0;

	if( oms->state != ms->state )
		bitmask |= MATCHSTATE_FLAG_STATE;

	if( oms->roundstate != ms->roundstate )
		bitmask |= MATCHSTATE_FLAG_ROUNDSTATE;

	if( oms->timelimit != ms->timelimit || oms->extendedtime != ms->extendedtime )
		bitmask |= MATCHSTATE_FLAG_TIMELIMIT;

	if( oms->clock_msecs != ms->clock_msecs )
		bitmask |= MATCHSTATE_FLAG_CLOCK_MSECS;

	if( oms->clock_secs != ms->clock_secs )
		bitmask |= MATCHSTATE_FLAG_CLOCK_SECS;

	if( oms->clock_mins != ms->clock_mins )
		bitmask |= MATCHSTATE_FLAG_CLOCK_MINUTES;


	// write it
	MSG_WriteByte( msg, svc_match );
	MSG_WriteByte( msg, bitmask );

	if( bitmask & MATCHSTATE_FLAG_STATE )
		MSG_WriteByte( msg, (qbyte)ms->state );

	if( bitmask & MATCHSTATE_FLAG_ROUNDSTATE )
		MSG_WriteByte( msg, (qbyte)ms->roundstate );

	if( bitmask & MATCHSTATE_FLAG_TIMELIMIT )
	{
		int timelimitmask = ms->timelimit;
		if( ms->extendedtime )
			timelimitmask |= MATCHSTATE_EXTENDEDTIME_BIT;
		MSG_WriteLong( msg, timelimitmask );
	}

	if( bitmask & MATCHSTATE_FLAG_CLOCK_MSECS )
		MSG_WriteByte( msg, (qbyte)( ms->clock_msecs * 0.1 ) );

	if( bitmask & MATCHSTATE_FLAG_CLOCK_SECS )
		MSG_WriteByte( msg, ms->clock_secs );

	if( bitmask & MATCHSTATE_FLAG_CLOCK_MINUTES )
		MSG_WriteShort( msg, ms->clock_mins );
}

//=============
//SV_WritePlayerstateToClient
//=============
void SV_WritePlayerstateToClient( player_state_t *ops, player_state_t *ps, msg_t *msg )
{
	int i;
	int pflags;
	player_state_t dummy;
	int statbits;

	if( !ops )
	{
		memset( &dummy, 0, sizeof( dummy ) );
		ops = &dummy;
	}

	//
	// determine what needs to be sent
	//
	pflags = 0;

	if( ps->pmove.pm_type != ops->pmove.pm_type )
		pflags |= PS_M_TYPE;

	if( ps->pmove.origin[0] != ops->pmove.origin[0] )
		pflags |= PS_M_ORIGIN0;
	if( ps->pmove.origin[1] != ops->pmove.origin[1] )
		pflags |= PS_M_ORIGIN1;
	if( ps->pmove.origin[2] != ops->pmove.origin[2] )
		pflags |= PS_M_ORIGIN2;

	if( ps->pmove.velocity[0] != ops->pmove.velocity[0] )
		pflags |= PS_M_VELOCITY0;
	if( ps->pmove.velocity[1] != ops->pmove.velocity[1] )
		pflags |= PS_M_VELOCITY1;
	if( ps->pmove.velocity[2] != ops->pmove.velocity[2] )
		pflags |= PS_M_VELOCITY2;

	if( ps->pmove.pm_time != ops->pmove.pm_time )
		pflags |= PS_M_TIME;

	if( ps->pmove.pm_flags != ops->pmove.pm_flags )
		pflags |= PS_M_FLAGS;

	if( ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0] )
		pflags |= PS_M_DELTA_ANGLES0;
	if( ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1] )
		pflags |= PS_M_DELTA_ANGLES1;
	if( ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2] )
		pflags |= PS_M_DELTA_ANGLES2;

	if( ps->event != ops->event )
		pflags |= PS_EVENT;

	if( ps->viewangles[0] != ops->viewangles[0]
	    || ps->viewangles[1] != ops->viewangles[1]
	    || ps->viewangles[2] != ops->viewangles[2] )
		pflags |= PS_VIEWANGLES;

	if( ps->pmove.gravity != ops->pmove.gravity )
		pflags |= PS_M_GRAVITY;

	if( ps->fov != ops->fov )
		pflags |= PS_FOV;

	if( ps->POVnum != ops->POVnum )
		pflags |= PS_POVNUM;

	if( ps->viewheight != ops->viewheight )
		pflags |= PS_VIEWHEIGHT;

	for( i = 0; i < PM_STAT_SIZE; i++ )
		if( ps->pmove.stats[i] != ops->pmove.stats[i] )
			pflags |= PS_PMOVESTATS;

	for( i = 0; i < MAX_WEAPLIST_STATS; i++ )
	{
		if( ps->weaponlist[i][0] != ops->weaponlist[i][0] ||
		    ps->weaponlist[i][1] != ops->weaponlist[i][1] ||
		    ps->weaponlist[i][2] != ops->weaponlist[i][2] )
		{
			pflags |= PS_WEAPONLIST;
			break;
		}
	}
	if( ps->plrkeys != ops->plrkeys )
		pflags |= PS_PLRKEYS;

	//
	// write it
	//
	MSG_WriteByte( msg, svc_playerinfo );

	if( pflags & 0xff000000 )
		pflags |= PS_MOREBITS3 | PS_MOREBITS2 | PS_MOREBITS1;
	else if( pflags & 0x00ff0000 )
		pflags |= PS_MOREBITS2 | PS_MOREBITS1;
	else if( pflags & 0x0000ff00 )
		pflags |= PS_MOREBITS1;

	MSG_WriteByte( msg, pflags&255 );

	if( pflags & 0xff000000 )
	{
		MSG_WriteByte( msg, ( pflags>>8 )&255 );
		MSG_WriteByte( msg, ( pflags>>16 )&255 );
		MSG_WriteByte( msg, ( pflags>>24 )&255 );
	}
	else if( pflags & 0x00ff0000 )
	{
		MSG_WriteByte( msg, ( pflags>>8 )&255 );
		MSG_WriteByte( msg, ( pflags>>16 )&255 );
	}
	else if( pflags & 0x0000ff00 )
	{
		MSG_WriteByte( msg, ( pflags>>8 )&255 );
	}

	//
	// write the pmove_state_t
	//
	if( pflags & PS_M_TYPE )
		MSG_WriteByte( msg, ps->pmove.pm_type );

	if( pflags & PS_M_ORIGIN0 )
		MSG_WriteInt3( msg, (int)( ps->pmove.origin[0]*PM_VECTOR_SNAP ) );
	if( pflags & PS_M_ORIGIN1 )
		MSG_WriteInt3( msg, (int)( ps->pmove.origin[1]*PM_VECTOR_SNAP ) );
	if( pflags & PS_M_ORIGIN2 )
		MSG_WriteInt3( msg, (int)( ps->pmove.origin[2]*PM_VECTOR_SNAP ) );

	if( pflags & PS_M_VELOCITY0 )
		MSG_WriteInt3( msg, (int)( ps->pmove.velocity[0]*PM_VECTOR_SNAP ) );
	if( pflags & PS_M_VELOCITY1 )
		MSG_WriteInt3( msg, (int)( ps->pmove.velocity[1]*PM_VECTOR_SNAP ) );
	if( pflags & PS_M_VELOCITY2 )
		MSG_WriteInt3( msg, (int)( ps->pmove.velocity[2]*PM_VECTOR_SNAP ) );

	if( pflags & PS_M_TIME )
		MSG_WriteByte( msg, ps->pmove.pm_time );

	if( pflags & PS_M_FLAGS )
		MSG_WriteShort( msg, ps->pmove.pm_flags );

	if( pflags & PS_M_DELTA_ANGLES0 )
		MSG_WriteShort( msg, ps->pmove.delta_angles[0] );
	if( pflags & PS_M_DELTA_ANGLES1 )
		MSG_WriteShort( msg, ps->pmove.delta_angles[1] );
	if( pflags & PS_M_DELTA_ANGLES2 )
		MSG_WriteShort( msg, ps->pmove.delta_angles[2] );

	if( pflags & PS_EVENT )
		MSG_WriteShort( msg, ps->event );

	if( pflags & PS_VIEWANGLES )
	{
		MSG_WriteAngle16( msg, ps->viewangles[0] );
		MSG_WriteAngle16( msg, ps->viewangles[1] );
		MSG_WriteAngle16( msg, ps->viewangles[2] );
	}

	if( pflags & PS_M_GRAVITY )
		MSG_WriteShort( msg, ps->pmove.gravity );

	if( pflags & PS_FOV )
		MSG_WriteByte( msg, (qbyte)ps->fov );

	if( pflags & PS_POVNUM )
		MSG_WriteByte( msg, (qbyte)ps->POVnum );

	if( pflags & PS_VIEWHEIGHT )
		MSG_WriteChar( msg, (char)ps->viewheight );

	if( pflags & PS_PMOVESTATS )
	{
		for( i = 0; i < PM_STAT_SIZE; i++ )
		{
			MSG_WriteShort( msg, ps->pmove.stats[i] );
		}
	}

	if( pflags & PS_WEAPONLIST )
	{
		// send weaponlist stats
		statbits = 0;
		for( i = 0; i < MAX_WEAPLIST_STATS; i++ )
		{
			if( ps->weaponlist[i][0] != ops->weaponlist[i][0] ||
			    ps->weaponlist[i][1] != ops->weaponlist[i][1] ||
			    ps->weaponlist[i][2] != ops->weaponlist[i][2] )
				statbits |= ( 1<<i );
		}

		MSG_WriteShort( msg, statbits );
		for( i = 0; i < MAX_WEAPLIST_STATS; i++ )
		{
			if( statbits & ( 1<<i ) )
			{
				MSG_WriteByte( msg, (qbyte)ps->weaponlist[i][0] );
				MSG_WriteByte( msg, (qbyte)ps->weaponlist[i][1] );
				MSG_WriteByte( msg, (qbyte)ps->weaponlist[i][2] );
			}
		}
	}

	if( pflags & PS_PLRKEYS )
		MSG_WriteByte( msg, ps->plrkeys );

	// send stats
	statbits = 0;
	for( i = 0; i < PS_MAX_STATS; i++ )
	{
		if( ps->stats[i] != ops->stats[i] )
			statbits |= 1<<i;
	}

	MSG_WriteLong( msg, statbits );
	for( i = 0; i < PS_MAX_STATS; i++ )
	{
		if( statbits & ( 1<<i ) )
			MSG_WriteShort( msg, ps->stats[i] );
	}
}

//=============
//SV_WriteSoundToClient
//=============
void SV_WriteSoundToClient( const sound_t *sound, msg_t *msg )
{
	int v, flags, channel, sendchan;

	channel = sound->channel;
	if( channel & CHAN_NO_PHS_ADD )
		channel &= 7;
	sendchan = ( sound->entnum << 3 ) | ( channel & 7 );

	flags = 0;
	if( sound->volume != DEFAULT_SOUND_PACKET_VOLUME )
		flags |= SND_VOLUME;
	if( sound->attenuation != DEFAULT_SOUND_PACKET_ATTENUATION )
		flags |= SND_ATTENUATION;

	v = Q_rint( sound->pos[0] ); flags |= ( SND_POS0_8|SND_POS0_16 );
	if( v+256/2 >= 0 && v+256/2 < 256 ) flags &= ~SND_POS0_16;else if( v+65536/2 >= 0 && v+65536/2 < 65536 ) flags &= ~SND_POS0_8;

	v = Q_rint( sound->pos[1] ); flags |= ( SND_POS1_8|SND_POS1_16 );
	if( v+256/2 >= 0 && v+256/2 < 256 ) flags &= ~SND_POS1_16;else if( v+65536/2 >= 0 && v+65536/2 < 65536 ) flags &= ~SND_POS1_8;

	v = Q_rint( sound->pos[2] ); flags |= ( SND_POS2_8|SND_POS2_16 );
	if( v+256/2 >= 0 && v+256/2 < 256 ) flags &= ~SND_POS2_16;else if( v+65536/2 >= 0 && v+65536/2 < 65536 ) flags &= ~SND_POS2_8;

	MSG_WriteByte( msg, svc_sound );
	MSG_WriteByte( msg, flags );
	MSG_WriteByte( msg, sound->num );

	// always send the entity number for channel overrides
	MSG_WriteShort( msg, sendchan );

	if( ( flags & ( SND_POS0_8|SND_POS0_16 ) ) == SND_POS0_8 )
		MSG_WriteChar( msg, Q_rint( sound->pos[0] ) );
	else if( ( flags & ( SND_POS0_8|SND_POS0_16 ) ) == SND_POS0_16 )
		MSG_WriteShort( msg, Q_rint( sound->pos[0] ) );
	else
		MSG_WriteInt3( msg, Q_rint( sound->pos[0] ) );

	if( ( flags & ( SND_POS1_8|SND_POS1_16 ) ) == SND_POS1_8 )
		MSG_WriteChar( msg, Q_rint( sound->pos[1] ) );
	else if( ( flags & ( SND_POS1_8|SND_POS1_16 ) ) == SND_POS1_16 )
		MSG_WriteShort( msg, Q_rint( sound->pos[1] ) );
	else
		MSG_WriteInt3( msg, Q_rint( sound->pos[1] ) );

	if( ( flags & ( SND_POS2_8|SND_POS2_16 ) ) == SND_POS2_8 )
		MSG_WriteChar( msg, Q_rint( sound->pos[2] ) );
	else if( ( flags & ( SND_POS2_8|SND_POS2_16 ) ) == SND_POS2_16 )
		MSG_WriteShort( msg, Q_rint( sound->pos[2] ) );
	else
		MSG_WriteInt3( msg, Q_rint( sound->pos[2] ) );

	if( flags & SND_VOLUME )
		MSG_WriteByte( msg, sound->volume * 255 );
	if( flags & SND_ATTENUATION )
		MSG_WriteByte( msg, sound->attenuation );
}
