/* $Id: ogcache-client.c,v 1.6 2004/02/01 00:41:00 stpohle Exp $
 * OpenGameCache-Client: this file will hold the protocol for the gameserver communication
 */

#include "basic.h"
#include "bomberclone.h"
#include "udp.h"
#include "ogcache-client.h"

int ogc_sock = -1;
struct game_entry ogc_array[MAX_OGC_ENTRYS];
char ogc_host[LEN_OGCHOST+1];
char ogc_port[LEN_OGCPORT+1];
char ogc_game[LEN_GAME+1];
int ogc_ai_family;
struct _sockaddr ogc_addr;
int ogc_browsing = 0;
int ogc_lasttimeout = 0;

/* fill out the whole gameentry */
static void fill_gameentry (char *pos, struct game_entry *ge) {
	char *pos_next;		/* pointer to the next seperator */
	int i = 0;

	while (pos != NULL)
	{
		/* search and test the next seperator */
		pos_next = strchr (pos, '\t');
		if (pos_next == NULL)
			pos_next = strchr (pos, '\n');
		/* if we found a return delete it.. 
		 * if we found the tab delete it */
		if (pos_next != NULL)
		{
			if (pos_next[0] == '\n')
			{	/* RETURN found */
				pos_next[0] = '\0';
				pos_next = NULL;
			}
			else
				pos_next[0] = '\0';
		}

		/* fill the struct with our data */
		switch (i)
		{
		case (0):	/* serial */
			ge->serial = atoi (pos);
			break;
		case (1):	/* host */
			strncpy (ge->host, pos, LEN_OGCHOST);
			break;
		case (2):	/* port */
			strncpy (ge->port, pos, LEN_OGCPORT);
			break;
		case (3):	/* game */
			strncpy (ge->game, pos, LEN_GAME);
			break;
		case (4):	/* version */
			strncpy (ge->version, pos, LEN_VERSION);
			break;
		case (5):	/* nettype */
			if (strcmp (pos, "IPv6") == 0)
				ge->ai_family = PF_INET6;
			else
				ge->ai_family = PF_INET;
			break;
		case (6):	/* netname */
			strncpy (ge->gamename, pos, LEN_GAMENAME);
			break;
		case (7):	/* state */
			strncpy (ge->status, pos, LEN_STATUS);
			break;
		case (8):	/* curplayers */
			ge->curplayers = atoi (pos);
			break;
		case (9):	/* maxplayers */
			ge->maxplayers = atoi (pos);
			break;
		default:
			return;
		}

		/* increase i for the next entry */
		i++;
		if (pos_next != NULL)
			pos = pos_next + 1;
		else
			pos = NULL;
	}
	
	d_printf ("fill_gameentry [Serial:%d, Addr:%s%s, Game:%s, Gamename:%s\n", ge->serial, ge->host, ge->port, ge->game, ge->gamename);
};


/* add a game to the list or update one */
static int ogc_listadd (char *data) {
	struct game_entry ge;
	int i;
	
	/* fill out the entry and check if there was a problem */
	fill_gameentry (data, &ge);
	if (ge.serial == -1) return 0;
	
	for (i = 0; (i < MAX_OGC_ENTRYS && ogc_array[i].serial != -1 && ogc_array[i].serial != ge.serial); i++);
	
	if (i < MAX_OGC_ENTRYS && ogc_array[i].serial == ge.serial)
		ogc_array[i] = ge;
	else if (i < MAX_OGC_ENTRYS && ogc_array[i].serial == -1)
		ogc_array[i] = ge;
	
	return 1;
};


/* delete the game from the list */
static int ogc_listdel (char *data) {
	int i, serial, res = 0;
	
	serial = atoi (data);
	
	for (i = 0; i < MAX_OGC_ENTRYS; i++)
		if (ogc_array[i].serial == serial) {
			ogc_array[i].serial = -1;
			res = 1;
		}

	return res;
};


/* work with the incoming packet */
static int ogc_do_inpacket (char *in, int len, struct _sockaddr *addr) {
	char *param_start = strchr (in, ':');
	int i = 0;
	
	/* set param_start at the place where the first parameter is
	 * and if there is a : delete this one */
	if (param_start == NULL) {
		if ((param_start = strchr (in, '\n')) != NULL) {
			param_start[0] = '\0';
			param_start = NULL;
		}
	}
	else {
		param_start[0] = '\0';
		param_start++;
	}
	
	/*
     *	work with the incoming data
	 */
	if (strcmp (in, "ENTRY") == 0)
		i = ogc_listadd (param_start);
	
	else if (strcmp (in, "DELENTRY") == 0) 
		i = ogc_listdel (param_start);
	
	return i;
};



