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

*/
// sv_main.c -- server main program

#include "qwsvdef.h"

#define CHAN_AUTO   0
#define CHAN_WEAPON 1
#define CHAN_VOICE  2
#define CHAN_ITEM   3
#define CHAN_BODY   4

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

Con_Printf redirection

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

char  outputbuf[8000];

redirect_t  sv_redirected;

extern cvar_t sv_phs;

/*
==================
SV_FlushRedirect
==================
*/
void SV_FlushRedirect (void)
{
  char  send[8000+6];

  if (sv_redirected == RD_PACKET)
  {
    send[0] = 0xff;
    send[1] = 0xff;
    send[2] = 0xff;
    send[3] = 0xff;
    send[4] = A2C_PRINT;
    memcpy (send+5, outputbuf, strlen(outputbuf)+1);

    NET_SendPacket (strlen(send)+1, send, net_from);
  }
  else if (sv_redirected == RD_CLIENT)
  {
    ClientReliableWrite_Begin (host_client, svc_print, strlen(outputbuf)+3);
    ClientReliableWrite_Byte (host_client, PRINT_HIGH);
    ClientReliableWrite_String (host_client, outputbuf);
  }

  // clear it
  outputbuf[0] = 0;
}


/*
==================
SV_BeginRedirect

  Send Con_Printf data to the remote client
  instead of the console
==================
*/
void SV_BeginRedirect (redirect_t rd)
{
  sv_redirected = rd;
  outputbuf[0] = 0;
}

void SV_EndRedirect (void)
{
  SV_FlushRedirect ();
  sv_redirected = RD_NONE;
}


/*
================
Con_Printf

Handles cursor positioning, line wrapping, etc
================
*/
#define MAXPRINTMSG 4096
// FIXME: make a buffer size safe vsprintf?
void Con_Printf (char *fmt, ...)
{
  va_list   argptr;
  char    msg[MAXPRINTMSG];
  
  va_start (argptr,fmt);
  vsprintf (msg,fmt,argptr);
  va_end (argptr);

  // add to redirected message
  if (sv_redirected)
  {
    if (strlen (msg) + strlen(outputbuf) > sizeof(outputbuf) - 1)
      SV_FlushRedirect ();
    strcat (outputbuf, msg);
    return;
  }

  Sys_Printf ("%s", msg); // also echo to debugging console
  if (sv_logfile)
    fprintf (sv_logfile, "%s", msg);
}

/*
================
Con_DPrintf

A Con_Printf that only shows up if the "developer" cvar is set
================
*/
void Con_DPrintf (char *fmt, ...)
{
  va_list   argptr;
  char    msg[MAXPRINTMSG];

  if (!developer.value)
    return;

  va_start (argptr,fmt);
  vsprintf (msg,fmt,argptr);
  va_end (argptr);
  
  Con_Printf ("%s", msg);
}

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

EVENT MESSAGES

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

static void SV_PrintToClient(client_t *cl, int level, char *string)
{
  ClientReliableWrite_Begin (cl, svc_print, strlen(string)+3);
  ClientReliableWrite_Byte (cl, level);
  ClientReliableWrite_String (cl, string);
}


/*
=================
SV_ClientPrintf

Sends text across to be displayed if the level passes
=================
*/
void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...)
{
  va_list   argptr;
  char    string[1024];
  
  if (level < cl->messagelevel)
    return;
  
  va_start (argptr,fmt);
  vsprintf (string, fmt,argptr);
  va_end (argptr);

  SV_PrintToClient(cl, level, string);
}

/*
=================
SV_BroadcastPrintf

Sends text to all active clients
=================
*/
void SV_BroadcastPrintf (int level, char *fmt, ...)
{
  va_list   argptr;
  char    string[1024];
  client_t  *cl;
  int     i;

  va_start (argptr,fmt);
  vsprintf (string, fmt,argptr);
  va_end (argptr);
  
  Sys_Printf ("%s", string);  // print to the console

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

    SV_PrintToClient(cl, level, string);
  }
}

