/*
Copyright (C) 1996-1997 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 "bspfile.h"
#include "model.h"
#include "qwsvdef.h"
#include "server.h"
#include "sys.h"

// FIXME - header hacks
extern int sv_nailmodel, sv_supernailmodel, sv_playermodel;

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

The PVS must include a small area around the client to allow head bobbing
or other small motion on the client side.  Otherwise, a bob might cause an
entity that should be visible to not show up, especially when the bob
crosses a waterline.

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

int fatbytes;
byte fatpvs[MAX_MAP_LEAFS / 8];

void
SV_AddToFatPVS(vec3_t org, mnode_t *node)
{
    int i;
    byte *pvs;
    mplane_t *plane;
    float d;

    while (1) {
	// if this is a leaf, accumulate the pvs bits
	if (node->contents < 0) {
	    if (node->contents != CONTENTS_SOLID) {
		pvs = Mod_LeafPVS((mleaf_t *)node, sv.worldmodel);
		for (i = 0; i < fatbytes; i++)
		    fatpvs[i] |= pvs[i];
	    }
	    return;
	}

	plane = node->plane;
	d = DotProduct(org, plane->normal) - plane->dist;
	if (d > 8)
	    node = node->children[0];
	else if (d < -8)
	    node = node->children[1];
	else {			// go down both
	    SV_AddToFatPVS(org, node->children[0]);
	    node = node->children[1];
	}
    }
}

/*
=============
SV_FatPVS

Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
given point.
=============
*/
byte *
SV_FatPVS(vec3_t org)
{
    fatbytes = (sv.worldmodel->numleafs + 31) >> 3;
    memset(fatpvs, 0, fatbytes);
    SV_AddToFatPVS(org, sv.worldmodel->nodes);
    return fatpvs;
}

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

// because there can be a lot of nails, there is a special
// network protocol for them
#define	MAX_NAILS	32
edict_t *nails[MAX_NAILS];
int numnails;

qboolean
SV_AddNailUpdate(edict_t *ent)
{
    if (ent->v.modelindex != sv_nailmodel
	&& ent->v.modelindex != sv_supernailmodel)
	return false;
    if (numnails == MAX_NAILS)
	return true;
    nails[numnails] = ent;
    numnails++;
    return true;
}

void
SV_EmitNailUpdate(sizebuf_t *msg)
{
    byte bits[6];		// [48 bits] xyzpy 12 12 12 4 8
    int n, i;
    edict_t *ent;
    int x, y, z, p, yaw;

    if (!numnails)
	return;

    MSG_WriteByte(msg, svc_nails);
    MSG_WriteByte(msg, numnails);

    for (n = 0; n < numnails; n++) {
	ent = nails[n];
	x = (int)(ent->v.origin[0] + 4096) >> 1;
	y = (int)(ent->v.origin[1] + 4096) >> 1;
	z = (int)(ent->v.origin[2] + 4096) >> 1;
	p = (int)(16 * ent->v.angles[0] / 360) & 15;
	yaw = (int)(256 * ent->v.angles[1] / 360) & 255;

	bits[0] = x;
	bits[1] = (x >> 8) | (y << 4);
	bits[2] = (y >> 4);
	bits[3] = z;
	bits[4] = (z >> 8) | (p << 4);
	bits[5] = yaw;

	for (i = 0; i < 6; i++)
	    MSG_WriteByte(msg, bits[i]);
    }
}

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


/*
==================
SV_WriteDelta

Writes part of a packetentities message.
Can delta from either a baseline or a previous packet_entity
==================
*/
void
SV_WriteDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *msg,
	      qboolean force)
{
    int bits;
    int i;
    float miss;

// send an update
    bits = 0;

    for (i = 0; i < 3; i++) {
	miss = to->origin[i] - from->origin[i];
	if (miss < -0.1 || miss > 0.1)
	    bits |= U_ORIGIN1 << i;
    }

    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->colormap != from->colormap)
	bits |= U_COLORMAP;

    if (to->skinnum != from->skinnum)
	bits |= U_SKIN;

    if (to->frame != from->frame)
	bits |= U_FRAME;

    if (to->effects != from->effects)
	bits |= U_EFFECTS;

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

    if (bits & 511)
	bits |= U_MOREBITS;

    if (to->flags & U_SOLID)
	bits |= U_SOLID;

    //
    // write the message
    //
    if (!to->number)
	SV_Error("Unset entity number");
    if (to->number >= 512)
	SV_Error("Entity number >= 512");

    if (!bits && !force)
	return;			// nothing to send!
    i = to->number | (bits & ~511);
    if (i & U_REMOVE)
	Sys_Error("%s: U_REMOVE", __func__);
    MSG_WriteShort(msg, i);

    if (bits & U_MOREBITS)
	MSG_WriteByte(msg, bits & 255);
    if (bits & U_MODEL)
	MSG_WriteByte(msg, to->modelindex);
    if (bits & U_FRAME)
	MSG_WriteByte(msg, to->frame);
    if (bits & U_COLORMAP)
	MSG_WriteByte(msg, to->colormap);
    if (bits & U_SKIN)
	MSG_WriteByte(msg, to->skinnum);
    if (bits & U_EFFECTS)
	MSG_WriteByte(msg, to->effects);
    if (bits & U_ORIGIN1)
	MSG_WriteCoord(msg, to->origin[0]);
    if (bits & U_ANGLE1)
	MSG_WriteAngle(msg, to->angles[0]);
    if (bits & U_ORIGIN2)
	MSG_WriteCoord(msg, to->origin[1]);
    if (bits & U_ANGLE2)
	MSG_WriteAngle(msg, to->angles[1]);
    if (bits & U_ORIGIN3)
	MSG_WriteCoord(msg, to->origin[2]);
    if (bits & U_ANGLE3)
	MSG_WriteAngle(msg, to->angles[2]);
}

