/*
   Copyright (C) 1997-2001 Id Software, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

   See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

 */
// cl_serverlist.c  -- interactuates with the master server

#include "client.h"

//=========================================================

typedef struct serverlist_s
{
	char address[32];
	struct serverlist_s *pnext;
} serverlist_t;

serverlist_t *serverList;


static qboolean filter_allow_full = qfalse;
static qboolean filter_allow_empty = qfalse;

//=========================================================

//=================
//CL_FreeServerlist
//=================
static void CL_FreeServerlist( void )
{
	serverlist_t *ptr;

	while( serverList )
	{
		ptr = serverList;
		serverList = serverList->pnext;
		Mem_TempFree( ptr );
	}
}

//=================
//CL_ServerIsInList
//=================
static qboolean CL_ServerIsInList( char *adr )
{

	serverlist_t *server;

	if( !serverList )
		return qfalse;

	server = serverList;
	while( server )
	{
		if( !Q_stricmp( server->address, adr ) )
			return qtrue;
		server = server->pnext;
	}

	return qfalse;
}

//=================
//CL_AddServerToList
//=================
static qboolean CL_AddServerToList( char *adr )
{

	serverlist_t *newserv;
	netadr_t nadr;

	if( !adr || !strlen( adr ) )
		return qfalse;

	if( !NET_StringToAddress( adr, &nadr ) )
		return qfalse;

	if( CL_ServerIsInList( adr ) )
		return qfalse;

	newserv = (serverlist_t *)Mem_TempMalloc( sizeof( serverlist_t ) );
	Q_strncpyz( newserv->address, adr, sizeof( newserv->address ) );
	newserv->pnext = serverList;
	serverList = newserv;

	return qtrue;
}

/*
   =================
   CL_ParseGetInfoResponse

   Handle a reply from getservers message to master server
   =================
 */
void CL_ParseGetInfoResponse( const socket_t *socket, const netadr_t *address, msg_t *msg )
{
	char *s = MSG_ReadString( msg );
	Com_DPrintf( "%s\n", s );
}


/*
   =================
   CL_QueryGetInfoMessage_f - getinfo 83.97.146.17:27911
   =================
 */
void CL_QueryGetInfoMessage_f( void )
{
	netadr_t adr;
	char *requeststring;
	char *server;

	//get what master
	server = Cmd_Argv( 1 );
	if( !server || !( *server ) )
	{
		Com_Printf( "no address provided %s...\n", server ? server : "" );
		return;
	}

	requeststring = va( "getinfo" );

	// send a broadcast packet
	Com_Printf( "quering %s...\n", server );

	if( NET_StringToAddress( server, &adr ) )
	{
		if( !adr.port )
			adr.port = BigShort( PORT_SERVER );
		Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requeststring );
	}
	else
	{
		Com_Printf( "Bad address: %s\n", server );
	}
}

typedef struct
{
	unsigned int starttime;
	char address[MAX_STRING_CHARS];
} pingserver_t;
static pingserver_t pingServer;

void CL_PingServer_f( void )
{
	char *address_string;
	char requestString[32];
	netadr_t adr;

	if( Cmd_Argc() < 2 )
		Com_Printf( "Usage: pingserver [ip:port]\n" );

	address_string = Cmd_Argv( 1 );

	if( !Q_stricmp( address_string, pingServer.address ) && cls.realtime - pingServer.starttime < SERVER_PINGING_TIMEOUT )  // don't ping again before getting the reply
		return;

	Q_strncpyz( pingServer.address, address_string, sizeof( pingServer.address ) );
	pingServer.starttime = cls.realtime;

	Q_snprintfz( requestString, sizeof( requestString ), "info %i %s %s", APP_PROTOCOL_VERSION,
	             filter_allow_full ? "full" : "",
	             filter_allow_empty ? "empty" : "" );

	if( NET_StringToAddress( address_string, &adr ) )
		Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requestString );
}

/*
   =================
   CL_ParseStatusMessage

   Handle a reply from a ping
   =================
 */
void CL_ParseStatusMessage( const socket_t *socket, const netadr_t *address, msg_t *msg )
{
	char *s = MSG_ReadString( msg );

	Com_DPrintf( "%s\n", s );

	if( !Q_stricmp( NET_AddressToString( address ), pingServer.address ) )
	{
		unsigned int ping = cls.realtime - pingServer.starttime;
		CL_UIModule_AddToServerList( NET_AddressToString( address ), va( "\\\\ping\\\\%i%s", ping, s ) );
		pingServer.address[0] = 0;
		pingServer.starttime = 0;
		return;
	}

	CL_UIModule_AddToServerList( NET_AddressToString( address ), s );
}

/*
   =================
   CL_ParseGetServersResponse

   Handle a reply from getservers message to master server
   =================
 */