/*
=================
SV_BroadcastCommand

Sends text to all active clients
=================
*/
void SV_BroadcastCommand (char *fmt, ...)
{
  va_list   argptr;
  char    string[1024];
  
  if (!sv.state)
    return;
  va_start (argptr,fmt);
  vsprintf (string, fmt,argptr);
  va_end (argptr);

  MSG_WriteByte (&sv.reliable_datagram, svc_stufftext);
  MSG_WriteString (&sv.reliable_datagram, string);
}


/*
=================
SV_Multicast

Sends the contents of sv.multicast to a subset of the clients,
then clears sv.multicast.

MULTICAST_ALL same as broadcast
MULTICAST_PVS send to clients potentially visible from org
MULTICAST_PHS send to clients potentially hearable from org
=================
*/
void SV_Multicast (vec3_t origin, int to)
{
  client_t  *client;
  byte    *mask;
  mleaf_t   *leaf;
  int     leafnum;
  int     j;
  qboolean  reliable;

  leaf = Mod_PointInLeaf (origin, sv.worldmodel);
  if (!leaf)
    leafnum = 0;
  else
    leafnum = leaf - sv.worldmodel->leafs;

  reliable = false;

  switch (to)
  {
  case MULTICAST_ALL_R:
    reliable = true;  // intentional fallthrough
  case MULTICAST_ALL:
    mask = sv.pvs;    // leaf 0 is everything;
    break;

  case MULTICAST_PHS_R:
    reliable = true;  // intentional fallthrough
  case MULTICAST_PHS:
    mask = sv.phs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5);
    break;

  case MULTICAST_PVS_R:
    reliable = true;  // intentional fallthrough
  case MULTICAST_PVS:
    mask = sv.pvs + leafnum * 4*((sv.worldmodel->numleafs+31)>>5);
    break;

  default:
    mask = NULL;
    SV_Error ("SV_Multicast: bad to:%i", to);
  }

  // send the data to all relevent clients
  for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++)
  {
    if (client->state != cs_spawned)
      continue;

    if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) {
      vec3_t delta;
      VectorSubtract(origin, client->edict->v.origin, delta);
      if (Length(delta) <= 1024)
        goto inrange;
    }

    leaf = Mod_PointInLeaf (client->edict->v.origin, sv.worldmodel);
    if (leaf)
    {
      // -1 is because pvs rows are 1 based, not 0 based like leafs
      leafnum = leaf - sv.worldmodel->leafs - 1;
      if ( !(mask[leafnum>>3] & (1<<(leafnum&7)) ) )
      {
//        Con_Printf ("supressed multicast\n");
        continue;
      }
    }

inrange:
    if (reliable) {
      ClientReliableCheckBlock(client, sv.multicast.cursize);
      ClientReliableWrite_SZ(client, sv.multicast.data, sv.multicast.cursize);
    } else
      SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize);
  }

  SZ_Clear (&sv.multicast);
}


/*  
==================
SV_StartSound

Each entity can have eight independant sound sources, like voice,
weapon, feet, etc.

Channel 0 is an auto-allocate channel, the others override anything
allready running on that entity/channel pair.

An attenuation of 0 will play full volume everywhere in the level.
Larger attenuations will drop off.  (max 4 attenuation)

==================
*/  
void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
    float attenuation)
{       
    int         sound_num;
    int     field_mask;
    int     i;
  int     ent;
  vec3_t    origin;
  qboolean  use_phs;
  qboolean  reliable = false;

  if (volume < 0 || volume > 255)
    SV_Error ("SV_StartSound: volume = %i", volume);

  if (attenuation < 0 || attenuation > 4)
    SV_Error ("SV_StartSound: attenuation = %f", attenuation);

  if (channel < 0 || channel > 15)
    SV_Error ("SV_StartSound: channel = %i", channel);

// find precache number for sound
    for (sound_num=1 ; sound_num<MAX_SOUNDS
        && sv.sound_precache[sound_num] ; sound_num++)
        if (!strcmp(sample, sv.sound_precache[sound_num]))
            break;
    
    if ( sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num] )
    {
        Con_Printf ("SV_StartSound: %s not precacheed\n", sample);
        return;
    }
    
  ent = NUM_FOR_EDICT(entity);

  if ((channel & 8) || !sv_phs.value) // no PHS flag
  {
    if (channel & 8)
      reliable = true; // sounds that break the phs are reliable
    use_phs = false;
    channel &= 7;
  }
  else
    use_phs = true;