/*
=============
SV_EmitPacketEntities

Writes a delta update of a packet_entities_t to the message.

=============
*/
void
SV_EmitPacketEntities(client_t *client, packet_entities_t * to,
		      sizebuf_t *msg)
{
    edict_t *ent;
    client_frame_t *fromframe;
    packet_entities_t *from;
    int oldindex, newindex;
    int oldnum, newnum;
    int oldmax;

    // this is the frame that we are going to delta update from
    if (client->delta_sequence != -1) {
	fromframe = &client->frames[client->delta_sequence & UPDATE_MASK];
	from = &fromframe->entities;
	oldmax = from->num_entities;

	MSG_WriteByte(msg, svc_deltapacketentities);
	MSG_WriteByte(msg, client->delta_sequence);
    } else {
	oldmax = 0;		// no delta update
	from = NULL;

	MSG_WriteByte(msg, svc_packetentities);
    }

    newindex = 0;
    oldindex = 0;
//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK
//                      , client->netchan.outgoing_sequence & UPDATE_MASK);
    while (newindex < to->num_entities || oldindex < oldmax) {
	newnum =
	    newindex >=
	    to->num_entities ? 9999 : to->entities[newindex].number;
	oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number;

	if (newnum == oldnum) {	// delta update from old position
//Con_Printf ("delta %i\n", newnum);
	    SV_WriteDelta(&from->entities[oldindex], &to->entities[newindex],
			  msg, false);
	    oldindex++;
	    newindex++;
	    continue;
	}

	if (newnum < oldnum) {	// this is a new entity, send it from the baseline
	    ent = EDICT_NUM(newnum);
//Con_Printf ("baseline %i\n", newnum);
	    SV_WriteDelta(&ent->baseline, &to->entities[newindex], msg, true);
	    newindex++;
	    continue;
	}

	if (newnum > oldnum) {	// the old entity isn't present in the new message
//Con_Printf ("remove %i\n", oldnum);
	    MSG_WriteShort(msg, oldnum | U_REMOVE);
	    oldindex++;
	    continue;
	}
    }

    MSG_WriteShort(msg, 0);	// end of packetentities
}

