/*
netchan.h - net channel abstraction layer
Copyright (C) 2007 Uncle Mike

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

#ifndef NET_MSG_H
#define NET_MSG_H

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

  ELEMENTS COMMUNICATED ACROSS THE NET

==========================================================
*/
#include "net_buffer.h"

// 0 == regular, 1 == file stream
#define MAX_STREAMS			2    

// flow control bytes per second limits
#define MAX_RATE			100000.0f				
#define MIN_RATE			1000.0f

// default data rate
#define DEFAULT_RATE		(9999.0f)

// NETWORKING INFO

// This is the packet payload without any header bytes (which are attached for actual sending)
#define NET_MAX_PAYLOAD		MAX_INIT_MSG

// Theoretically maximum size of UDP-packet without header and hardware-specific data
#define NET_MAX_FRAGMENT		65536

// because encoded as highpart of uint32
#define NET_MAX_BUFFER_ID		32767

// because encoded as lowpart of uint32
#define NET_MAX_BUFFERS_COUNT		32767

// This is the payload plus any header info (excluding UDP header)

// Packet header is:
//  4 bytes of outgoing seq
//  4 bytes of incoming seq
//  and for each stream
// {
//  byte (on/off)
//  int (fragment id)
//  int (startpos)
//  int (length)
// }
#define HEADER_BYTES		( 8 + MAX_STREAMS * 13 )

// Pad this to next higher 16 byte boundary
// This is the largest packet that can come in/out over the wire, before processing the header
//  bytes will be stripped by the networking channel layer
#define NET_MAX_MESSAGE		PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 )

#define MASTERSERVER_ADR		"ms.xash.su:27010"
#define MS_SCAN_REQUEST		"1\xFF" "0.0.0.0:0\0"
#define PORT_MASTER			27010
#define PORT_CLIENT			27005
#define PORT_SERVER			27015

#define MULTIPLAYER_BACKUP		64	// how many data slots to use when in multiplayer (must be power of 2)
#define SINGLEPLAYER_BACKUP		16	// same for single player  
#define CMD_BACKUP			64	// allow a lot of command backups for very fast systems
#define CMD_MASK			(CMD_BACKUP - 1)
#define NUM_PACKET_ENTITIES		256	// 170 Mb for multiplayer with 32 players
#define MAX_CUSTOM_BASELINES		64

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

NET

==============================================================
*/
#define MAX_FLOWS			2

#define FLOW_OUTGOING		0
#define FLOW_INCOMING		1
#define MAX_LATENT			32
#define MASK_LATENT			( MAX_LATENT - 1 )

#define FRAG_NORMAL_STREAM		0
#define FRAG_FILE_STREAM		1

// message data
typedef struct
{
	int		size;		// size of message sent/received
	double		time;		// time that message was sent/received
} flowstats_t;

typedef struct
{
	flowstats_t	stats[MAX_LATENT];	// data for last MAX_LATENT messages
	int		current;		// current message position
	double		nextcompute; 	// time when we should recompute k/sec data
	float		kbytespersec;	// average data
	float		avgkbytespersec;
	int		totalbytes;
} flow_t;

// generic fragment structure
typedef struct fragbuf_s
{
	struct fragbuf_s	*next;				// next buffer in chain
	int		bufferid;				// id of this buffer
	sizebuf_t		frag_message;			// message buffer where raw data is stored
	byte		frag_message_buf[NET_MAX_FRAGMENT];	// the actual data sits here
	qboolean		isfile;				// is this a file buffer?
	qboolean		isbuffer;				// is this file buffer from memory ( custom decal, etc. ).
	qboolean		iscompressed;			// is compressed file, we should using filename.ztmp
	char		filename[MAX_OSPATH];		// name of the file to save out on remote host
	int		foffset;				// offset in file from which to read data  
	int		size;				// size of data to read at that offset
} fragbuf_t;

// Waiting list of fragbuf chains
typedef struct fbufqueue_s
{
	struct fbufqueue_s	*next;		// next chain in waiting list
	int		fragbufcount;	// number of buffers in this chain
	fragbuf_t		*fragbufs;	// the actual buffers
} fragbufwaiting_t;

