//voice chat routines.

//needs quite a bit of work.

//it needs a proper protocol.
//the server needs to be able to shutdown again.
//options about who gets the sound data is also needed.

#include "bothdefs.h"

#ifdef VOICECHAT

#include "quakedef.h"
#ifdef _WIN32
#include "winquake.h"

#define EWOULDBLOCK	WSAEWOULDBLOCK
#define EMSGSIZE	WSAEMSGSIZE
#define ECONNRESET	WSAECONNRESET
#define ECONNABORTED	WSAECONNABORTED
#define ECONNREFUSED	WSAECONNREFUSED
#define EADDRNOTAVAIL	WSAEADDRNOTAVAIL

#define qerrno WSAGetLastError()
#else
#define qerrno errno

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <errno.h>

#include <unistd.h>

#ifdef sun
#include <sys/filio.h>
#endif

#ifdef NeXT
#include <libc.h>
#endif

#define closesocket close
#define ioctlsocket ioctl
#endif

#include "voicechat.h"

static int CLVS_socket;
static int SVVS_socket;
static qboolean SVVS_inited;

static qbyte inputbuffer[44100];
static int readpoint;
static qbyte outputbuffer[44100];
static int sendpoint;

/*
Protocol:
	Sound chunk:
		(char)	data begins with codec id.
		(short)	followed by number of samples
		(short)	then size in bytes of chunk.
*/

