#include "stinc.h"
#include "stmisc.h"
#include "stnet.h"

gzGameSpyQuerySettings::gzGameSpyQuerySettings()
{
	this->m_enable        = false;
	this->m_bindPort      = 0;
	this->m_showPlayers   = false;
	this->m_prependExMark = false;
}
void gzGameSpyQuerySettings::Load()
{
	struct stat attrib;
	stat("Network.ini", &attrib);
	if (this->m_confModTime != attrib.st_mtime)
	{
		this->m_confModTime = attrib.st_mtime;

		this->m_cfg = new aTextConfig("Network.ini");
		if (!this->m_cfg->IsLoaded())
			stConsole::Out("[Error] Network.ini can not be loaded or not found.\n");

		if (Get_Game_Mode() == 4)
			this->m_enable = true;
		else
			this->m_enable = this->Load_Bool("GameSpy", "Enable", false);

		this->m_bindPort      = (unsigned short)this->Load_Int("GameSpy", "BindPort", 0);
		this->m_showPlayers   = this->Load_Int("GameSpy", "ShowPlayers", false);
		this->m_prependExMark = this->Load_Bool("GameSpy", "PrependExMark", false);

		delete this->m_cfg;
		this->m_cfg = NULL;
	}
}

gzGameSpyQueryManager *gzGameSpyQueryMgr = NULL;
void gzGameSpyQueryManager::Delete()
{
	if (this->m_socket != NULL)
	{
		aString query;
		query.Printf("\\heartbeat\\%u\\gamename\\ccrenegade\\statechanged\\2", this->m_bindPort);
		this->m_socket->Send(this->m_gsaAddr, query.GetString(), query.GetLength());
		delete this->m_socket;
		this->m_socket = NULL;
	}
}
void gzGameSpyQueryManager::Settings_Loaded()
{
#ifdef DEVMSG
	stConsole::Out("[GSA] Resolving master.gamespy.com...\n");
#endif
	this->m_gsaAddr.Address("master.gamespy.com");
	this->m_gsaAddr.Port(27900);
#ifdef DEVMSG
	stConsole::Out("[GSA] Resolved master.gamespy.com to IP: %s\n", this->m_gsaAddr.Address().GetString());
#endif

	if (this->m_settings->m_enable)
	{
		if (this->m_bindPort != this->m_settings->m_bindPort)
		{
			if (this->m_socket)
			{
				aString query;
				query.Printf("\\heartbeat\\%u\\gamename\\ccrenegade\\statechanged\\2", this->m_bindPort);
				this->m_socket->Send(this->m_gsaAddr, query.GetString(), query.GetLength());
				delete this->m_socket;
			}

			this->m_bindPort = this->m_settings->m_bindPort;

			sockaddr_in bindip;
			bindip.sin_addr.s_addr = cGame->IP;
			this->m_socket = new aSocketUDP(aSOCKET_NONBLOCKING);
			aIPv4Address listen;
			listen.Address(inet_ntoa(bindip.sin_addr));
			listen.Port(this->m_bindPort);
			if (!this->m_socket->Listen(listen))
			{
				stConsole::Out("[GSA] Unable to bind socket; Error: %d\n", this->m_socket->GetError());
				return;
			}

			aString msg;
			msg.Printf("\\heartbeat\\%u\\gamename\\ccrenegade", this->m_bindPort);
			this->m_socket->Send(this->m_gsaAddr, msg.GetString(), msg.GetLength());
		}
	}
	else
	{
		if (this->m_socket)
		{
			delete this->m_socket;
			this->m_socket = NULL;
		}
	}
}
gzGameSpyQueryManager::gzGameSpyQueryManager()
{
	this->RegisterEvent(EVT_GAME_THINK);

	this->m_socket = NULL;
	this->m_queryCount = 0;
	this->m_bindPort = 0;
	this->m_gsaTimeoutCounter = GSA_TIMEOUT;
	this->m_settings = new gzGameSpyQuerySettings;
	this->m_settings->SetOwner(this);
}
void gzGameSpyQueryManager::Think()
{
	if (this->m_socket)
	{

#if VERC(1, 0, 1)
		SubFrameTime(this->m_gsaTimeoutCounter, true);
		if (this->m_gsaTimeoutCounter < 0.0f)
		{
#ifdef DEVMSG
			stConsole::Out("[GSA] Timeout for waiting query from master.gamespy.com; Restarting GSA query service...\n");
#endif
			this->m_gsaTimeoutCounter = GSA_TIMEOUT;
			delete this->m_socket;
			this->m_socket = NULL;

			// Because of port difference check with settings
			this->m_bindPort = 0;

			// Restart the service
			this->Settings_Loaded();
			return;
		}
#endif

		char data[64];
		memset(&data, 0, sizeof(data));
		aIPv4Address src;
		int dataLen = this->m_socket->RecvFrom(data, sizeof(data) - 1, 0, src);

		// Have data
		if (dataLen > 0)
		{
			this->m_queryCount++;
			if (!strnicmp(data, "\\echo\\", 6))
			{
				aString out;
				out.Printf("%s\\final\\\\queryid\\%u.1", data, this->m_queryCount);
				this->m_socket->Send(src, out.GetString(), out.GetLength());
			}
			else if (!stricmp(data, "\\info\\") || !stricmp(data, "\\status\\"))
			{

#if VERC(1, 0, 1)
				if (src.Address() == this->m_gsaAddr.Address())
				{
#ifdef DEVMSG
					stConsole::Out("[GSA] Query received from master.gamespy.com\n");
#endif
					this->m_gsaTimeoutCounter = GSA_TIMEOUT;
				}
#endif
				aString msg, map = cGame->MapName.m_Buffer;
				if (map.Find('.', true) != -1)
					map.Resize(map.Find('.', true));

				msg.Printf("\\gamename\\ccrenegade\\gamever\\838\\hostname\\%ls\\hostport\\%hu\\mapname\\%s\\gametype\\C&C\\numplayers\\%u\\maxplayers\\%u\\CSVR\\1\\DED\\1\\DG\\%d\\password\\%d\\TC\\%d\\FF\\%d\\SC\\%d\\SSC\\%s\\queryid\\%u.1",
					((this->m_settings->m_prependExMark) ? aWideString::Format(L"!%ls", cGame->GameTitle.m_Buffer).GetString() : cGame->GameTitle.m_Buffer),
					cGame->Port,
					map.GetString(),
					cGame->CurrentPlayers,
					cGame->MaxPlayers,
					cGame->DriverIsAlwaysGunner,
					cGame->IsPassworded,
					cGame->IsTeamChangingAllowed,
					cGame->IsFriendlyFirePermitted,
					cGame->As_Cnc()->StartingCredits,
					stVersion.GetString(),
					this->m_queryCount
				);
				this->m_socket->Send(src, msg.GetString(), msg.GetLength());
			}
			if (this->m_settings->m_showPlayers && !stricmp(data, "\\status\\"))
			{
				if (this->m_settings->m_showPlayers)
				{
					int sendCount = 2;
					aString msg;
					for (nc_GenericSLNode<nc_cPlayer> *pList = nc_cPlayerManager::PlayerList->HeadNode; pList != NULL; pList = pList->NodeNext)
					{
						if (pList->NodeData->IsActive)
						{
							unsigned long time = (GetTickCount() - SystemTime - nc_cNetwork::PServerConnection->RemoteList[pList->NodeData->PlayerId]->JoinTime) / 1000;
							aString pData;
							pData.Printf("\\player_%d\\%ls\\score_%d\\%.0f\\kills_%d\\%d\\deaths_%d\\%d\\ping_%d\\%d\\skin_%d\\%s\\time_%d\\%03d.%02d.%02d",
								pList->NodeData->PlayerId,
								pList->NodeData->PlayerName.m_Buffer,
								pList->NodeData->PlayerId,
								pList->NodeData->Score.Get(),
								pList->NodeData->PlayerId,
								pList->NodeData->Kills.Get(),
								pList->NodeData->PlayerId,
								pList->NodeData->Deaths.Get(),
								pList->NodeData->PlayerId,
								nc_cNetwork::PServerConnection->RemoteList[pList->NodeData->PlayerId]->Ping,
								pList->NodeData->PlayerId,
								(pList->NodeData->PlayerType.Get() == 1) ? "GDI" : "Nod",
								pList->NodeData->PlayerId,
								time / 3600,
								(time / 60) % 60,
								time % 60
							);
							if ((msg.GetLength() + pData.GetLength()) > GSA_MAX_PACKET_LENGTH)
							{
								msg += aString::Format("\\queryid\\%u.%d", this->m_queryCount, sendCount);
								this->m_socket->Send(src, msg.GetString(), msg.GetLength());
								sendCount++;
								msg.Clear();
							}
							msg += pData;
						}
					}
					if (msg.GetLength() > 0)
					{
						msg += aString::Format("\\queryid\\%u.%d", this->m_queryCount, sendCount);
						this->m_socket->Send(src, msg.GetString(), msg.GetLength());
						sendCount++;
						msg.Clear();
					}
					msg.Printf("\\final\\\\queryid\\%u.%d", this->m_queryCount, sendCount);
					this->m_socket->Send(src, msg.GetString(), msg.GetLength());
				}
			}
#ifdef DEVMSG
			stConsole::Out("[GSA] %s:%hu -> %s\n", src.Address().GetString(), src.Port(), data);
#endif
		}
	}
}

#if ISDEV()
gzPlayerTraceRoute::gzPlayerTraceRoute(const char *ip)
{
	this->m_dst.Address(ip);
	this->Create(NULL);
	this->Run();
}
int gzPlayerTraceRoute::Entry(void *)
{
	aNetTraceRoute traceRoute;
	traceRoute.Run(this->m_dst, 30, 1000);
	DebugLog("IP: %s; Route count: %d", this->m_dst.Address().GetString(), traceRoute.GetRoute().Count());
	for (unsigned int i = 0; i < traceRoute.GetRoute().Count(); i++)
		DebugLog("\t[%d] %s", i + 1, traceRoute.GetRoute()[i].ip);
	return 0;
}
#endif
