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

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

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;
  Q_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;

extern  int sv_nailmodel, sv_supernailmodel, sv_playermodel;

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 ("U_REMOVE");
  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);
}