/*
=============
SV_WritePlayersToClient

=============
*/
void
SV_WritePlayersToClient(client_t *client, edict_t *clent, byte *pvs,
			sizebuf_t *msg)
{
    int i, j;
    client_t *cl;
    edict_t *ent;
    int msec;
    usercmd_t cmd;
    int pflags;

    for (j = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) {
	if (cl->state != cs_spawned)
	    continue;

	ent = cl->edict;

	// ZOID visibility tracking
	if (ent != clent &&
	    !(client->spec_track && client->spec_track - 1 == j)) {
	    if (cl->spectator)
		continue;

	    // ignore if not touching a PV leaf
	    for (i = 0; i < ent->num_leafs; i++)
		if (pvs[ent->leafnums[i] >> 3] &
		    (1 << (ent->leafnums[i] & 7)))
		    break;
	    if (i == ent->num_leafs)
		continue;	// not visible
	}

	pflags = PF_MSEC | PF_COMMAND;

	if (ent->v.modelindex != sv_playermodel)
	    pflags |= PF_MODEL;
	for (i = 0; i < 3; i++)
	    if (ent->v.velocity[i])
		pflags |= PF_VELOCITY1 << i;
	if (ent->v.effects)
	    pflags |= PF_EFFECTS;
	if (ent->v.skin)
	    pflags |= PF_SKINNUM;
	if (ent->v.health <= 0)
	    pflags |= PF_DEAD;
	if (ent->v.mins[2] != -24)
	    pflags |= PF_GIB;

	if (cl->spectator) {	// only sent origin and velocity to spectators
	    pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3;
	} else if (ent == clent) {	// don't send a lot of data on personal entity
	    pflags &= ~(PF_MSEC | PF_COMMAND);
	    if (ent->v.weaponframe)
		pflags |= PF_WEAPONFRAME;
	}

	if (client->spec_track && client->spec_track - 1 == j &&
	    ent->v.weaponframe)
	    pflags |= PF_WEAPONFRAME;

	MSG_WriteByte(msg, svc_playerinfo);
	MSG_WriteByte(msg, j);
	MSG_WriteShort(msg, pflags);

	for (i = 0; i < 3; i++)
	    MSG_WriteCoord(msg, ent->v.origin[i]);

	MSG_WriteByte(msg, ent->v.frame);

	if (pflags & PF_MSEC) {
	    msec = 1000 * (sv.time - cl->localtime);
	    if (msec > 255)
		msec = 255;
	    MSG_WriteByte(msg, msec);
	}

	if (pflags & PF_COMMAND) {
	    cmd = cl->lastcmd;

	    if (ent->v.health <= 0) {	// don't show the corpse looking around...
		cmd.angles[0] = 0;
		cmd.angles[1] = ent->v.angles[1];
		cmd.angles[0] = 0;
	    }

	    cmd.buttons = 0;	// never send buttons
	    cmd.impulse = 0;	// never send impulses

	    MSG_WriteDeltaUsercmd(msg, &nullcmd, &cmd);
	}

	for (i = 0; i < 3; i++)
	    if (pflags & (PF_VELOCITY1 << i))
		MSG_WriteShort(msg, ent->v.velocity[i]);

	if (pflags & PF_MODEL)
	    MSG_WriteByte(msg, ent->v.modelindex);

	if (pflags & PF_SKINNUM)
	    MSG_WriteByte(msg, ent->v.skin);

	if (pflags & PF_EFFECTS)
	    MSG_WriteByte(msg, ent->v.effects);

	if (pflags & PF_WEAPONFRAME)
	    MSG_WriteByte(msg, ent->v.weaponframe);
    }
}


/*
=============
SV_WriteEntitiesToClient

Encodes the current state of the world as
a svc_packetentities messages and possibly
a svc_nails message and
svc_playerinfo messages
=============
*/
void
SV_WriteEntitiesToClient(client_t *client, sizebuf_t *msg)
{
    int e, i;
    byte *pvs;
    vec3_t org;
    edict_t *ent;
    packet_entities_t *pack;
    edict_t *clent;
    client_frame_t *frame;
    entity_state_t *state;

    // this is the frame we are creating
    frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK];

    // find the client's PVS
    clent = client->edict;
    VectorAdd(clent->v.origin, clent->v.view_ofs, org);
    pvs = SV_FatPVS(org);

    // send over the players in the PVS
    SV_WritePlayersToClient(client, clent, pvs, msg);

    // put other visible entities into either a packet_entities or a nails message
    pack = &frame->entities;
    pack->num_entities = 0;

    numnails = 0;

    for (e = MAX_CLIENTS + 1, ent = EDICT_NUM(e); e < sv.num_edicts;
	 e++, ent = NEXT_EDICT(ent)) {
	// ignore ents without visible models
	if (!ent->v.modelindex || !*PR_GetString(ent->v.model))
	    continue;

	// ignore if not touching a PV leaf
	for (i = 0; i < ent->num_leafs; i++)
	    if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)))
		break;

	if (i == ent->num_leafs)
	    continue;		// not visible

	if (SV_AddNailUpdate(ent))
	    continue;		// added to the special update list

	// add to the packetentities
	if (pack->num_entities == MAX_PACKET_ENTITIES)
	    continue;		// all full

	state = &pack->entities[pack->num_entities];
	pack->num_entities++;

	state->number = e;
	state->flags = 0;
	VectorCopy(ent->v.origin, state->origin);
	VectorCopy(ent->v.angles, state->angles);
	state->modelindex = ent->v.modelindex;
	state->frame = ent->v.frame;
	state->colormap = ent->v.colormap;
	state->skinnum = ent->v.skin;
	state->effects = ent->v.effects;
    }

    // encode the packet entities as a delta from the
    // last packetentities acknowledged by the client

    SV_EmitPacketEntities(client, pack, msg);

    // now add the specialized nail update
    SV_EmitNailUpdate(msg);
}
