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

#include "../qcommon/qcommon.h"
#include "../game/game.h"

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

#define	MAX_MASTERS	8	/* max recipients for heartbeat packets */

typedef enum {
	ss_dead,		/* no map loaded */
	ss_loading,		/* spawning level edicts */
	ss_game,		/* actively running */
	ss_cinematic,
	ss_demo,
	ss_pic
} server_state_t;

/* some qc commands are only valid before the server has finished */
/* initializing (precache commands, static sounds / objects, etc) */

typedef struct {
	server_state_t	state;	/* precache commands are only valid during load */

	qboolean	attractloop;	/* running cinematics and demos for the local system only */
	qboolean	loadgame;	/* client begins should reuse existing entity */

	unsigned	time;	/* always sv.framenum * 100 msec */
	int		framenum;

	char		name[MAX_QPATH];	/* map name, or cinematic name */
	struct cmodel_s *models[MAX_MODELS];

	char		configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];
	entity_state_t	baselines[MAX_EDICTS];

	/* the multicast buffer is used to send a message to a set of clients */
	/* it is only used to marshall data until SV_Multicast is called */
	sizebuf_t	multicast;
	byte		multicast_buf[MAX_MSGLEN];

	/* demo server information */
	fileHandle_t	demofile;
	qboolean	timedemo;	/* don't time sync */
} server_t;

#define EDICT_NUM(n) ((edict_t *)((byte *)ge->edicts + ge->edict_size*(n)))
#define NUM_FOR_EDICT(e) ( ((byte *)(e)-(byte *)ge->edicts ) / ge->edict_size)


typedef enum {
	cs_free,		/* can be reused for a new connection */
	cs_zombie,		/* client has been disconnected, but don't reuse */
				/* connection for a couple seconds */
	cs_connected,		/* has been assigned to a client_t, but not in game yet */
	cs_spawned		/* client is fully in game */
} client_state_t;

typedef struct {
	int		areabytes;
	byte		areabits  [MAX_MAP_AREAS / 8];	/* portalarea visibility bits */
	player_state_t	ps;
	int		num_entities;
	int		first_entity;	/* into the circular sv_packet_entities[] */
	int		senttime;	/* for ping calculations */
} client_frame_t;

#define	LATENCY_COUNTS	16
#define	RATE_MESSAGES	10

typedef struct client_s {
	client_state_t	state;

	char		userinfo  [MAX_INFO_STRING];	/* name, etc */

	int		lastframe;	/* for delta compression */
	usercmd_t	lastcmd;/* for filling in big drops */

	int		commandMsec;	/* every seconds this is reset, if user */
	/* commands exhaust it, assume time cheating */

	int		frame_latency[LATENCY_COUNTS];
	int		ping;

	int		message_size[RATE_MESSAGES];	/* used to rate drop packets */
	int		rate;
	int		surpressCount;	/* number of messages rate supressed */

	edict_t        *edict;		/* EDICT_NUM(clientnum+1) */
	char		name      [32];	/* extracted from userinfo, high bits masked */
	int		messagelevel;	/* for filtering printed messages */

	/* The datagram is written to by sound calls, prints, temp ents, etc. */
	/* It can be harmlessly overflowed. */
	sizebuf_t	datagram;
	byte		datagram_buf[MAX_MSGLEN];

	client_frame_t	frames[UPDATE_BACKUP];	/* updates can be delta'd from here */

	byte           *download;	/* file being downloaded */
	int		downloadsize;	/* total bytes (can't use EOF because of paks) */
	int		downloadcount;	/* bytes sent */

	int		lastmessage;	/* sv.framenum when packet was last received */
	int		lastconnect;

	int		challenge;	/* challenge of this user, randomly generated */

	netchan_t	netchan;
} client_t;

/* a client can leave the server in one of four ways: */
/* dropping properly by quiting or disconnecting */
/* timing out if no valid messages are received for timeout.value seconds */
/* getting kicked off by the server operator */
/* a program error, like an overflowed reliable buffer */

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

/* MAX_CHALLENGES is made large to prevent a denial */
/* of service attack that could cycle all of them */
/* out before legitimate users connected */
#define	MAX_CHALLENGES	1024

typedef struct {
	netadr_t	adr;
	int		challenge;
	int		time;
} challenge_t;


typedef struct {
	qboolean	initialized;	/* sv_init has completed */
	int		realtime;	/* always increasing, no clamping, etc */

	char		mapcmd[MAX_TOKEN_CHARS];	/* ie: *intro.cin+base  */

	int		spawncount;	/* incremented each server start */
	
	/* used to check late spawns */
	client_t       *clients;		/* [maxclients->value]; */
	int		num_client_entities;	/* maxclients->value*UPDATE_BA
						 * CKUP*MAX_PACKET_ENTITIES */
	int		next_client_entities;	/* next client_entity to use */
	entity_state_t *client_entities;	/* [num_client_entities] */

	int		last_heartbeat;

	challenge_t	challenges[MAX_CHALLENGES];	/* to prevent invalid
							 * IPs from connecting */

	/* serverrecord values */
	FILE           *demofile;
	sizebuf_t	demo_multicast;
	byte		demo_multicast_buf[MAX_MSGLEN];
} server_static_t;

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