/* start a new listen server to get the list */
int ogc_init (char *localport, char *server, char *port, char *game, int ai_family) {
	int i;
	
	ogc_sock = -1;

	ogc_sock = udp_server (localport, ai_family);
	d_printf ("ogc_init (Localport: %s, Server %s:%s Game %s socket=%d)\n", localport, server, port, game, ogc_sock);
	if (ogc_sock <= 0)
		return 0;

	strncpy (ogc_host, server, LEN_OGCHOST);
	strncpy (ogc_port, port, LEN_OGCPORT);
	strncpy (ogc_game, game, LEN_GAME);
	ogc_ai_family = ai_family;
	
	d_printf ("ogc_host:%s ogc_port:%s ogc_game:%s ogc_ai_family:%d\n", ogc_host, ogc_port, ogc_game, ogc_ai_family);
	if (dns_filladdr (ogc_host, LEN_OGCHOST, ogc_port, LEN_OGCPORT, ogc_ai_family, &ogc_addr) < 0) {
		udp_close (ogc_sock);
		ogc_sock = -1;
		return 0;
	}
	
	for (i = 0; i < MAX_OGC_ENTRYS; i++)
		ogc_array[i].serial = -1;
	
	return 1;
};



/* send the status of the curent game to the game cache */
int ogc_sendgamestatus (int sock, char *game, char *version, char *gamename,
						int curplayers, int maxplayers, char *status) {
	char data[BUF_SIZE];
	
	if (sock <= 0 || ogc_sock <= 0) return 0;
	sprintf (data, "GAME:%s\t%s\t%s", game, version, gamename);
	if (ogc_ai_family == PF_INET)
		sprintf (data, "%s\tIPv4", data);
	else
		sprintf (data, "%s\tIPv6", data);
	sprintf (data, "%s\t%d\t%d\t%s", data, curplayers, maxplayers, status);

	udp_send (sock, data, strlen (data), &ogc_addr, ogc_ai_family);
	
	return 1;
};


/* send to the gamecache that this game has quit */
int ogc_sendgamequit (int sock) {
	char data[BUF_SIZE];

	if (sock <= 0 || ogc_sock <= 0) return 0;
	
	sprintf (data, "QUIT:");
	udp_send (sock, data, strlen (data), &ogc_addr, ogc_ai_family);
	
	return 1;
};


/* send to the gamecache that this browser does not anymore exist */
void ogc_shutdown () {
	if (ogc_sock <= 0)
		return;
	
	d_printf ("ogc_shutdown\n");
	if (ogc_browsing)
		ogc_browsestop ();
	udp_close (ogc_sock);
	
	ogc_sock = -1;
};



/* check the socket for incoming data and work 
 * with them if there are any */
int ogc_loop () {
	int len, i = 0;
	char in[BUF_SIZE];
	struct _sockaddr inaddr;
	
	if (ogc_sock <= 0)
		return -1;
		
	len = udp_get (ogc_sock, in, BUF_SIZE, &inaddr, ogc_ai_family);	
	
	if (len > 0) {  /* we have got something */
		d_printf ("ogc_got: %s\n", in);
		i = ogc_do_inpacket (in, len, &inaddr);
	}
	
	if (ogc_browsing && (time (NULL) - ogc_lasttimeout) > 30)
		ogc_browsestart ();
	
	return i;
};


/* send to the gamecache that we want 
 * to browse through the open games */
void ogc_browsestart () {
	char data[BUF_SIZE];
	
	d_printf ("ogc_browsestart\n");
	
	if (ogc_sock <= 0)
		return;

	sprintf (data, "LISTGAMES:%s", ogc_game);

	udp_send (ogc_sock, data, strlen (data), &ogc_addr, ogc_ai_family);
	ogc_browsing = 1;

	ogc_lasttimeout = time (NULL);
};


/* send the gamecache that we don't want to watch the list no more */
void ogc_browsestop () {
	char data[BUF_SIZE];
	
	d_printf ("ogc_browsestop\n");

	ogc_browsing = 0;
	if (ogc_sock <= 0)
		return;

	sprintf (data, "LISTQUIT:");
	
	udp_send (ogc_sock, data, strlen (data), &ogc_addr, ogc_ai_family);
};