// Network Connection Channel
typedef struct netchan_s
{
	netsrc_t		sock;		// NS_SERVER or NS_CLIENT, depending on channel.
	netadr_t		remote_address;	// address this channel is talking to.  
	int		qport;		// qport value to write when transmitting

	double		last_received;	// for timeouts
	double		connect_time;	// Usage: host.realtime - netchan.connect_time
	double		rate;		// bandwidth choke. bytes per second
	double		cleartime;	// if realtime > cleartime, free to send next packet

	// Sequencing variables
	int		incoming_sequence;			// increasing count of sequence numbers               
	int		incoming_acknowledged;		// # of last outgoing message that has been ack'd.          
	int		incoming_reliable_acknowledged;	// toggles T/F as reliable messages are received.	
	int		incoming_reliable_sequence;		// single bit, maintained local	    
	int		outgoing_sequence;			// message we are sending to remote              
	int		reliable_sequence;			// whether the message contains reliable payload, single bit
	int		last_reliable_sequence;		// outgoing sequence number of last send that had reliable data

	// callback to get actual framgment size
	void		*client;
	int (*pfnBlockSize)( void *cl );

	// staging and holding areas
	sizebuf_t		message;
	byte		message_buf[NET_MAX_MESSAGE];

	// reliable message buffer.
	// we keep adding to it until reliable is acknowledged.  Then we clear it.
	int		reliable_length;
	byte		reliable_buf[NET_MAX_MESSAGE];	// unacked reliable message (max size for loopback connection)

	// Waiting list of buffered fragments to go onto queue.
	// Multiple outgoing buffers can be queued in succession
	fragbufwaiting_t	*waitlist[MAX_STREAMS]; 

	int		reliable_fragment[MAX_STREAMS];	// is reliable waiting buf a fragment?          
	uint		reliable_fragid[MAX_STREAMS];		// buffer id for each waiting fragment

	fragbuf_t		*fragbufs[MAX_STREAMS];	// the current fragment being set
	int		fragbufcount[MAX_STREAMS];	// the total number of fragments in this stream

	int		frag_startpos[MAX_STREAMS];	// position in outgoing buffer where frag data starts
	int		frag_length[MAX_STREAMS];	// length of frag data in the buffer

	fragbuf_t		*incomingbufs[MAX_STREAMS];	// incoming fragments are stored here
	qboolean		incomingready[MAX_STREAMS];	// set to true when incoming data is ready

	// Only referenced by the FRAG_FILE_STREAM component
	char		incomingfilename[MAX_OSPATH];	// Name of file being downloaded

	void		*tempbuffer;		// download file buffer
	int		tempbuffersize;		// current size

	// incoming and outgoing flow metrics
	flow_t		flow[MAX_FLOWS];  

	// added for net_speeds
	size_t		total_sended;
	size_t		total_received;
} netchan_t;

extern netadr_t		net_from;
extern netadr_t		net_local;
extern sizebuf_t		net_message;
extern byte		net_message_buffer[NET_MAX_MESSAGE];
extern convar_t		*net_speeds;
extern convar_t		sv_lan;
extern convar_t		sv_lan_rate;
extern int		net_drop;

void Netchan_Init( void );
void Netchan_Shutdown( void );
void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void * ) );
void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, char *filename, byte *pbuf, int size );
qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *length );
qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg );
void Netchan_CreateFragments( netchan_t *chan, sizebuf_t *msg );
int Netchan_CreateFileFragments( netchan_t *chan, const char *filename );
void Netchan_Transmit( netchan_t *chan, int lengthInBytes, byte *data );
void Netchan_TransmitBits( netchan_t *chan, int lengthInBits, byte *data );
void Netchan_OutOfBand( int net_socket, netadr_t adr, int length, byte *data );
void Netchan_OutOfBandPrint( int net_socket, netadr_t adr, char *format, ... );
qboolean Netchan_Process( netchan_t *chan, sizebuf_t *msg );
void Netchan_UpdateProgress( netchan_t *chan );
qboolean Netchan_IncomingReady( netchan_t *chan );
qboolean Netchan_CanPacket( netchan_t *chan, qboolean choke );
qboolean Netchan_IsLocal( netchan_t *chan );
void Netchan_ReportFlow( netchan_t *chan );
void Netchan_FragSend( netchan_t *chan );
void Netchan_Clear( netchan_t *chan );

#endif//NET_MSG_H