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

*/
// msg.c -- Message IO functions
#include "qcommon.h"

/*
==============================================================================

			MESSAGE IO FUNCTIONS

Handles byte ordering and avoids alignment errors
==============================================================================
*/


void MSG_Init( msg_t *buf, qbyte *data, int length )
{
	memset( buf, 0, sizeof(*buf) );
	buf->data = data;
	buf->maxsize = length;
	buf->compressed = qfalse;
}

void MSG_Clear( msg_t *buf )
{
	buf->cursize = 0;
	buf->overflowed = qfalse;
	buf->compressed = qfalse;
}

void *MSG_GetSpace( msg_t *buf, int length )
{
	void	*data;

	if( buf->cursize + length > buf->maxsize )
	{
		if( !buf->allowoverflow )
			Com_Error( ERR_FATAL, "MSG_GetSpace: overflow without allowoverflow set" );

		if( length > buf->maxsize )
			Com_Error( ERR_FATAL, "MSG_GetSpace: %i is > full buffer size", length );

		Com_Printf( "MSG_GetSpace: overflow\n" );
		MSG_Clear( buf ); 
		buf->overflowed = qtrue;
	}

	data = buf->data + buf->cursize;
	buf->cursize += length;

	return data;
}

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

void MSG_Print( msg_t *buf, char *data )
{
	size_t		len;

	len = strlen(data)+1;

	if( buf->cursize )
	{
		if( buf->data[buf->cursize-1] )
			memcpy( (qbyte *)MSG_GetSpace(buf, (int)len), data, len ); // no trailing 0
		else
		{
			qbyte *dest = (qbyte *)MSG_GetSpace( buf, (int)len-1 );
			if( !buf->overflowed )
				memcpy( dest-1, data, len ); // write over trailing 0
		}
	}
	else
		memcpy( (qbyte *)MSG_GetSpace(buf, (int)len), data, len );
}

//
// writing functions
//

void MSG_Write( msg_t *buf, void *data, int length )
{
	memcpy( MSG_GetSpace(buf, length), data, length );		
}

void MSG_WriteChar( msg_t *sb, int c )
{
	qbyte	*buf;

#ifdef PARANOID
	if( c < -128 || c > 127 )
		Com_Error( ERR_FATAL, "MSG_WriteChar: range error" );
#endif

	buf = MSG_GetSpace( sb, 1 );
	buf[0] = c;
}

void MSG_WriteByte( msg_t *sb, int c )
{
	qbyte	*buf;

#ifdef PARANOID
	if( c < 0 || c > 255 )
		Com_Error( ERR_FATAL, "MSG_WriteByte: range error" );
#endif

	buf = MSG_GetSpace( sb, 1 );
	buf[0] = c;
}

void MSG_WriteShort( msg_t *sb, int c )
{
	qbyte	*buf;

#ifdef PARANOID
	if( c < ((short)0x8000) || c > (short)0x7fff )
		Com_Error( ERR_FATAL, "MSG_WriteShort: range error" );
#endif

	buf = MSG_GetSpace( sb, 2 );
	buf[0] = c&0xff;
	buf[1] = (c>>8)&0xff;
}

void MSG_WriteLong( msg_t *sb, int c )
{
	qbyte	*buf;

	buf = MSG_GetSpace( sb, 4 );
	buf[0] = c&0xff;
	buf[1] = (c>>8)&0xff;
	buf[2] = (c>>16)&0xff;
	buf[3] = c>>24;
}

void MSG_WriteFloat( msg_t *sb, float f )
{
	union
	{
		float	f;
		int	l;
	} dat;

	dat.f = f;
	dat.l = LittleLong( dat.l );

	MSG_Write( sb, &dat.l, 4 );
}

void MSG_WriteString( msg_t *sb, char *s )
{
	if( !s )
		MSG_Write( sb, "", 1 );
	else
		MSG_Write( sb, s, (int)strlen(s)+1 );
}

void MSG_WriteInt3( msg_t *sb, int c )
{
	qbyte	*buf;

	buf = MSG_GetSpace( sb, 3 );
	buf[0] = c&0xff;
	buf[1] = (c>>8)&0xff;
	buf[2] = (c>>16)&0xff;
}