#ifndef CLIENTONLY
void SVVC_ServerInit(void)
{	
	netadr_t adr;
	struct sockaddr_in address;
	unsigned long _true = true;
	int i;
	int port = PORT_SERVER;

	if ((SVVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
	{
		Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
	}

	if (ioctlsocket (SVVS_socket, FIONBIO, &_true) == -1)
	{
		Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
	}

	address.sin_family = AF_INET;
//ZOID -- check for interface binding option
	if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) {
		address.sin_addr.s_addr = inet_addr(com_argv[i+1]);
		Con_TPrintf(TL_NETBINDINTERFACE,
				inet_ntoa(address.sin_addr));
	} else
		address.sin_addr.s_addr = INADDR_ANY;

	if (port == PORT_ANY)
		address.sin_port = 0;
	else
		address.sin_port = htons((short)port);
	
	if( bind (SVVS_socket, (void *)&address, sizeof(address)) == -1)
	{
		closesocket(SVVS_socket);
		return;
	}
	
	listen(SVVS_socket, 3);

	SVVS_inited = true;


	Con_Printf("VC server is running\n");
	SockadrToNetadr((struct sockaddr_qstorage*)&address, &adr);
	Info_SetValueForKey(svs.info, "voiceaddress", NET_AdrToString(adr), MAX_SERVERINFO_STRING);
	return;
}

//currently a dum forwarding/broadcasting mechanism
void SVVC_Frame (qboolean running)
{
	struct sockaddr_in frm;
	int size = sizeof(frm);
	int newcl;
	int i, j;
	char buffer[1400];

	if (!running)
		return;

	if (!SVVS_socket)
	{
		SVVC_ServerInit();
		return;
	}
	newcl = accept(SVVS_socket, (struct sockaddr *)&frm, &size);
	if (newcl != INVALID_SOCKET)
	{
		for (i = 0; i < MAX_CLIENTS; i++)
		{
			if (!svs.clients[i].voicechat.socket)	//this really isn't the right way...
			{
				svs.clients[i].voicechat.socket = newcl;
				break;
			}
		}
	}

	for (i = 0; i < MAX_CLIENTS; i++)
	{
		host_client = &svs.clients[i];

		if (host_client->voicechat.socket)
		{
			size = recv(host_client->voicechat.socket, buffer, sizeof(buffer), 0);
			if (size > 0)
			{
				for (j = 0; j < MAX_CLIENTS; j++)
				{
					if (j != i && svs.clients[j].voicechat.socket)	//gotta be capable of receiving, and not the sender (that would be dumb).
						send(svs.clients[j].voicechat.socket, buffer, size, 0);
				}
			}
		}
	}
}
#endif

#ifndef SERVERONLY

sfxcache_t *voicesoundbuffer[2];
sfx_t recordaudio[2] = {
	{"recordaudio1",
	{NULL, false},
	NULL},
	{"recordaudio2",
	{NULL, false},
	NULL}
};
int audiobuffer;
void SNDVC_Submit(int codec, qbyte *buffer, int samples, int freq, int width)
{
	S_RawAudio(0, buffer, freq, samples, 1, width);
		/*
	soundcardinfo_t *cursndcard;
	audiobuffer=0;
	if (!recordaudio[audiobuffer].cache.data)
	{
		voicesoundbuffer[audiobuffer] = BZ_Malloc(44100*2+sizeof(sfxcache_t));					
		recordaudio[audiobuffer].cache.data = voicesoundbuffer[audiobuffer];
		recordaudio[audiobuffer].cache.fake = true;
	}

	cursndcard = sndcardinfo;
	if (!cursndcard)
	{
		Con_Printf("Accepting voice chat with no sound card\n");
		return;
	}
	voicesoundbuffer[audiobuffer]->length = samples;
	voicesoundbuffer[audiobuffer]->stereo = false;
	voicesoundbuffer[audiobuffer]->speed = sndcardinfo->sn.speed;
	voicesoundbuffer[audiobuffer]->width = width;
	voicesoundbuffer[audiobuffer]->loopstart=-1;

//	Con_DPrintf("Submit %i\n", (int)samples);

	if (codec == 0)	//codec 0 is special. (A straight copy)
		ResampleSfx(&recordaudio[audiobuffer], freq, width, buffer);
	else
	{
		qbyte *temp = BZ_Malloc(samples*width);
		audiocodecs[codec].decode(buffer, (short*)temp, samples);
		ResampleSfx(&recordaudio[audiobuffer], freq, width, temp);
		BZ_Free(temp);
	}

	for (cursndcard = sndcardinfo; cursndcard; cursndcard=cursndcard->next)
	{
		if (0&&cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].sfx == &recordaudio[1-audiobuffer])	//other buffer is playing.
		{
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].end - voicesoundbuffer[1-audiobuffer]->length- cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos;
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end + samples;

			if (cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos >= voicesoundbuffer[audiobuffer]->length)
			{
				cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+1-audiobuffer].pos = voicesoundbuffer[1-audiobuffer]->length;
				cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
				Con_Printf("Sound out of sync\n");
			}
		}
		else
		{
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].sfx = &recordaudio[audiobuffer];
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].pos = 0;
			cursndcard->channel[MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS+audiobuffer].end = cursndcard->paintedtime + samples;
		}
	}
	audiobuffer = 1-audiobuffer;*/
}















void CLVC_SetServer (char *addy)
{
	unsigned long _true = true;
	struct sockaddr_qstorage	from;
	

	if ((CLVS_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
	{
		Sys_Error ("FTP_UDP_OpenSocket: socket: %s\n", strerror(qerrno));
	}


	{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)
		netadr_t qaddy;		
		if (!NET_StringToAdr (addy, &qaddy))	//server doesn't exist.
			return;
		if (qaddy.type != NA_IP)	//Only TCP is supported.
			return;
		if (!qaddy.port)
			qaddy.port = htons(PORT_SERVER);
		NetadrToSockadr(&qaddy, &from);

	}

	//not yet non blocking.
	if (connect(CLVS_socket, (struct sockaddr*)&from, sizeof(from)) == -1)
	{
		Con_Printf ("FTP_TCP_OpenSocket: connect: %i %s\n", qerrno, strerror(qerrno));
		closesocket(CLVS_socket);		
		CLVS_socket = 0;
		return;
	}
	
	if (ioctlsocket (CLVS_socket, FIONBIO, &_true) == -1)	//now make it non blocking.
	{
		Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO: %s\n", strerror(qerrno));
	}
}

void CLVC_Disconnect (void)
{
	closesocket(CLVS_socket);		
	CLVS_socket = 0;
}

void CLVC_Poll (void)
{
	int codec;
	int size;

	if (!CLVS_socket)
		return;
	
	while (1)
	{
		size = recv(CLVS_socket, &inputbuffer[readpoint], sizeof(inputbuffer) - readpoint, 0);
		if (size>0)
		{
			int samps;
			int bytes;
			readpoint+=size;
			if (readpoint >= 1)
			{
				codec = *inputbuffer;

				if (codec >= 0 && codec <= 127 && readpoint>=5)	//just in case.
				{
					samps = LittleLong(*(signed short *)(inputbuffer+1));
					bytes = LittleLong(*(unsigned short *)(inputbuffer+3));
//					Con_DPrintf("read %i\n", size);
					if (samps < 0)	//something special
					{
						readpoint=0;
					}
					else
					{
						if (readpoint >= bytes+5)
						{
							if (codec == 1)
								Con_Printf("Reading\n");
							if (codec < audionumcodecs && audiocodecs[codec].decode)
							{
								SNDVC_Submit(codec, ((qbyte *)inputbuffer)+5, samps, 11025, 2);
								readpoint -= bytes+5;
								memmove(inputbuffer, &inputbuffer[readpoint+bytes+5], readpoint);
							}
							else
							{
								Con_Printf("Bad codec %i\n", (int)codec);
								readpoint=0;
								closesocket(CLVS_socket);
								CLVS_socket = 0;
							}
						}
					}
				}
			}
		}
		else if (readpoint >= sizeof(inputbuffer) || readpoint < 0)
		{
			Con_Printf("Too small buffer or extended client %i\n", (int)readpoint);
			readpoint=0;
			closesocket(CLVS_socket);
			CLVS_socket = 0;
		}
		else
		{
			break;
		}
	}
}

void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width)	//this correctly buffers data ready to be sent.
{
	int sent;
	qbyte codec;
	unsigned short *sampleswritten;
	samples/=width;
	if (!CLVS_socket)
	{
		if (cls.state)
		{
			char *server;
			server = Info_ValueForKey(cl.serverinfo, "voiceaddress");
			if (*server)
				CLVC_SetServer(server);
		}

		SNDVC_Submit(0, buffer, samples, freq, width);
		return;
	}
	else if (!cls.state)
	{
		readpoint = 0;
		sendpoint= 0;
		SNDVC_Submit(0, buffer, samples, freq, width);
		return;
	}

	SNDVC_Submit(0, buffer, samples, freq, width);	//remembering at all times that we will not be allowed to hear it ourselves.

	//add to send buffer.
	if (samples > 0x7ffe)
		samples = 0x7ffe;

	if (sendpoint + samples*width+sizeof(unsigned char)+sizeof(short)+sizeof(*sampleswritten) < sizeof(outputbuffer))
	{
//		Con_DPrintf("sending %i\n", (int)samples);
		codec = 1;
		outputbuffer[sendpoint] = codec; sendpoint += sizeof(unsigned char);
		*(unsigned short*)(&outputbuffer[sendpoint]) = samples; sendpoint += sizeof(unsigned short);
		sampleswritten = (short *)&outputbuffer[sendpoint]; sendpoint += sizeof(*sampleswritten);
		*sampleswritten = audiocodecs[codec].encode((short *)buffer, &outputbuffer[sendpoint], samples);
		sendpoint += *sampleswritten;
	}
	else
	{
		Con_Printf("Connection overflowing\n");
	}

//try and send it
	sent = send(CLVS_socket, outputbuffer, sendpoint, 0);
	if (sent > 0)
	{
//		Con_DPrintf("sent %i\n", (int)sent);
		sendpoint -= sent;
	}
	else
	{
CLVS_socket=0;
	}


//	SNDVC_Submit(buffer, samples, freq, width);
}

#endif
#endif