extern netadr_t	master_adr[MAX_MASTERS];	/* address of the master server */

extern server_static_t svs;	/* persistant server info */
extern server_t	sv;		/* local server */

extern cvar_t  *sv_paused;
extern cvar_t  *maxclients;
extern cvar_t  *sv_noreload;	/* don't reload level state when reentering */
extern cvar_t  *sv_airaccelerate;	/* don't reload level state when reentering */

/* development tool */
extern cvar_t  *sv_enforcetime;

extern client_t *sv_client;
extern edict_t *sv_player;

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

//
/* sv_main.c */
//

extern cvar_t	*sv_killserver;

void		SV_FinalMessage(char *message, qboolean reconnect);
void		SV_DropClient(client_t * drop);

int		SV_ModelIndex(char *name);
int		SV_SoundIndex(char *name);
int		SV_ImageIndex(char *name);

void		SV_WriteClientdataToMessage(client_t * client, sizebuf_t * msg);

void		SV_ExecuteUserCommand(char *s);
void		SV_InitOperatorCommands(void);

void		SV_SendServerinfo(client_t * client);
void		SV_UserinfoChanged(client_t * cl);


void		Master_Heartbeat(void);
void		Master_Packet(void);

//
/* sv_init.c */
//
void		SV_InitGame(void);
void		SV_Map    (qboolean attractloop, char *levelstring, qboolean loadgame);


//
/* sv_phys.c */
//
void		SV_PrepWorldFrame(void);

//
/* sv_send.c */
//
typedef enum {
	RD_NONE, RD_CLIENT, RD_PACKET
} redirect_t;

#define	SV_OUTPUTBUF_LENGTH	(MAX_MSGLEN - 16)

extern char	sv_outputbuf[SV_OUTPUTBUF_LENGTH];

void		SV_FlushRedirect(int sv_redirected, char *outputbuf);

void		SV_DemoCompleted(void);
void		SV_SendClientMessages(void);

void		SV_Multicast(vec3_t origin, multicast_t to);
void
SV_StartSound(vec3_t origin, edict_t * entity, int channel,
    int soundindex, float volume,
    float attenuation, float timeofs);
void		SV_ClientPrintf(client_t * cl, int level, char *fmt,...);
void		SV_BroadcastPrintf(int level, char *fmt,...);
void		SV_BroadcastCommand(char *fmt,...);

/* sv_user.c */
extern cvar_t  *allow_download;
extern cvar_t  *allow_download_players;
extern cvar_t  *allow_download_models;
extern cvar_t  *allow_download_sounds;
extern cvar_t  *allow_download_maps;

void		SV_Nextserver(void);
void		SV_ExecuteClientMessage(client_t * cl);

/* sv_ccmds.c */
void		SV_ReadLevelFile(void);
void		SV_Status_f(void);

/* sv_ents.c */
void		SV_WriteFrameToClient(client_t * client, sizebuf_t * msg);
void		SV_RecordDemoMessage(void);
void		SV_BuildClientFrame(client_t * client);
void		SV_Error  (char *error,...);

/* sv_game.c */
extern game_export_t *ge;

void		SV_InitGameProgs(void);
void		SV_ShutdownGameProgs(void);
void		SV_InitEdict(edict_t * e);



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

//
/* high level object sorting to reduce interaction tests */
//

void		SV_ClearWorld(void);

/* called after the world model has been loaded, before linking any entities */

void		SV_UnlinkEdict(edict_t * ent);

/* call before removing an entity, and before trying to move one, */
/* so it doesn't clip against itself */

void		SV_LinkEdict(edict_t * ent);

/* Needs to be called any time an entity changes origin, mins, maxs, */
/* or solid.  Automatically unlinks if needed. */
/* sets ent->v.absmin and ent->v.absmax */
/* sets ent->leafnums[] for pvs determination even if the entity */
/* is not solid */

int		SV_AreaEdicts(vec3_t mins, vec3_t maxs, edict_t ** list, int maxcount, int areatype);

/* fills in a table of edict pointers with edicts that have bounding boxes */
/* that intersect the given area.  It is possible for a non-axial bmodel */
/* to be returned that doesn't actually intersect the area on an exact */
/* test. */
/* returns the number of pointers filled in */
/* ??? does this always return the world? */

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

//
/* functions that interact with everything apropriate */
//
int		SV_PointContents(vec3_t p);

/* returns the CONTENTS_* value from the world at the given point. */
/* Quake 2 extends this to also check entities, to allow moving liquids */


trace_t		SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t * passedict, int contentmask);

/* mins and maxs are relative */

/* if the entire move stays in a solid volume, trace.allsolid will be set, */
/* trace.startsolid will be set, and trace.fraction will be 0 */

/* if the starting point is in a solid, it will be allowed to move out */
/* to an open area */

/* passedict is explicitly excluded from clipping checks (normally NULL) */