void MSG_WriteDeltaUsercmd( msg_t *buf, usercmd_t *from, usercmd_t *cmd )
{
	int		bits;

	// send the movement message

	bits = 0;
	if( cmd->angles[0] != from->angles[0] )
		bits |= CM_ANGLE1;
	if( cmd->angles[1] != from->angles[1] )
		bits |= CM_ANGLE2;
	if( cmd->angles[2] != from->angles[2] )
		bits |= CM_ANGLE3;
	if( cmd->forwardmove != from->forwardmove )
		bits |= CM_FORWARD;
	if( cmd->sidemove != from->sidemove )
		bits |= CM_SIDE;
	if( cmd->upmove != from->upmove )
		bits |= CM_UP;
	if( cmd->buttons != from->buttons )
		bits |= CM_BUTTONS;

    MSG_WriteByte( buf, bits );

	if( bits & CM_ANGLE1 )
		MSG_WriteShort( buf, cmd->angles[0] );
	if( bits & CM_ANGLE2 )
		MSG_WriteShort( buf, cmd->angles[1] );
	if( bits & CM_ANGLE3 )
		MSG_WriteShort( buf, cmd->angles[2] );
	
	if( bits & CM_FORWARD )
		MSG_WriteShort( buf, cmd->forwardmove );
	if( bits & CM_SIDE )
	  	MSG_WriteShort( buf, cmd->sidemove );
	if( bits & CM_UP )
		MSG_WriteShort( buf, cmd->upmove );

 	if( bits & CM_BUTTONS )
	  	MSG_WriteByte( buf, cmd->buttons );

    MSG_WriteByte( buf, cmd->msec );

	MSG_WriteLong( buf, cmd->serverTimeStamp );

}


void MSG_WriteDir( msg_t *sb, vec3_t dir )
{
	if( !dir )
	{
		MSG_WriteByte( sb, 0 );
		return;
	}

	MSG_WriteByte( sb, DirToByte(dir) );
}

