////////////////////////////////////////////////////////////////////////////////
//    Scorched3D (c) 2000-2003
//
//    This file is part of Scorched3D.
//
//    Scorched3D 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.
//
//    Scorched3D 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 Scorched3D; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

#include <server/ServerBrowserInfo.h>
#include <server/ScorchedServer.h>
#include <server/ServerState.h>
#include <server/ServerTooFewPlayersStimulus.h>
#include <common/OptionsGame.h>
#include <common/OptionsTransient.h>
#include <common/Defines.h>
#include <coms/NetInterface.h>
#include <tank/TankContainer.h>
#include <wx/wx.h>
#include <wx/utils.h>
#include <string.h>

ServerBrowserInfo *ServerBrowserInfo::instance_ = 0;

ServerBrowserInfo *ServerBrowserInfo::instance()
{
	if (!instance_)
	{
		instance_ = new ServerBrowserInfo();
	}
	return instance_;
}

ServerBrowserInfo::ServerBrowserInfo() : udpsock_(0)
{
	packetV_ = SDLNet_AllocPacketV(20, 10000);
}

ServerBrowserInfo::~ServerBrowserInfo()
{
}

bool ServerBrowserInfo::start()
{
	udpsock_ = SDLNet_UDP_Open(ScorchedServer::instance()->getOptionsGame().getPortNo() + 1);
	if(!udpsock_) return false;
	return true;
}

void ServerBrowserInfo::processMessages()
{ 
	if (!udpsock_) return;

	int numrecv = SDLNet_UDP_RecvV(udpsock_, packetV_);
	if(numrecv <=0) return;
	
	static char buffer[10000];
	for(int i=0; i<numrecv; i++) 
	{
		if (packetV_[i]->len == 0) packetV_[i]->data[0] = '\0';

		//printf("Packet = %s\n", (char *) packetV_[i]->data);
		NetInterface::getBytesIn() += packetV_[i]->len;
		NetInterface::getPings() ++;

		// Only compare the first few characters so a NULL is not needed
		if (0 == strncmp((char *) packetV_[i]->data, "status", 6)) processStatusMessage(buffer);
		else if (0 == strncmp((char *) packetV_[i]->data, "info", 4)) processInfoMessage(buffer);
		else if (0 == strncmp((char *) packetV_[i]->data, "players", 7)) processPlayerMessage(buffer);
		else processPingMessage(buffer);
		
		int len = (int) strlen(buffer)+1;
		memcpy(packetV_[i]->data, buffer, len);
		packetV_[i]->len = len;
		NetInterface::getBytesOut() += len;

		//printf("Packet len = %i\n", packetV_[i]->len);
		//printf("Packet = %s\n", packetV_[i]->data);
	}
	
	SDLNet_UDP_SendV(udpsock_, packetV_, numrecv);
}

void ServerBrowserInfo::processPingMessage(char *buffer)
{
	strcpy(buffer, "<pong/>");
}

void ServerBrowserInfo::processStatusMessage(char *buffer)
{
	char *serverName = (char *) ScorchedServer::instance()->getOptionsGame().getServerName();
	char version[256];
	sprintf(version, "%s (%s)", ScorchedVersion, ScorchedProtocolVersion);
	unsigned currentState = ScorchedServer::instance()->getGameState().getState();
	bool started = !ServerTooFewPlayersStimulus::instance()->acceptStateChange(0, 0, 0.0f);
	char players[25];
	sprintf(players, "%i", ScorchedServer::instance()->getTankContainer().getNoOfTanks());
	char maxplayers[25];
	sprintf(maxplayers, "%i", ScorchedServer::instance()->getOptionsGame().getNoMaxPlayers());
	char type[100];
	sprintf(type, "%s (%s)", 
		ScorchedServer::instance()->getOptionsTransient().getGameType(),
		((ScorchedServer::instance()->getOptionsGame().getTeams() > 1)?"Teams":"No Teams"));
	wxString osDesc = ::wxGetOsDescription();

	strcpy(buffer, "<status ");
	addTag(buffer, "gametype", type);
	addTag(buffer, "state", (started?"Started":"Waiting"));
	addTag(buffer, "servername", serverName);
	addTag(buffer, "fullversion", version);
	addTag(buffer, "version", ScorchedVersion);
	addTag(buffer, "protocolversion", ScorchedProtocolVersion);
	addTag(buffer, "mod", 
		ScorchedServer::instance()->getOptionsGame().getMod());
	addTag(buffer, "password", 
		ScorchedServer::instance()->getOptionsGame().getServerPassword()[0]?"On":"Off");
	addTag(buffer, "noplayers", players);
	addTag(buffer, "maxplayers", maxplayers);
	addTag(buffer, "os", osDesc.c_str());
	strcat(buffer, "/>");
}


void ServerBrowserInfo::processInfoMessage(char *buffer)
{
	// Add all of the other tank options
	// Currently nothing on the client uses this info
	strcpy(buffer, "<info ");
	std::list<OptionEntry *> &options = ScorchedServer::instance()->getOptionsGame().getOptions();
	std::list<OptionEntry *>::iterator optionItor;
	for (optionItor = options.begin();
		optionItor != options.end();
		optionItor ++)
	{
		OptionEntry *entry = (*optionItor);
		if (!(entry->getData() & OptionEntry::DataProtected))
		{
			if (strlen(entry->getValueAsString()) < 50)
			{
				addTag(buffer, entry->getName(), entry->getValueAsString());
			}
		}
	}	
	strcat(buffer, "/>");
}

void ServerBrowserInfo::processPlayerMessage(char *buffer)
{
	// Add all of the player information
	std::map<unsigned int, Tank *> &tanks =
		ScorchedServer::instance()->getTankContainer().getPlayingTanks();
	std::map<unsigned int, Tank *>::iterator tankItor;
	int i=0;

	strcpy(buffer, "<players ");
	for (tankItor =  tanks.begin();
		tankItor != tanks.end();
		tankItor++, i++)
	{
		Tank *tank = (*tankItor).second;
		static char tmp[128];

		sprintf(tmp, "pn%i", i);
		addTag(buffer, tmp, tank->getName());

		sprintf(tmp, "ps%i", i);
		addTag(buffer, tmp, tank->getScore().getScoreString());

		sprintf(tmp, "pt%i", i);
		addTag(buffer, tmp, tank->getScore().getTimePlayedString());

		sprintf(tmp, "pm%i", i);
		addTag(buffer, tmp, (tank->getTeam()==0)?"None":((tank->getTeam()==1)?"Red":"Green"));

		sprintf(tmp, "pr%i", i);
		addTag(buffer, tmp, tank->getScore().getStatsRank());
	}
	strcat(buffer, "/>");
}

void ServerBrowserInfo::addTag(char *buffer, const char *name, const char *value)
{
	static char newvalue[256];
	strcpy(newvalue, value);
	for (char *a=newvalue; *a; a++) if (*a == '\'') *a='\"';

	strcat(buffer, name); strcat(buffer, "='");
	strcat(buffer, newvalue); strcat(buffer, "' ");
}