static void CL_ParseGetServersResponse2( msg_t *msg )
{
	char adrString[32];
	qbyte addr[4];
	unsigned short port;
	netadr_t adr;

	MSG_BeginReading( msg );
	MSG_ReadLong( msg ); // skip the -1

	//jump over the command name
	if( !MSG_SkipData( msg, strlen( "getserversResponse\\" ) ) )
	{
		Com_Printf( "Invalid master packet ( missing getserversResponse )\n" );
		return;
	}

	while( msg->readcount +6 <= msg->cursize )
	{
		MSG_ReadData( msg, addr, 4 );
		port = ShortSwap( MSG_ReadShort( msg ) ); //both endians need this swapped.

		if( port == 0 )  // last server seen
			break;

		Q_snprintfz( adrString, sizeof( adrString ), "%u.%u.%u.%u:%u", addr[0], addr[1], addr[2], addr[3], port );

		Com_DPrintf( "%s\n", adrString );
		if( !NET_StringToAddress( adrString, &adr ) )
		{
			Com_Printf( "Bad address: %s\n", adrString );
			continue;
		}
		//Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requestString );
		CL_AddServerToList( adrString );

		if( '\\' != MSG_ReadChar( msg ) )
		{
			Com_Printf( "Invalid master packet ( missing seperator )\n" );
			break;
		}
	}
}

//jal: I will remove this function soon
//jal: the browser bug was the response string being parsed by
//jal: using MSG_ParseStringLine instead of using MSG_ParseString.
//jal: MSG_ParseStringLine cutted off the string when it found
//jal: a line ending, a zero or a -1.
void CL_ParseGetServersResponse( const socket_t *socket, const netadr_t *address, msg_t *msg )
{
	CL_FreeServerlist();
	CL_ParseGetServersResponse2( msg );
	//	CL_LoadServerList(); //jal: tmp
	//	CL_WriteServerList();
#if 1
	//send the servers to the ui
	{
		serverlist_t *server;
		netadr_t adr;

		server = serverList;
		while( server )
		{
			if( NET_StringToAddress( server->address, &adr ) )
			{
				CL_UIModule_AddToServerList( server->address, "\\\\EOT" );
			}
			server = server->pnext;
		}
	}
#else
	{
		serverlist_t *server;
		char requestString[32];
		netadr_t adr;

		Q_snprintfz( requestString, sizeof( requestString ), "info %i %s %s", APP_PROTOCOL_VERSION,
		             filter_allow_full ? "full" : "",
		             filter_allow_empty ? "empty" : "" );

		server = serverList;
		while( server )
		{
			if( NET_StringToAddress( server->address, &adr ) )
				Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requestString );
			server = server->pnext;
		}
	}
#endif
	CL_FreeServerlist();
}

/*
   =================
   CL_GetServers_f
   =================
 */
void CL_GetServers_f( void )
{
	netadr_t adr;
	char *requeststring;
	int i;
	char *modname, *master;

	filter_allow_full = qfalse;
	filter_allow_empty = qfalse;
	for( i = 0; i < Cmd_Argc(); i++ )
	{
		if( !Q_stricmp( "full", Cmd_Argv( i ) ) )
			filter_allow_full = qtrue;

		if( !Q_stricmp( "empty", Cmd_Argv( i ) ) )
			filter_allow_empty = qtrue;
	}

	if( !Q_stricmp( Cmd_Argv( 1 ), "local" ) )
	{
		// send a broadcast packet
		Com_Printf( "pinging broadcast...\n" );

		// erm... modname isn't sent in local queries?

		requeststring = va( "info %i %s %s", APP_PROTOCOL_VERSION,
		                    filter_allow_full ? "full" : "",
		                    filter_allow_empty ? "empty" : "" );

		for( i = 0; i < NUM_BROADCAST_PORTS; i++ )
		{
			NET_BroadcastAddress( &adr, PORT_SERVER + i );
			Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requeststring );
		}
		return;
	}

	//get what master
	master = Cmd_Argv( 2 );
	if( !master || !( *master ) )
		return;

	modname = Cmd_Argv( 3 );
	if( !modname || !modname[0] || !Q_stricmp( modname, DEFAULT_BASEGAME ) )
	{                                                                    // never allow anyone to use basewsw as mod name
		modname = APPLICATION;
	}
	assert( modname[0] );

	// create the message
	requeststring = va( "getservers %c%s %i %s %s", toupper( modname[0] ), modname+1, APP_PROTOCOL_VERSION,
	                    filter_allow_full ? "full" : "",
	                    filter_allow_empty ? "empty" : "" );

	// send a broadcast packet
	if( NET_StringToAddress( master, &adr ) && adr.type == NA_IP )
	{
		if( !adr.port )
			adr.port = BigShort( PORT_MASTER );
		Netchan_OutOfBandPrint( &cls.socket_udp, &adr, requeststring );

		Com_Printf( "quering %s...%s\n", master, NET_AddressToString( &adr ) );
	}
	else
	{
		Com_Printf( "Bad address: %s\n", master );
	}
}


//=============================================================================