//  if (channel == CHAN_BODY || channel == CHAN_VOICE)
//    reliable = true;

  channel = (ent<<3) | channel;

  field_mask = 0;
  if (volume != DEFAULT_SOUND_PACKET_VOLUME)
    channel |= SND_VOLUME;
  if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
    channel |= SND_ATTENUATION;

  // use the entity origin unless it is a bmodel
  if (entity->v.solid == SOLID_BSP)
  {
    for (i=0 ; i<3 ; i++)
      origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]);
  }
  else
  {
    VectorCopy (entity->v.origin, origin);
  }

  MSG_WriteByte (&sv.multicast, svc_sound);
  MSG_WriteShort (&sv.multicast, channel);
  if (channel & SND_VOLUME)
    MSG_WriteByte (&sv.multicast, volume);
  if (channel & SND_ATTENUATION)
    MSG_WriteByte (&sv.multicast, attenuation*64);
  MSG_WriteByte (&sv.multicast, sound_num);
  for (i=0 ; i<3 ; i++)
    MSG_WriteCoord (&sv.multicast, origin[i]);

  if (use_phs)
    SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS);
  else
    SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL);
}           


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

FRAME UPDATES

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

int   sv_nailmodel, sv_supernailmodel, sv_playermodel;

void SV_FindModelNumbers (void)
{
  int   i;

  sv_nailmodel = -1;
  sv_supernailmodel = -1;
  sv_playermodel = -1;

  for (i=0 ; i<MAX_MODELS ; i++)
  {
    if (!sv.model_precache[i])
      break;
    if (!strcmp(sv.model_precache[i],"progs/spike.mdl"))
      sv_nailmodel = i;
    if (!strcmp(sv.model_precache[i],"progs/s_spike.mdl"))
      sv_supernailmodel = i;
    if (!strcmp(sv.model_precache[i],"progs/player.mdl"))
      sv_playermodel = i;
  }
}


/*
==================
SV_WriteClientdataToMessage

==================
*/
void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
{
  int   i;
  edict_t *other;
  edict_t *ent;

  ent = client->edict;

  // send the chokecount for r_netgraph
  if (client->chokecount)
  {
    MSG_WriteByte (msg, svc_chokecount);
    MSG_WriteByte (msg, client->chokecount);
    client->chokecount = 0;
  }

  // send a damage message if the player got hit this frame
  if (ent->v.dmg_take || ent->v.dmg_save)
  {
    other = PROG_TO_EDICT(ent->v.dmg_inflictor);
    MSG_WriteByte (msg, svc_damage);
    MSG_WriteByte (msg, ent->v.dmg_save);
    MSG_WriteByte (msg, ent->v.dmg_take);
    for (i=0 ; i<3 ; i++)
      MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
  
    ent->v.dmg_take = 0;
    ent->v.dmg_save = 0;
  }

  // a fixangle might get lost in a dropped packet.  Oh well.
  if ( ent->v.fixangle )
  {
    MSG_WriteByte (msg, svc_setangle);
    for (i=0 ; i < 3 ; i++)
      MSG_WriteAngle (msg, ent->v.angles[i] );
    ent->v.fixangle = 0;
  }
}

