/*
Copyright (C) 2003-2006 Andrey Nazarov

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.

*/

#define MVD_REC_NUM_FRAMES	2
#define MVD_REC_FRAMES_MASK ( MVD_REC_NUM_FRAMES - 1 )

typedef enum {
	MVD_UNINITIALIZED,
	MVD_CHALLENGING,
	MVD_CONNECTING,
	MVD_CONNECTED,
	MVD_PRIMED,
	MVD_BUFFERING,
	MVD_ACTIVE
} mvdState_t;

#define LAYOUT_MSEC		3000
#define LAYOUT_TIMEOUT	90		/* in frames */

typedef enum {
	SBOARD_NONE,		/* no layout at all */
	SBOARD_FOLLOW,		/* layout of player being followed */
	SBOARD_CLIENTS,		/* MVD clients list */
	SBOARD_SCORES		/* current scores */
} scoreboard_t;

#define FLOOD_SAMPLES	16
#define	FLOOD_MASK		( FLOOD_SAMPLES - 1 )

typedef struct {
    player_state_t ps;
    int ping;
    int clientNum;
/* =================== */

    qboolean admin;
	qboolean following;
	qboolean savedFollowing;
	qboolean connected;
    int lastServerPacket;
	int followClientNum;
	int savedClientNum;
	int pmflags;
	client_t *cl;
	float fov;
	scoreboard_t scoreboard;
	int layoutTime;
	int floodSamples[FLOOD_SAMPLES];
	int floodHead;
	int floodTime;
	usercmd_t lastcmd;
	short delta_angles[3];
} mvdClient_t;

typedef struct {
	char layout[MAX_STRING_CHARS];			/* private layout */
	char *configstrings[MAX_CONFIGSTRINGS];	/* private configstrings */
} mvdPlayer_t;

typedef enum {
	MFF_ACTIVE			= ( 1 << 0 ),
	MFF_VALID			= ( 1 << 1 ),
	MFF_GAMESTATE		= ( 1 << 2 ),
	MFF_RELIABLE		= ( 1 << 3 )
} mvdFrameFlags_t;

typedef struct {
	mvdFrameFlags_t	flags;

	int				serverFrame;
	int				deltaPacketNum;
	int				serverPacketNum;
	int				gamestateSequence;

	byte			portalbytes[MAX_MAP_AREAS/8];
	int				numPortalBytes;

	/* indices into circular buffers */
	int				firstEntityState;
	int				numEntityStates;

	int				firstPlayerState;
	int				numPlayerStates;
	
	int				firstMessageByte;
	int				numMessageBytes;

	int				firstReliableMessageByte;
	int				numReliableMessageBytes;
} mvdFrame_t;

#define MAX_GAMESTATES	16
#define GAMESTATE_MASK	( MAX_GAMESTATES - 1 )

typedef struct mvdConfigstring_s {
	struct mvdConfigstring_s *next;
	int index;
	char string[1];
} mvdConfigstring_t;

typedef struct {
	entity_state_t s;
	qboolean	linked;
	int			num_clusters;
	int			clusternums[MAX_ENT_CLUSTERS];
	cnode_t		*headnode;
	int			areanum, areanum2;
} entityStateEx_t;

typedef struct {
	int serverPacketNum;
	char mapname[MAX_QPATH];
	char fullname[MAX_QPATH];
	int maxclients;
	cm_t cm;
	vec3_t spawnOrigin;
	vec3_t spawnAngles;
	qboolean spawnSet;
	mvdConfigstring_t *headCS, *tailCS;
	entityStateEx_t *baselines[SV_BASELINES_CHUNKS];
} mvdGamestate_t;

