#include "doomtype.h"
#include "doomstat.h"
#include "d_player.h"
#include "p_local.h"
#include "sv_main.h"
#include "c_console.h"

#define MASTERPORT				15100
#define MAX_MASTER_SERVERS		32

buf_t     ml_message;
byte      ml_message_buffer[MAX_UDP_PACKET];

typedef struct masterserver
{
	char      masterip[MAX_UDP_PACKET];
	netadr_t  masteraddr; // address of the master server
}masterserv, *pmasterserv;

masterserv masters[MAX_MASTER_SERVERS];

char      wadname[32];
bool      pwad;

CVAR (master, "1", CVAR_SERVERINFO);
CVAR (hostname, "Unnamed", CVAR_ARCHIVE);
CVAR (fragstats, "No", CVAR_ARCHIVE);

bool masterupdate;

//
// SV_InitMaster
//
void SV_InitMaster(void)
{
	ml_message.data = ml_message_buffer;
	ml_message.maxsize = sizeof(ml_message_buffer);

	// DL - default master list, load them from external config up in the future // todo
	SV_AddMaster("csdoom.dhs.org"); // todo
	SV_AddMaster("csdoom.voxelsoft.com");
}

//
// SV_ClearMasters
//
void SV_ClearMasters(void)
{
	memset(&masters, 0, sizeof(masters));
}

//
// SV_AddMaster
//
bool SV_AddMaster(char *masterip)
{
	int index;

	if(strlen(masterip) >= MAX_UDP_PACKET)return false;

	for(index = 0; index < MAX_MASTER_SERVERS; index++)
	{
		if(!strlen(masters[index].masterip))
		{
			strcpy(masters[index].masterip, masterip);
			
			NET_StringToAdr (masters[index].masterip, &masters[index].masteraddr);
			I_SetPort(masters[index].masteraddr, MASTERPORT);

			if(masters[index].masteraddr.ip[0] != 0 && masters[index].masteraddr.ip[1] != 0 && masters[index].masteraddr.ip[2] != 0 && masters[index].masteraddr.ip[3] != 0)
			{
				Printf(PRINT_MEDIUM, "Added master server: %s [%s]", masters[index].masterip, NET_AdrToString(masters[index].masteraddr));
				SV_UpdateMasterServer();
			}
			else
			{
				Printf(PRINT_MEDIUM, "Failed to resolve master server: %s, not added", masters[index].masterip, NET_AdrToString(masters[index].masteraddr));
				memset(&masters[index], 0, sizeof(masters[index]));
			}
			return true;
		}
	}

	Printf(PRINT_MEDIUM, "Failed to add master server: %s, too many master servers", masterip);
	return false;
}

//
// SV_ListMasters
//
void SV_ListMasters(void)
{
	int index;

	Printf(PRINT_MEDIUM, "Use addmaster/delmaster commands to modify this list");

	for(index = 0; index < MAX_MASTER_SERVERS; index++)
	{
		if(strlen(masters[index].masterip))
		{
			Printf(PRINT_MEDIUM, "%s [%s]", masters[index].masterip, NET_AdrToString(masters[index].masteraddr));
		}
	}

	Printf(PRINT_MEDIUM, "Current master server list");
}

//
// SV_RemoveMaster
//
bool SV_RemoveMaster(char *masterip)
{
	int index;

	for(index = 0; index < MAX_MASTER_SERVERS; index++)
	{
		if(strlen(masters[index].masterip))
		{
			if(strcmp(masters[index].masterip, masterip) == NULL)
			{
				Printf(PRINT_MEDIUM, "Removed master server: %s", masters[index].masterip);
				memset(&masters[index], 0, sizeof(masters[index]));
				SV_UpdateMasterServer();
				return true;
			}
		}
	}

	Printf(PRINT_MEDIUM, "Failed to remove master server: %s, not in list", masterip);
	return false;
}

//
// SV_UpdateMasterServer
//
void SV_UpdateMasterServer(void)
{
	int index;

	for(index = 0; index < MAX_MASTER_SERVERS; index++)
	{
		if(strlen(masters[index].masterip))
		{
		SZ_Clear(&ml_message);
		MSG_WriteLong(&ml_message, CHALLENGE);
		NET_SendPacket(ml_message.cursize, ml_message.data, masters[index].masteraddr);
		}
	}
}

//
// SV_UpdateMaster
//
void SV_UpdateMaster(void)
{
	int index;

	if (!master.value)
		return;

	// update master addresses from names every 3 hours
	if ((gametic % (TICRATE*60*60*3) ) == 0)
	{
		for(index = 0; index < MAX_MASTER_SERVERS; index++)
		{
			if(strlen(masters[index].masterip))
			{
				NET_StringToAdr (masters[index].masterip, &masters[index].masteraddr);
				I_SetPort(masters[index].masteraddr, MASTERPORT);
			}
		}
	}

	// Send to masters every 25 seconds
	if ((gametic % (TICRATE*25)) && !masterupdate)
		return;

	masterupdate = false;

	SV_UpdateMasterServer();
}

CVAR (sv_showlauncherpings, "0", CVAR_ARCHIVE)

//
// SV_SendServerInfo
// Sends server info to a launcher
//
void SV_SendServerInfo()
{
	int numclients = 0;
	int numplayers = 0;
	int i;

	for (i=0; i<MAXPLAYERS; i++)
		if (clients[i].cls >= cs_connected)
			numclients++;

	SZ_Clear(&ml_message);
	MSG_WriteLong(&ml_message, CHALLENGE);

	MSG_WriteString(&ml_message, (char *)hostname.string);
	
	MSG_WriteByte(&ml_message, numclients);
	MSG_WriteByte(&ml_message, maxclients);

	MSG_WriteString(&ml_message, level.mapname);
	
	if (!pwad)
	{
		//MSG_WriteByte(&ml_message, 0);
		// [DL] better let everyone know what basic wad we are running, even if no pwads // @todo list all wads
		MSG_WriteByte(&ml_message, 1);
		MSG_WriteString(&ml_message, wadname);
	}
	else
	{
		MSG_WriteByte(&ml_message, 1);
		MSG_WriteString(&ml_message, wadname);
	}

	MSG_WriteByte(&ml_message, (byte)deathmatch.value);
	MSG_WriteByte(&ml_message, (byte)gameskill.value);
	MSG_WriteByte(&ml_message, (byte)teamplay.value);
	MSG_WriteString(&ml_message, (char *)fragstats.string);

	MSG_WriteLong(&ml_message, dmflags);
	
	for (i=0; i<MAXPLAYERS; i++)
		if (clients[i].cls == cs_spawned)
			numplayers++;

	MSG_WriteByte(&ml_message, numplayers);
			
	for (i=0; i<MAXPLAYERS; i++)
	{
		if (clients[i].cls != cs_spawned)
			continue;

		MSG_WriteString(&ml_message, players[i].userinfo.netname);
		MSG_WriteShort(&ml_message, players[i].fragcount);
		MSG_WriteShort(&ml_message, int(players[i].ping / 35.0 * 1000) );
	}

	NET_SendPacket(ml_message.cursize, ml_message.data, net_from);

	if(sv_showlauncherpings.value)
		Printf(PRINT_MEDIUM, "Launcher refresh from: %s", NET_AdrToString(net_from));
}