/*
	net_svc_common.c

	(description)

	Copyright (C) 2001  Adam Olsen

	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:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/
static const char rcsid[] = 
	"$Id$";

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifndef _WIN32
# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif
#else
# include <windows.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include "QF/msg.h"
#include "QF/net.h"
#include "QF/net_svc.h"
#include "QF/net_ucmd.h"
#include "QF/sound.h"
#include "QF/sys.h"

// N is netquake-only, Q is quakeworld-only, M is identical in both,
// D is exists in both, but is different, X is used in neither
static const char *net_svc_strings[] = {
	"svc_bad",
	"svc_nop",					// M
	"svc_disconnect",			// M
	"svc_qwupdatestat",			// D
	"svc_version",				// N  [long] server version
	"svc_setview",				// N  [short] entity number
	"svc_qwsound",				// D  <see code>
	"svc_time",					// N  [float] server time
	"svc_qwprint",				// D  [string] null terminated string
	"svc_stufftext",			// M  [string] stuffed into client's
								//    console buffer the string
								//    should be \n terminated
	"svc_setangle",				// M  [vec3] set the view angle to this
								//    absolute value
	"svc_serverdata",			// D  [long] version ...
	"svc_lightstyle",			// M  [byte] [string]
	"svc_updatename",			// N  [byte] [string]
	"svc_updatefrags",			// M  [byte] [short]
	"svc_clientdata",			// N  <shortbits + data>
	"svc_stopsound",			// M  <see code>
	"svc_updatecolors",			// N  [byte] [byte]
	"svc_particle",				// N  [vec3] <variable>
	"svc_damage",				// M  [byte] impact [byte] blood [vec3]
								//    from
	"svc_spawnstatic",			// M
	"svc_spawnbinary",			// X
	"svc_spawnbaseline",		// M
	"svc_qwtempentity",			// D  <variable>
	"svc_setpause",				// M
	"svc_signonnum",			// N
	"svc_centerprint",			// M
	"svc_killedmonster",		// M
	"svc_foundsecret",			// M
	"svc_spawnstaticsound",		// M
	"svc_qwintermission",		// D
	"svc_finale",				// M  [string] music [string] text
	"svc_qwcdtrack",			// D  [byte] track [byte] looptrack
	"svc_sellscreen",			// M
	"svc_smallkick",			// D  Quake svc_cutscene
	"svc_bigkick",				// Q
	"svc_updateping",			// Q
	"svc_updateentertime",		// Q
	"svc_updatestatlong",		// Q
	"svc_muzzleflash",			// Q
	"svc_updateuserinfo",		// Q
	"svc_download",				// Q
	"svc_playerinfo",			// Q
	"svc_nails",				// Q
	"svc_chokecount",			// Q
	"svc_modellist",			// Q
	"svc_soundlist",			// Q
	"svc_packetentities",		// Q
	"svc_deltapacketentities",	// Q
	"svc_maxspeed",				// Q
	"svc_entgravity",			// Q
	"svc_setinfo",				// Q
	"svc_qwserverinfo",			// Q
	"svc_updatepl",				// Q
};

const char *
NET_SVC_GetString (svc_t type)
{
	if (type >= 0 && type < (sizeof (net_svc_strings) / sizeof (const char *)))
		return net_svc_strings[type];
	else
		return "Invalid SVC";
}


// This is evil }:>
net_svc_emit_t net_svc_common_emit_jumptable[] = {
	[svc_nop] = (net_svc_emit_t) NET_SVC_NOP_Emit,
	[svc_disconnect] = (net_svc_emit_t) NET_SVC_Disconnect_Emit,
	[svc_centerprint] = (net_svc_emit_t) NET_SVC_Centerprint_Emit,
	[svc_stufftext] = (net_svc_emit_t) NET_SVC_Stufftext_Emit,
	[svc_damage] = (net_svc_emit_t) NET_SVC_Damage_Emit,
	[svc_setangle] = (net_svc_emit_t) NET_SVC_SetAngle_Emit,
	[svc_lightstyle] = (net_svc_emit_t) NET_SVC_LightStyle_Emit,
	[svc_stopsound] = (net_svc_emit_t) NET_SVC_StopSound_Emit,
	[svc_updatefrags] = (net_svc_emit_t) NET_SVC_UpdateFrags_Emit,
	[svc_spawnbaseline] = (net_svc_emit_t) NET_SVC_SpawnBaseline_Emit,
	[svc_spawnstatic] = (net_svc_emit_t) NET_SVC_SpawnStatic_Emit,
	[svc_killedmonster] = (net_svc_emit_t) NET_SVC_KilledMonster_Emit,
	[svc_foundsecret] = (net_svc_emit_t) NET_SVC_FoundSecret_Emit,
	[svc_spawnstaticsound] = (net_svc_emit_t) NET_SVC_SpawnStaticSound_Emit,
	[svc_finale] = (net_svc_emit_t) NET_SVC_Finale_Emit,
	[svc_sellscreen] = (net_svc_emit_t) NET_SVC_SellScreen_Emit,
	[svc_setpause] = (net_svc_emit_t) NET_SVC_SetPause_Emit,
};
int net_svc_common_emit_count =
	sizeof (net_svc_common_emit_jumptable) / sizeof (net_svc_emit_t);

net_svc_parse_t net_svc_common_parse_jumptable[] = {
	[svc_nop] = (net_svc_parse_t) NET_SVC_NOP_Parse,
	[svc_disconnect] = (net_svc_parse_t) NET_SVC_Disconnect_Parse,
	[svc_centerprint] = (net_svc_parse_t) NET_SVC_Centerprint_Parse,
	[svc_stufftext] = (net_svc_parse_t) NET_SVC_Stufftext_Parse,
	[svc_damage] = (net_svc_parse_t) NET_SVC_Damage_Parse,
	[svc_setangle] = (net_svc_parse_t) NET_SVC_SetAngle_Parse,
	[svc_lightstyle] = (net_svc_parse_t) NET_SVC_LightStyle_Parse,
	[svc_stopsound] = (net_svc_parse_t) NET_SVC_StopSound_Parse,
	[svc_updatefrags] = (net_svc_parse_t) NET_SVC_UpdateFrags_Parse,
	[svc_spawnbaseline] = (net_svc_parse_t) NET_SVC_SpawnBaseline_Parse,
	[svc_spawnstatic] = (net_svc_parse_t) NET_SVC_SpawnStatic_Parse,
	[svc_killedmonster] = (net_svc_parse_t) NET_SVC_KilledMonster_Parse,
	[svc_foundsecret] = (net_svc_parse_t) NET_SVC_FoundSecret_Parse,
	[svc_spawnstaticsound] = (net_svc_parse_t) NET_SVC_SpawnStaticSound_Parse,
	[svc_finale] = (net_svc_parse_t) NET_SVC_Finale_Parse,
	[svc_sellscreen] = (net_svc_parse_t) NET_SVC_SellScreen_Parse,
	[svc_setpause] = (net_svc_parse_t) NET_SVC_SetPause_Parse,
};
int net_svc_common_parse_count =
	sizeof (net_svc_common_parse_jumptable) / sizeof (net_svc_parse_t);


net_svc_emit_t *net_svc_emit_jumptable;
int net_svc_emit_count;

net_svc_emit_t *net_svc_emit_jumptable;
int net_svc_emit_count;
net_svc_parse_t *net_svc_parse_jumptable;
int net_svc_parse_count;

net_status_t
NET_SVC_Emit (svc_t type, void *block, sizebuf_t *buf)
{
	int oldsize = buf->cursize;
	net_status_t retval;

	if (type < 0 || type >= net_svc_emit_count ||
		!net_svc_emit_jumptable[type])
//		return NET_ERROR;
		Sys_Error ("NET_SVC_Emit: invalid type %d\n", type);

	MSG_WriteByte (buf, type);
	retval = net_svc_emit_jumptable[type] (block, buf);
	if (retval == NET_SHORT)
		buf->cursize = oldsize; // revert the buffer
	return retval;
}

net_status_t
NET_SVC_Parse (svc_t *type, net_svc_any_t *block, msg_t *msg)
{
	*type = MSG_ReadByte (msg);

	if (*type < 0 || *type >= net_svc_parse_count ||
		!net_svc_parse_jumptable[*type])
		return NET_ERROR;

	return net_svc_parse_jumptable[*type] (block, msg);
}


net_status_t
NET_SVC_NOP_Emit (net_svc_nop_t *block, sizebuf_t *buf)
{
	return buf->overflowed;
}

net_status_t
NET_SVC_NOP_Parse (net_svc_nop_t *block, msg_t *msg)
{
	return msg->badread;
}

net_status_t
NET_SVC_Disconnect_Emit (net_svc_disconnect_t *block, sizebuf_t *buf)
{
	return buf->overflowed;
}

net_status_t
NET_SVC_Disconnect_Parse (net_svc_disconnect_t *block, msg_t *msg)
{
	return msg->badread;
}

net_status_t
NET_SVC_Centerprint_Emit (net_svc_centerprint_t *block, sizebuf_t *buf)
{
	MSG_WriteString (buf, block->message);

	return buf->overflowed;
}

net_status_t
NET_SVC_Centerprint_Parse (net_svc_centerprint_t *block, msg_t *msg)
{
	block->message = MSG_ReadString (msg);

	return msg->badread;
}

net_status_t
NET_SVC_Stufftext_Emit (net_svc_stufftext_t *block, sizebuf_t *buf)
{
	MSG_WriteString (buf, block->commands);

	return buf->overflowed;
}

net_status_t
NET_SVC_Stufftext_Parse (net_svc_stufftext_t *block, msg_t *msg)
{
	block->commands = MSG_ReadString (msg);

	return msg->badread;
}

net_status_t
NET_SVC_Damage_Emit (net_svc_damage_t *block, sizebuf_t *buf)
{
	int i;

	MSG_WriteByte (buf, block->armor);
	MSG_WriteByte (buf, block->blood);
	for (i = 0; i < 3; i++)
		MSG_WriteCoord (buf, block->from[i]);

	return buf->overflowed;
}

net_status_t
NET_SVC_Damage_Parse (net_svc_damage_t *block, msg_t *msg)
{
	int i;

	block->armor = MSG_ReadByte (msg);
	block->blood = MSG_ReadByte (msg);
	for (i = 0; i < 3; i++)
		block->from[i] = MSG_ReadCoord (msg);

	return msg->badread;
}

net_status_t
NET_SVC_SetAngle_Emit (net_svc_setangle_t *block, sizebuf_t *buf)
{
	int i;

	for (i = 0; i < 3; i++)
		MSG_WriteAngle (buf, block->angles[i]);

	return buf->overflowed;
}

net_status_t
NET_SVC_SetAngle_Parse (net_svc_setangle_t *block, msg_t *msg)
{
	int i;

	for (i = 0; i < 3; i++)
		block->angles[i] = MSG_ReadAngle (msg);

	return msg->badread;
}

net_status_t
NET_SVC_LightStyle_Emit (net_svc_lightstyle_t *block, sizebuf_t *buf)
{
	MSG_WriteByte (buf, block->stylenum);
	MSG_WriteString (buf, block->map);

	return buf->overflowed;
}

net_status_t
NET_SVC_LightStyle_Parse (net_svc_lightstyle_t *block, msg_t *msg)
{
	block->stylenum = MSG_ReadByte (msg);
	block->map = MSG_ReadString (msg);

	return msg->badread;
}

net_status_t
NET_SVC_StopSound_Emit (net_svc_stopsound_t *block, sizebuf_t *buf)
{
	MSG_WriteShort (buf, (block->entity << 3) & (block->channel & 7));

	return buf->overflowed;
}

net_status_t
NET_SVC_StopSound_Parse (net_svc_stopsound_t *block, msg_t *msg)
{
	int i = MSG_ReadShort (msg);
	block->entity = i >> 3;
	block->channel = i & 7;

	return msg->badread;
}

net_status_t
NET_SVC_UpdateFrags_Emit (net_svc_updatefrags_t *block, sizebuf_t *buf)
{
	MSG_WriteByte (buf, block->player);
	MSG_WriteShort (buf, block->frags);

	return buf->overflowed;
}

net_status_t
NET_SVC_UpdateFrags_Parse (net_svc_updatefrags_t *block, msg_t *msg)
{
	block->player = MSG_ReadByte (msg);
	block->frags = MSG_ReadShort (msg);

	return msg->badread;
}

net_status_t
NET_SVC_SpawnBaseline_Emit (net_svc_spawnbaseline_t *block, sizebuf_t *buf)
{
	int i;

	MSG_WriteShort (buf, block->num);
	MSG_WriteByte (buf, block->modelindex);
	MSG_WriteByte (buf, block->frame);
	MSG_WriteByte (buf, block->colormap);
	MSG_WriteByte (buf, block->skinnum);

	for (i = 0; i < 3; i++) {
		MSG_WriteCoord (buf, block->origin[i]);
		MSG_WriteAngle (buf, block->angles[i]);
	}

	return buf->overflowed;
}

net_status_t
NET_SVC_SpawnBaseline_Parse (net_svc_spawnbaseline_t *block, msg_t *msg)
{
	int i;

	block->num = MSG_ReadShort (msg);
	block->modelindex = MSG_ReadByte (msg);
	block->frame = MSG_ReadByte (msg);
	block->colormap = MSG_ReadByte (msg);
	block->skinnum = MSG_ReadByte (msg);

	// these are interlaced?  bad drugs...
	for (i = 0; i < 3; i ++) {
		block->origin[i] = MSG_ReadCoord (msg);
		block->angles[i] = MSG_ReadAngle (msg);
	}

	return msg->badread;
}

net_status_t
NET_SVC_SpawnStatic_Emit (net_svc_spawnstatic_t *block, sizebuf_t *buf)
{
	int i;

	MSG_WriteByte (buf, block->modelindex);
	MSG_WriteByte (buf, block->frame);
	MSG_WriteByte (buf, block->colormap);
	MSG_WriteByte (buf, block->skinnum);
	for (i = 0; i < 3; i++) {
		MSG_WriteCoord (buf, block->origin[i]);
		MSG_WriteAngle (buf, block->angles[i]);
	}

	return buf->overflowed;
}

net_status_t
NET_SVC_SpawnStatic_Parse (net_svc_spawnstatic_t *block, msg_t *msg)
{
	int i;

	block->modelindex = MSG_ReadByte (msg);
	block->frame = MSG_ReadByte (msg);
	block->colormap = MSG_ReadByte (msg);
	block->skinnum = MSG_ReadByte (msg);

	// these are interlaced?  bad drugs...
	for (i = 0; i < 3; i++) {
		block->origin[i] = MSG_ReadCoord (msg);
		block->angles[i] = MSG_ReadAngle (msg);
	}

	return msg->badread;
}

net_status_t
NET_SVC_KilledMonster_Emit (net_svc_killedmonster_t *block, sizebuf_t *buf)
{
	return buf->overflowed;
}

net_status_t
NET_SVC_KilledMonster_Parse (net_svc_killedmonster_t *block, msg_t *msg)
{
	return msg->badread;
}

net_status_t
NET_SVC_FoundSecret_Emit (net_svc_foundsecret_t *block, sizebuf_t *buf)
{
	return buf->overflowed;
}

net_status_t
NET_SVC_FoundSecret_Parse (net_svc_foundsecret_t *block, msg_t *msg)
{
	return msg->badread;
}

net_status_t
NET_SVC_SpawnStaticSound_Emit (net_svc_spawnstaticsound_t *block,
							   sizebuf_t *buf)
{
	int i;

	for (i = 0; i < 3; i++)
		MSG_WriteCoord (buf, block->position[i]);
	MSG_WriteByte (buf, block->sound_num);
	MSG_WriteByte (buf, block->volume);
	MSG_WriteByte (buf, block->attenuation);

	return buf->overflowed;
}

net_status_t
NET_SVC_SpawnStaticSound_Parse (net_svc_spawnstaticsound_t *block,
								msg_t *msg)
{
	int i;

	for (i = 0; i < 3; i++)
		block->position[i] = MSG_ReadCoord (msg);
	block->sound_num = MSG_ReadByte (msg);
	block->volume = MSG_ReadByte (msg);
	block->attenuation = MSG_ReadByte (msg);

	return msg->badread;
}

net_status_t
NET_SVC_Finale_Emit (net_svc_finale_t *block, sizebuf_t *buf)
{
	MSG_WriteString (buf, block->message);

	return buf->overflowed;
}

net_status_t
NET_SVC_Finale_Parse (net_svc_finale_t *block, msg_t *msg)
{
	block->message = MSG_ReadString (msg);

	return msg->badread;
}

net_status_t
NET_SVC_SellScreen_Emit (net_svc_sellscreen_t *block, sizebuf_t *buf)
{
	return buf->overflowed;
}

net_status_t
NET_SVC_SellScreen_Parse (net_svc_sellscreen_t *block, msg_t *msg)
{
	return msg->badread;
}

net_status_t
NET_SVC_SetPause_Emit (net_svc_setpause_t *block, sizebuf_t *buf)
{
	MSG_WriteByte (buf, block->paused);

	return buf->overflowed;
}

net_status_t
NET_SVC_SetPause_Parse (net_svc_setpause_t *block, msg_t *msg)
{
	block->paused = MSG_ReadByte (msg);

	return msg->badread;
}