//==================
//MSG_WriteDeltaEntity
//
//Writes part of a packetentities message.
//Can delta from either a baseline or a previous packet_entity
//==================
void MSG_WriteDeltaEntity( entity_state_t *from, entity_state_t *to, msg_t *msg, qboolean force, qboolean newentity )
{
	int		bits;

	if( !to->number )
		Com_Error( ERR_FATAL, "Unset entity number" );
	if( to->number >= MAX_EDICTS )
		Com_Error( ERR_FATAL, "Entity number >= MAX_EDICTS" );

// send an update
	bits = 0;

	if( to->number >= 256 )
		bits |= U_NUMBER16;		// number8 is implicit otherwise

	if( to->origin[0] != from->origin[0] )
		bits |= U_ORIGIN1;
	if( to->origin[1] != from->origin[1] )
		bits |= U_ORIGIN2;
	if( to->origin[2] != from->origin[2] )
		bits |= U_ORIGIN3;

	if( to->angles[0] != from->angles[0] )
		bits |= U_ANGLE1;		
	if( to->angles[1] != from->angles[1] )
		bits |= U_ANGLE2;
	if( to->angles[2] != from->angles[2] )
		bits |= U_ANGLE3;

	if( to->skinnum != from->skinnum )
	{
		if( (unsigned)to->skinnum < 256 )
			bits |= U_SKIN8;
		else if( (unsigned)to->skinnum < 0x10000 )
			bits |= U_SKIN16;
		else
			bits |= (U_SKIN8|U_SKIN16);
	}

	if( to->frame != from->frame )
	{
		if( to->frame < 256 )
			bits |= U_FRAME8;
		else
			bits |= U_FRAME16;
	}

	if( to->effects != from->effects )
	{
		if( to->effects < 256 )
			bits |= U_EFFECTS8;
		else if( to->effects < 0x8000 )
			bits |= U_EFFECTS16;
		else
			bits |= U_EFFECTS8|U_EFFECTS16;
	}

	if( to->renderfx != from->renderfx )
	{
		if( to->renderfx < 256 )
			bits |= U_RENDERFX8;
		else if( to->renderfx < 0x8000 )
			bits |= U_RENDERFX16;
		else
			bits |= U_RENDERFX8|U_RENDERFX16;
	}

	if( to->solid != from->solid )
		bits |= U_SOLID;

	// events are not delta compressed, just 0 compressed
	if( to->events[0] )
		bits |= U_EVENT;
	if( to->events[1] )
		bits |= U_EVENT2;

	if( to->modelindex != from->modelindex )
		bits |= U_MODEL;
	if( to->modelindex2 != from->modelindex2 )
		bits |= U_MODEL2;

	if( (to->type != from->type) || (to->takedamage != from->takedamage) )
		bits |= U_TYPE;

	if( to->sound != from->sound )
		bits |= U_SOUND;

	if( newentity )
		bits |= U_OLDORIGIN;

	if( to->weapon != from->weapon )
		bits |= U_WEAPON;

	if( to->light != from->light )
		bits |= U_LIGHT;

	if( to->team != from->team )
		bits |= U_TEAM;

	//
	// write the message
	//
	if( !bits && !force )
		return;		// nothing to send!

	//----------

	if( bits & 0xff000000 )
		bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1;
	else if( bits & 0x00ff0000 )
		bits |= U_MOREBITS2 | U_MOREBITS1;
	else if( bits & 0x0000ff00 )
		bits |= U_MOREBITS1;

	MSG_WriteByte( msg, bits&255 );

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

	//----------

	if( bits & U_NUMBER16 )
		MSG_WriteShort( msg, to->number );
	else
		MSG_WriteByte( msg,	to->number );

	if( bits & U_SOLID )
		MSG_WriteShort( msg, to->solid );

	if( bits & U_MODEL )
		MSG_WriteByte( msg, to->modelindex );
	if( bits & U_MODEL2 )
		MSG_WriteByte( msg, to->modelindex2 );

	if( bits & U_FRAME8 )
		MSG_WriteByte( msg, to->frame );
	else if( bits & U_FRAME16 )
		MSG_WriteShort( msg, to->frame );

	if( (bits & U_SKIN8) && (bits & U_SKIN16) )		//used for laser colors
		MSG_WriteLong( msg, to->skinnum );
	else if( bits & U_SKIN8 )
		MSG_WriteByte( msg, to->skinnum );
	else if( bits & U_SKIN16 )
		MSG_WriteShort( msg, to->skinnum );


	if( (bits & (U_EFFECTS8|U_EFFECTS16)) == (U_EFFECTS8|U_EFFECTS16) )
		MSG_WriteLong( msg, to->effects );
	else if( bits & U_EFFECTS8 )
		MSG_WriteByte( msg, to->effects );
	else if( bits & U_EFFECTS16 )
		MSG_WriteShort( msg, to->effects );

	if( (bits & (U_RENDERFX8|U_RENDERFX16)) == (U_RENDERFX8|U_RENDERFX16) )
		MSG_WriteLong( msg, to->renderfx );
	else if( bits & U_RENDERFX8 )
		MSG_WriteByte( msg, to->renderfx );
	else if( bits & U_RENDERFX16 )
		MSG_WriteShort( msg, to->renderfx );

	if( bits & U_ORIGIN1 )
		MSG_WriteCoord( msg, to->origin[0] );
	if( bits & U_ORIGIN2 )
		MSG_WriteCoord( msg, to->origin[1] );
	if( bits & U_ORIGIN3 )
		MSG_WriteCoord( msg, to->origin[2] );

	if( bits & U_ANGLE1 && (to->solid == SOLID_BMODEL) )
		MSG_WriteAngle16( msg, to->angles[0] );
	else if( bits & U_ANGLE1 )
		MSG_WriteAngle( msg, to->angles[0] );

	if( bits & U_ANGLE2 && (to->solid == SOLID_BMODEL) )
		MSG_WriteAngle16( msg, to->angles[1] );
	else if( bits & U_ANGLE2 )
		MSG_WriteAngle( msg, to->angles[1] );

	if( bits & U_ANGLE3 && (to->solid == SOLID_BMODEL) )
		MSG_WriteAngle16( msg, to->angles[2] );
	else if( bits & U_ANGLE3 )
		MSG_WriteAngle( msg, to->angles[2] );

	if( bits & U_OLDORIGIN )
		MSG_WritePos( msg, to->old_origin );

	if( bits & U_TYPE ) {
		MSG_WriteByte( msg, to->type|(ET_INVERSE * to->takedamage) );
	}

	if( bits & U_SOUND )
		MSG_WriteByte( msg, to->sound );
	if( bits & U_EVENT ) {
		if ( !to->eventParms[0] ) {
			MSG_WriteByte( msg, to->events[0] );
		} else {
			MSG_WriteByte( msg, to->events[0] | EV_INVERSE );
			MSG_WriteByte( msg, to->eventParms[0] );
		}
	}
	if( bits & U_EVENT2 ) {
		if( !to->eventParms[1] ) {
			MSG_WriteByte( msg, to->events[1] );
		} else {
			MSG_WriteByte( msg, to->events[1] | EV_INVERSE );
			MSG_WriteByte( msg, to->eventParms[1] );
		}
	}

	if( bits & U_WEAPON )
		MSG_WriteByte( msg, to->weapon );

	if( bits & U_LIGHT )
		MSG_WriteLong( msg, to->light );

	if( bits & U_TEAM )
		MSG_WriteByte( msg, to->team );
}


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

//
// reading functions
//

void MSG_BeginReading( msg_t *msg )
{
	msg->readcount = 0;
}

