/*
 * qstat
 * by Steve Jankowski
 *
 * Cube 2 / Sauerbraten protocol
 * Copyright 2011 NoisyB
 *
 * Licensed under the Artistic License, see LICENSE.txt for license terms
 */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "debug.h"
#include "qstat.h"
#include "packet_manip.h"

struct offset {
	unsigned char *d;
	int pos;
	int len;
};

//#define SB_MASTER_SERVER "http://sauerbraten.org/masterserver/retrieve.do?item=list"
#define SB_PROTOCOL	258
#define MIN(a, b)    ((a) < (b) ? (a) : (b))
#define MAX_ATTR	255
#define MAX_STRING	1024

static int
getint(struct offset *d)
{
	int val = 0;

	if (d->pos >= d->len) {
		return (0);
	}

	val = d->d[d->pos++] & 0xff;// 8 bit value

	// except...
	if ((val == 0x80) && (d->pos < d->len - 2)) {       // 16 bit value
		val = (d->d[d->pos++] & 0xff);
		val |= (d->d[d->pos++] & 0xff) << 8;
	} else if ((val == 0x81) && (d->pos < d->len - 4)) {        // 32 bit value
		val = (d->d[d->pos++] & 0xff);
		val |= (d->d[d->pos++] & 0xff) << 8;
		val |= (d->d[d->pos++] & 0xff) << 16;
		val |= (d->d[d->pos++] & 0xff) << 24;
	}

	return (val);
}


static char *
getstr(char *dest, int dest_len, struct offset *d)
{
	int len = 0;

	if (d->pos >= d->len) {
		return (NULL);
	}

	len = MIN(dest_len, d->len - d->pos);
	strncpy(dest, (const char *)d->d + d->pos, len)[len - 1] = 0;
	d->pos += strlen(dest) + 1;

	return (dest);
}


static char *
sb_getversion_s(int n)
{
	static char *version_s[] =
	{
		"Justice",
		"CTF",
		"Assassin",
		"Summer",
		"Spring",
		"Gui",
		"Water",
		"Normalmap",
		"Sp",
		"Occlusion",
		"Shader",
		"Physics",
		"Mp",
		"",
		"Agc",
		"Quakecon",
		"Independence"
	};

	n = SB_PROTOCOL - n;
	if ((n >= 0) && ((size_t)n < sizeof(version_s) / sizeof(version_s[0]))) {
		return (version_s[n]);
	}
	return ("unknown");
}


static char *
sb_getmode_s(int n)
{
	static char *mode_s[] =
	{
		"slowmo SP",
		"slowmo DMSP",
		"demo",
		"SP",
		"DMSP",
		"ffa/default",
		"coopedit",
		"ffa/duel",
		"teamplay",
		"instagib",
		"instagib team",
		"efficiency",
		"efficiency team",
		"insta arena",
		"insta clan arena",
		"tactics arena",
		"tactics clan arena",
		"capture",
		"insta capture",
		"regen capture",
		"assassin",
		"insta assassin",
		"ctf",
		"insta ctf"
	};

	n += 6;
	if ((n >= 0) && ((size_t)n < sizeof(mode_s) / sizeof(mode_s[0]))) {
		return (mode_s[n]);
	}
	return ("unknown");
}


query_status_t
send_cube2_request_packet(struct qserver *server)
{
	return (send_packet(server, server->type->status_packet, server->type->status_len));
}


query_status_t
deal_with_cube2_packet(struct qserver *server, char *rawpkt, int pktlen)
{
	// skip unimplemented ack, crc, etc
	int i;
	int numattr;
	int attr[MAX_ATTR];
	char buf[MAX_STRING];

	enum {
		MM_OPEN = 0,
		MM_VETO,
		MM_LOCKED,
		MM_PRIVATE
	};

	struct offset d;
	d.d = (unsigned char *)rawpkt;
	d.pos = 0;
	d.len = pktlen;

	server->ping_total += time_delta(&packet_recv_time, &server->packet_time1);
	getint(&d);             // we have the ping already
	server->num_players = getint(&d);
	numattr = getint(&d);
	for (i = 0; i < numattr && i < MAX_ATTR; i++) {
		attr[i] = getint(&d);
	}

	server->protocol_version = attr[0];

	sprintf(buf, "%d %s", attr[0], sb_getversion_s(attr[0]));
	add_rule(server, "version", buf, NO_FLAGS);

	sprintf(buf, "%d %s", attr[1], sb_getmode_s(attr[1]));
	add_rule(server, "mode", buf, NO_FLAGS);

	sprintf(buf, "%d", attr[2]);
	add_rule(server, "seconds_left", buf, NO_FLAGS);

	server->max_players = attr[3];

	switch (attr[5]) {
	case MM_OPEN:
		sprintf(buf, "%d open", attr[5]);
		break;

	case MM_VETO:
		sprintf(buf, "%d veto", attr[5]);
		break;

	case MM_LOCKED:
		sprintf(buf, "%d locked", attr[5]);
		break;

	case MM_PRIVATE:
		sprintf(buf, "%d private", attr[5]);
		break;

	default:
		sprintf(buf, "%d unknown", attr[5]);
	}
	add_rule(server, "mm", buf, NO_FLAGS);

	for (i = 0; i < numattr && i < MAX_ATTR; i++) {
		char buf2[MAX_STRING];
		sprintf(buf, "attr%d", i);
		sprintf(buf2, "%d", attr[i]);
		add_rule(server, buf, buf2, NO_FLAGS);
	}

	getstr(buf, MAX_STRING, &d);
	server->map_name = strdup(buf);
	getstr(buf, MAX_STRING, &d);
	server->server_name = strdup(buf);

	return (DONE_FORCE);
}
