/*
 * qstat 2.14
 * by Steve Jankowski
 *
 * Terraria / TShock query protocol
 * Copyright 2012 Steven Hartland
 *
 * Licensed under the Artistic License, see LICENSE.txt for license terms
 *
 */

#include <sys/types.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define strtok_ret strtok_r
#else
#include <winsock.h>
#define strtok_ret strtok_s
#endif
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

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

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

query_status_t deal_with_terraria_packet( struct qserver *server, char *rawpkt, int pktlen )
{
	char *s, *end, *key, *val, *sep, *linep, *varp;
	int complete_response = 0;
	char last_char;
	unsigned short port = 0;
	debug( 2, "processing..." );

    if ( 0 == pktlen )
    {
        // Disconnect?
        return REQ_ERROR;
    }

	complete_response = ( 0 == strncmp( "HTTP/1.1 200", rawpkt, 12 ) && '}' == rawpkt[pktlen-1] ) ? 1 : 0;
	last_char = rawpkt[pktlen-1];
	rawpkt[pktlen-1] = '\0';

	debug( 3, "packet: combined = %d, complete = %d", server->combined, complete_response );
	if ( ! complete_response )
	{
		if ( ! server->combined )
		{
			// response fragment recieved
			int pkt_id;
			int pkt_max;
			server->retry1 = n_retries;
			if ( 0 == server->n_requests )
			{
				server->ping_total = time_delta( &packet_recv_time, &server->packet_time1 );
				server->n_requests++;
			}

			// We're expecting more to come
			debug( 5, "fragment recieved..." );
			pkt_id = packet_count( server );
			pkt_max = pkt_id+1;
			rawpkt[pktlen-1] = last_char; // restore the last character
			if ( ! add_packet( server, 0, pkt_id, pkt_max, pktlen, rawpkt, 1 ) )
			{
				// fatal error e.g. out of memory
				return MEM_ERROR;
			}

			if ( 0 == pkt_id )
			{
				return INPROGRESS;
			}

			// combine_packets will call us recursively
			return combine_packets( server );
		}

		// recursive call which is still incomplete
		return INPROGRESS;
	}

	// find the end of the headers
	s = strstr( rawpkt, "\x0d\x0a\x0d\x0a" );

	if ( NULL == s )
	{
		debug(1, "Error: missing end of headers");
		return REQ_ERROR;
	}

	s += 4;

	end = &rawpkt[pktlen-1];

	// Correct ping
	// Not quite right but gives a good estimate
	server->ping_total = ( server->ping_total * server->n_requests ) / 2;

	debug( 3, "processing response..." );

	s = strtok_ret( s, "\x0d\x0a", &linep );

	// NOTE: id=XXX and msg=XXX will be processed by the mod following the one they where the response of
	while ( NULL != s )
	{
		debug( 2, "LINE: %s\n", s );
		if ( 0 == strcmp( s, "{" ) )
		{
			s = strtok_ret( NULL, "\x0d\x0a", &linep );
			continue;
		}

		s = strtok_ret( s, "\"", &varp );
		key = strtok_ret( NULL, "\"", &varp );
		sep = strtok_ret( NULL, " ", &varp );
		val = strtok_ret( NULL, "\"", &varp );
		if ( NULL == val )
		{
			// world etc may be empty which results in NULL val
			s = strtok_ret( NULL, "\x0d\x0a", &linep );
			continue;
		}
		//if ( NULL == val && sep
		debug( 2, "var: '%s' = '%s', sep: '%s'\n", key, val, sep );
		if ( 0 == strcmp( key, "name" ) )
		{
			server->server_name = strdup( val );
		}
		else if ( 0 == strcmp( key, "port" ) )
		{
			port = atoi( val );
			change_server_port( server, port, 0 );
			add_rule( server, key, val, NO_FLAGS );
		}
		else if ( 0 == strcmp( key, "playercount" ) )
		{
			server->num_players = atoi( val );
		}
		else if ( 0 == strcmp( key, "maxplayers" ) )
		{
			server->max_players = atoi( val );
		}
		else if ( 0 == strcmp( key, "world" ) )
		{
			server->map_name = strdup( val );
		}
		else if ( 0 == strcmp( key, "players" ) )
		{
			// Players are ", " seperated but potentially player names can have spaces
			// so we manually check for the leading space and fix if found
			char *playersp;
			char *player_name = strtok_ret( val, ",", &playersp );
			while ( NULL != player_name )
			{
				struct player *player = add_player( server, server->n_player_info );
				if ( NULL != player )
				{
					if ( ' ' == player_name[0] )
					{
						player_name++;
					}
					player->name = strdup( player_name );
					debug( 4, "Player: %s\n", player->name );
				}
				player_name = strtok_ret( NULL, ",", &playersp );
			}
		}
		else if ( 0 == strcmp( key, "status" ) )
		{
			if ( 200 != atoi( val ) )
			{
				server->server_name = DOWN;
				return DONE_FORCE;
			}
		}
		else
		{
			add_rule( server, key, val, NO_FLAGS);
		}

		s = strtok_ret( NULL, "\x0d\x0a", &linep );
	}

	gettimeofday( &server->packet_time1, NULL );

	return DONE_FORCE;
}