int MSG_ReadShort( msg_t *msg_read )
{
	int	c;

	if( msg_read->readcount+2 > msg_read->cursize )
		c = -1;
	else		
		c = (short)( msg_read->data[msg_read->readcount]
		+ (msg_read->data[msg_read->readcount+1]<<8) );

	msg_read->readcount += 2;

	return c;
}

int MSG_ReadInt3( msg_t *msg_read )
{
	int	c;

	if( msg_read->readcount+3 > msg_read->cursize )
		c = -1;
	else
		c = msg_read->data[msg_read->readcount] | 
			(msg_read->data[msg_read->readcount+1]<<8) | 
			(msg_read->data[msg_read->readcount+2]<<16) | 
			((msg_read->data[msg_read->readcount+2] & 0x80) ? ~0xFFFFFF : 0);

	msg_read->readcount += 3;

	return c;
}

int MSG_ReadLong( msg_t *msg_read )
{
	int	c;

	if (msg_read->readcount+4 > msg_read->cursize)
		c = -1;
	else
		c = msg_read->data[msg_read->readcount]
		+ (msg_read->data[msg_read->readcount+1]<<8 )
		+ (msg_read->data[msg_read->readcount+2]<<16)
		+ (msg_read->data[msg_read->readcount+3]<<24);

	msg_read->readcount += 4;

	return c;
}

float MSG_ReadFloat( msg_t *msg_read )
{
	union
	{
		qbyte	b[4];
		float	f;
		int	l;
	} dat;

	if( msg_read->readcount+4 > msg_read->cursize )
		dat.f = -1;
	else
	{
		dat.b[0] =	msg_read->data[msg_read->readcount];
		dat.b[1] =	msg_read->data[msg_read->readcount+1];
		dat.b[2] =	msg_read->data[msg_read->readcount+2];
		dat.b[3] =	msg_read->data[msg_read->readcount+3];
	}
	msg_read->readcount += 4;

	dat.l = LittleLong( dat.l );

	return dat.f;	
}

char *MSG_ReadString( msg_t *msg_read )
{
	int		l, c;
	static char	string[2048];

	l = 0;
	do
	{
		c = MSG_ReadByte( msg_read );
		if( c == 255 )
			continue;
		if( c == -1 || c == 0 )
			break;
		string[l] = c;
		l++;
	} while( l < sizeof(string)-1 );

	string[l] = 0;

	return string;
}

char *MSG_ReadStringLine( msg_t *msg_read )
{
	int		l, c;
	static char	string[2048];

	l = 0;
	do
	{
		c = MSG_ReadByte( msg_read );
		if( c == 255 )
			continue;
		if( c == -1 || c == 0 || c == '\n' )
			break;
		string[l] = c;
		l++;
	} while( l < sizeof(string)-1 );

	string[l] = 0;

	return string;
}

void MSG_ReadDir( msg_t *sb, vec3_t dir )
{
	ByteToDir( MSG_ReadByte (sb), dir );
}

void MSG_ReadDeltaUsercmd( msg_t *msg_read, usercmd_t *from, usercmd_t *move )
{
	int bits;

	memcpy( move, from, sizeof(*move) );

	bits = MSG_ReadByte (msg_read);

	// read current angles
	if( bits & CM_ANGLE1 )
		move->angles[0] = MSG_ReadShort( msg_read );
	if( bits & CM_ANGLE2 )
		move->angles[1] = MSG_ReadShort( msg_read );
	if( bits & CM_ANGLE3 )
		move->angles[2] = MSG_ReadShort( msg_read );

	// read movement
	if( bits & CM_FORWARD )
		move->forwardmove = MSG_ReadShort( msg_read );
	if( bits & CM_SIDE )
		move->sidemove = MSG_ReadShort( msg_read );
	if( bits & CM_UP )
		move->upmove = MSG_ReadShort( msg_read );

	// read buttons
	if( bits & CM_BUTTONS )
		move->buttons = MSG_ReadByte( msg_read );

	// read time to run command
	move->msec = MSG_ReadByte( msg_read );

	move->serverTimeStamp = MSG_ReadLong( msg_read );

}


void MSG_ReadData( msg_t *msg_read, void *data, int len )
{
	int		i;

	for( i = 0; i < len; i++ )
		((qbyte *)data)[i] = MSG_ReadByte( msg_read );
}

int MSG_SkipData( msg_t *msg_read, int len )
{
	if ( msg_read->readcount + len < msg_read->cursize )
	{
		msg_read->readcount += len;
		return 1;
	}
	else
	{
		return 0;
	}
}