/*
=======================
SV_UpdateClientStats

Performs a delta update of the stats array.  This should only be performed
when a reliable message can be delivered this frame.
=======================
*/
void SV_UpdateClientStats (client_t *client)
{
  edict_t *ent;
  int   stats[MAX_CL_STATS];
  int   i;
  
  ent = client->edict;
  memset (stats, 0, sizeof(stats));
  
  // if we are a spectator and we are tracking a player, we get his stats
  // so our status bar reflects his
  if (client->spectator && client->spec_track > 0)
    ent = svs.clients[client->spec_track - 1].edict;

  stats[STAT_HEALTH] = ent->v.health;
  stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel));
  stats[STAT_AMMO] = ent->v.currentammo;
  stats[STAT_ARMOR] = ent->v.armorvalue;
  stats[STAT_SHELLS] = ent->v.ammo_shells;
  stats[STAT_NAILS] = ent->v.ammo_nails;
  stats[STAT_ROCKETS] = ent->v.ammo_rockets;
  stats[STAT_CELLS] = ent->v.ammo_cells;
  if (!client->spectator)
    stats[STAT_ACTIVEWEAPON] = ent->v.weapon;
  // stuff the sigil bits into the high bits of items for sbar
  stats[STAT_ITEMS] = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28);

  for (i=0 ; i<MAX_CL_STATS ; i++)
    if (stats[i] != client->stats[i])
    {
      client->stats[i] = stats[i];
      if (stats[i] >=0 && stats[i] <= 255)
      {
        ClientReliableWrite_Begin(client, svc_updatestat, 3);
        ClientReliableWrite_Byte(client, i);
        ClientReliableWrite_Byte(client, stats[i]);
      }
      else
      {
        ClientReliableWrite_Begin(client, svc_updatestatlong, 6);
        ClientReliableWrite_Byte(client, i);
        ClientReliableWrite_Long(client, stats[i]);
      }
    }
}

/*
=======================
SV_SendClientDatagram
=======================
*/
qboolean SV_SendClientDatagram (client_t *client)
{
  byte    buf[MAX_DATAGRAM];
  sizebuf_t msg;

  msg.data = buf;
  msg.maxsize = sizeof(buf);
  msg.cursize = 0;
  msg.allowoverflow = true;
  msg.overflowed = false;

  // add the client specific data to the datagram
  SV_WriteClientdataToMessage (client, &msg);

  // send over all the objects that are in the PVS
  // this will include clients, a packetentities, and
  // possibly a nails update
  SV_WriteEntitiesToClient (client, &msg);

  // copy the accumulated multicast datagram
  // for this client out to the message
  if (client->datagram.overflowed)
    Con_Printf ("WARNING: datagram overflowed for %s\n", client->name);
  else
    SZ_Write (&msg, client->datagram.data, client->datagram.cursize);
  SZ_Clear (&client->datagram);

  // send deltas over reliable stream
  if (Netchan_CanReliable (&client->netchan))
    SV_UpdateClientStats (client);

  if (msg.overflowed)
  {
    Con_Printf ("WARNING: msg overflowed for %s\n", client->name);
    SZ_Clear (&msg);
  }

  // send the datagram
  Netchan_Transmit (&client->netchan, msg.cursize, buf);

  return true;
}

/*
=======================
SV_UpdateToReliableMessages
=======================
*/
void SV_UpdateToReliableMessages (void)
{
  int     i, j;
  client_t *client;
  eval_t *val;
  edict_t *ent;

// check for changes to be sent over the reliable streams to all clients
  for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++)
  {
    if (host_client->state != cs_spawned)
      continue;
    if (host_client->sendinfo)
    {
      host_client->sendinfo = false;
      SV_FullClientUpdate (host_client, &sv.reliable_datagram);
    }
    if (host_client->old_frags != host_client->edict->v.frags)
    {
      for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++)
      {
        if (client->state < cs_connected)
          continue;
        ClientReliableWrite_Begin(client, svc_updatefrags, 4);
        ClientReliableWrite_Byte(client, i);
        ClientReliableWrite_Short(client, host_client->edict->v.frags);
      }

      host_client->old_frags = host_client->edict->v.frags;
    }

    // maxspeed/entgravity changes
    ent = host_client->edict;

    val = GetEdictFieldValue(ent, "gravity");
    if (val && host_client->entgravity != val->_float) {
      host_client->entgravity = val->_float;
      ClientReliableWrite_Begin(host_client, svc_entgravity, 5);
      ClientReliableWrite_Float(host_client, host_client->entgravity);
    }
    val = GetEdictFieldValue(ent, "maxspeed");
    if (val && host_client->maxspeed != val->_float) {
      host_client->maxspeed = val->_float;
      ClientReliableWrite_Begin(host_client, svc_maxspeed, 5);
      ClientReliableWrite_Float(host_client, host_client->maxspeed);
    }

  }

  if (sv.datagram.overflowed)
    SZ_Clear (&sv.datagram);

  // append the broadcast messages to each client messages
  for (j=0, client = svs.clients ; j<MAX_CLIENTS ; j++, client++)
  {
    if (client->state < cs_connected)
      continue; // reliables go to all connected or spawned

    ClientReliableCheckBlock(client, sv.reliable_datagram.cursize);
    ClientReliableWrite_SZ(client, sv.reliable_datagram.data, sv.reliable_datagram.cursize);

    if (client->state != cs_spawned)
      continue; // datagrams only go to spawned
    SZ_Write (&client->datagram
      , sv.datagram.data
      , sv.datagram.cursize);
  }

  SZ_Clear (&sv.reliable_datagram);
  SZ_Clear (&sv.datagram);
}

