/*
This file is intended as a set of exports for an NQ-based engine.
This is supported _purely_ for clients, and will not work for servers.

documentation can be found inside net_qtv.h
*/



#include "qtv.h"
int build_number(void);

static cluster_t *cluster;

//note that a qsockaddr is only 16 bytes.
//this is not enough for ipv6 etc.
struct qsockaddr
{
	int ipid;
};
static char resolvedadrstring[128];
static int lastadrid;

#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#endif

#include "net_qtv.h"


PUBEXPORT void PUBLIC QTV_Command (char *command, char *results, int resultlen)
{
	if (!cluster)
		return;
	Rcon_Command(cluster, NULL, command, results, resultlen, true);
}
PUBEXPORT void PUBLIC QTV_RunFrame (void)
{
	if (!cluster)
		return;
	Cluster_Run(cluster, false);
}


EXPORT int PUBLIC QTV_Init (void)
{
	if (cluster)
		return 0;

	cluster = malloc(sizeof(*cluster));
	if (cluster)
	{
		memset(cluster, 0, sizeof(*cluster));

		cluster->qwdsocket[0] = INVALID_SOCKET;
		cluster->qwdsocket[1] = INVALID_SOCKET;
		cluster->tcpsocket[0] = INVALID_SOCKET;
		cluster->tcpsocket[1] = INVALID_SOCKET;
		cluster->anticheattime = 1*1000;
		cluster->tooslowdelay = 100;
		cluster->qwlistenportnum = 0;
		cluster->allownqclients = true;
		strcpy(cluster->hostname, DEFAULT_HOSTNAME);
		cluster->buildnumber = build_number();
		cluster->maxproxies = -1;

		strcpy(cluster->demodir, "qw/demos/");
		return 0;
	}

	return -1;
}
EXPORT void PUBLIC QTV_Shutdown (void)
{
}
EXPORT void PUBLIC QTV_Listen (qboolean state)
{
}
EXPORT int PUBLIC QTV_OpenSocket (int port)
{
	return 0;
}
EXPORT int PUBLIC QTV_CloseSocket (int socket)
{
	//give it a chance to close any server connections from us disconnecting (should have already send disconnect message, but won't have run the server so not noticed the lack of viewers)
	Cluster_Run(cluster, false);
	return 0;
}
EXPORT int PUBLIC QTV_Connect (int socket, struct qsockaddr *addr)
{
	if (addr->ipid == lastadrid)
	{
		strlcpy(cluster->autojoinadr, resolvedadrstring, sizeof(cluster->autojoinadr));
		return 0;
	}
	else
	{
		cluster->autojoinadr[0] = 0;
		return -1;
	}
	return 0;
}
EXPORT int PUBLIC QTV_CheckNewConnections (void)
{
	return -1;
}

static byte pendingbuf[8][1032];
static int pendinglen[8];
static unsigned int pendingin, pendingout;
void QTV_DoReceive(void *data, int length)
{
	int idx;
	if (length > sizeof(pendingbuf[0]))
		return;
	idx = pendingout++;
	idx &= 7;
	memcpy(pendingbuf[idx], data, length);
	pendinglen[idx] = length;
}
EXPORT int PUBLIC QTV_Read (int socket, byte *buf, int len, struct qsockaddr *addr)
{
	if (pendingout == pendingin)
	{
		Cluster_Run(cluster, false);
		Cluster_Run(cluster, false);
	}

	while (pendingin != pendingout)
	{
		int idx = pendingin++;
		idx &= 7;
		if (pendinglen[idx] > len)
			continue;	//error
		memcpy(buf, pendingbuf[idx], pendinglen[idx]);
		return pendinglen[idx];
	}
	return 0;
}
EXPORT int PUBLIC QTV_Write (int socket, byte *buf, int len, struct qsockaddr *addr)
{
	netmsg_t m;
	netadr_t from;
	from.tcpcon = NULL;
	((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;

	m.cursize = len;
	m.data = buf;
	m.readpos = 0;

	QW_ProcessUDPPacket(cluster, &m, from);

	if (pendingout == pendingin)
		Cluster_Run(cluster, false);

	return 0;
}
EXPORT int PUBLIC QTV_Broadcast (int socket, byte *buf, int len)
{
	netmsg_t m;
	netadr_t from;
	from.tcpcon = NULL;
	((struct sockaddr*)from.sockaddr)->sa_family = AF_UNSPEC;

	m.cursize = len;
	m.data = buf;
	m.readpos = 0;

	QW_ProcessUDPPacket(cluster, &m, from);

	return 0;
}
EXPORT char *PUBLIC QTV_AddrToString (struct qsockaddr *addr)
{
	return 0;
}
EXPORT int PUBLIC QTV_StringPortToAddr (char *string, int port, struct qsockaddr *addr)
{
	if (!strncmp(string, "file:", 5) || !strncmp(string, "demo:", 5))
	{
		snprintf(resolvedadrstring, sizeof(resolvedadrstring), "%s", string);
		addr->ipid = ++lastadrid;
		return 0;
	}
	if (!strncmp(string, "qw:", 3) || !strncmp(string, "udp:", 4) || !strncmp(string, "tcp:", 4))
	{
		if (port)
			snprintf(resolvedadrstring, sizeof(resolvedadrstring), "%s:%i", string, port);
		else
			snprintf(resolvedadrstring, sizeof(resolvedadrstring), "%s", string);
		addr->ipid = ++lastadrid;
		return 0;
	}
	return -1;
}
EXPORT int PUBLIC QTV_StringToAddr (char *string, struct qsockaddr *addr)
{
	return QTV_StringPortToAddr(string, 0, addr);
}
EXPORT int PUBLIC QTV_GetSocketAddr (int socket, struct qsockaddr *addr)
{
	return 0;
}
EXPORT int PUBLIC QTV_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
	return 0;
}
EXPORT int PUBLIC QTV_GetAddrFromName (char *name, struct qsockaddr *addr)
{
	return QTV_StringToAddr(name, addr);
}
EXPORT int PUBLIC QTV_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
	return 0;
}
EXPORT int PUBLIC QTV_GetSocketPort (struct qsockaddr *addr)
{
	return 0;
}
EXPORT int PUBLIC QTV_SetSocketPort (struct qsockaddr *addr, int port)
{
	return 0;
}