typedef struct mvd_s {
	mvdState_t		state;
	int				realtime;

	/* demo related variables */
	fileHandle_t	demofile;
	qboolean	demoplayback;
	qboolean	demorecording;
	qboolean	demowaiting;
	qboolean	demobackwards;
	int			demofileSize;
	int			demofileFrameOffset;
	int         demofilePercent;
	char		demopath[MAX_QPATH];

	/* connection variables */
	netadr_t	serverAddress;
	int			serverProtocol;
	int			connectCount;
	int			connectTime;
	int			challenge;
	int			quakePort;
	netchan_t	*netchan;
	int			servercount;
	int			sendtime;
	int			timeoutcount;
	int			clientNum;
    usercmd_t   cmd;

	/* delta decoder variables */
	int		        maxEntityStates;
	int		        nextEntityStates;
	entityStateEx_t	*entityStates;  /* [maxEntityStates] */
    
	int		        maxPlayerStates;
	int		        nextPlayerStates;
	playerStateEx_t	*playerStates;  /* [maxPlayerStates] */
	
	int			lastServerFrame;		/* last valid frame number received */
	mvdFrame_t	*frames;		        /* [frameBackup] */
	int			frameBackup;
	int			packetNums[UPDATE_BACKUP]; /* for converting from serverFrames
                                              to packetNums */

	int		serverPacketNum;	/* number of the last packet recevied */
	int		validPacketNum;		/* number of the last packet with a valid frame */
	int		activePacketNum;    /* frame displayed to clients */
    int     prevPacketNum;      /* previous frame displayed */

	int		maxMessageBytes;
	int		nextMessageBytes;
	byte	*messageBytes;		/* [maxMessageBytes] */

	int		maxReliableMessageBytes;
	int		nextReliableMessageBytes;
	byte	*reliableMessageBytes;		/* [maxReliableMessageBytes] */

	int		lastReliableMessageBytes;

	mvdGamestate_t gamestates[MAX_GAMESTATES];
	int		activeGamestateSequence;
	int		gamestateSequence;
	mvdGamestate_t *nextGS;		/* partial gamestate being currently parsed */

	/* game data */
	char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; /* latest configstrings */
	mvdClient_t *clients;	/* [svs.maxclients] */

	/* derived data */
	mvdPlayer_t *players;	/* [maxPlayers] */
	int		maxPlayers;
	int		lastLayoutPacket;
} mvd_t;

typedef struct {
	listElem_t	elem;
	int			sequence;
	int			size;
	int			numFrames;
	time_t		time;
	qboolean	completed;
	char		*filename;
	char		*fullpath;
} mvdCacheEntry_t;

extern list_t		rec_cache;

extern mvd_t			mvd;

extern cmdbuf_t	mvd_buffer;
extern byte		mvd_buffer_text[MAX_STRING_CHARS];

extern	game_export_t	mvd_ge;

extern cvar_t	*mvd_nodelta;
extern cvar_t	*mvd_wait_clients;
extern cvar_t	*mvd_cache_megs;
extern cvar_t	*mvd_shownet;
extern cvar_t	*mvd_buffer_size;
extern cvar_t	*mvd_debug;
extern cvar_t	*mvd_buffer_size;
extern cvar_t	*mvd_pause;
extern cvar_t	*mvd_noblend;
extern cvar_t	*mvd_nogun;
extern cvar_t	*mvd_motd;
extern cvar_t	*mvd_custom_fov;
extern cvar_t	*mvd_nextserver;
extern cvar_t	*mvd_timeout;
extern cvar_t	*mvd_admin_password;
extern cvar_t	*mvd_flood_msgs;
extern cvar_t	*mvd_flood_persecond;
extern cvar_t	*mvd_flood_waitdelay;
extern cvar_t	*mvd_autoscores;
extern cvar_t	*mvd_safecmd;

#define MVD_Mallocz( size )     Z_TagMallocz( size, TAG_MVD )
#define MVD_CopyString( s )		Z_TagCopyString( s, TAG_MVD )

qboolean MVD_PlayerIsActive( int playerNum );

const char *MVD_Play_g( const char *partial, int state );
void MVD_Play_f( void );
void MVD_Connect_f( void );
void MVD_Disconnect_f( void );
void MVD_Disconnect( void );

qboolean MVD_ReadNextMessage( fileHandle_t f );
qboolean MVD_ParseNextMessage( void );
void MVD_ParseMessage( void );

void MVD_ClientCommand( const char *string );

void MVD_ForwardToServer_f( void );

void MVD_StreamedStop_f( void );
void MVD_StreamedRecord_f( void );

void MVD_BackwardsDown_f( void );
void MVD_BackwardsUp_f( void );

void MVD_Jump_f( void );

void MVD_Register( void );
void MVD_RecBeginFrame( void );
void MVD_RecEndFrame( void );
void MVD_RecStop( void );
void MVD_RecMulticast( const vec3_t origin, multicast_t to );
void MVD_RecUnicast( int clientNum );
void MVD_RecConfigstring( int index, const char *string );
void MVD_ListCache( qboolean fromConsole );

void MVD_Multicast( qboolean unbuffered, vec3_t origin, multicast_t to );
void MVD_TransitionGamestate( int sequence );
void MVD_GameSpawnEntities( char *mapname, char *entstring, char *spawnpoint );

void MVD_ExecuteString( const char *line );

void MVD_ClientFrame( int msec );

void MVD_UpdateFollower( mvdClient_t *client, playerStateEx_t *src );