#ifdef _WIN32
#pragma optimize( "", off )
#endif



/*
=======================
SV_SendClientMessages
=======================
*/
void SV_SendClientMessages (void)
{
  int     i, j;
  client_t  *c;

// update frags, names, etc
  SV_UpdateToReliableMessages ();

// build individual updates
  for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++)
  {
    if (!c->state)
      continue;

    if (c->drop) {
      SV_DropClient(c);
      c->drop = false;
      continue;
    }

    // check to see if we have a backbuf to stick in the reliable
    if (c->num_backbuf) {
      // will it fit?
      if (c->netchan.message.cursize + c->backbuf_size[0] <
        c->netchan.message.maxsize) {

        Con_DPrintf("%s: backbuf %d bytes\n",
          c->name, c->backbuf_size[0]);

        // it'll fit
        SZ_Write(&c->netchan.message, c->backbuf_data[0],
          c->backbuf_size[0]);
        
        //move along, move along
        for (j = 1; j < c->num_backbuf; j++) {
          memcpy(c->backbuf_data[j - 1], c->backbuf_data[j],
            c->backbuf_size[j]);
          c->backbuf_size[j - 1] = c->backbuf_size[j];
        }

        c->num_backbuf--;
        if (c->num_backbuf) {
          memset(&c->backbuf, 0, sizeof(c->backbuf));
          c->backbuf.data = c->backbuf_data[c->num_backbuf - 1];
          c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1];
          c->backbuf.maxsize = sizeof(c->backbuf_data[c->num_backbuf - 1]);
        }
      }
    }

    // if the reliable message overflowed,
    // drop the client
    if (c->netchan.message.overflowed)
    {
      SZ_Clear (&c->netchan.message);
      SZ_Clear (&c->datagram);
      SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name);
      Con_Printf ("WARNING: reliable overflow for %s\n",c->name);
      SV_DropClient (c);
      c->send_message = true;
      c->netchan.cleartime = 0; // don't choke this message
    }

    // only send messages if the client has sent one
    // and the bandwidth is not choked
    if (!c->send_message)
      continue;
    c->send_message = false;  // try putting this after choke?
    if (!sv.paused && !Netchan_CanPacket (&c->netchan))
    {
      c->chokecount++;
      continue;   // bandwidth choke
    }

    if (c->state == cs_spawned)
      SV_SendClientDatagram (c);
    else
      Netchan_Transmit (&c->netchan, 0, NULL);  // just update reliable
      
  }
}

#ifdef _WIN32
#pragma optimize( "", on )
#endif



/*
=======================
SV_SendMessagesToAll

FIXME: does this sequence right?
=======================
*/
void SV_SendMessagesToAll (void)
{
  int     i;
  client_t  *c;

  for (i=0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++)
    if (c->state)   // FIXME: should this only send to active?
      c->send_message = true;
  
  SV_SendClientMessages ();
}

