
#include "StdAfx.h"
#include "Server.h"
#include "Rules.h"
#include "Database.h"
#include "World.h"
#include "Network.h"
#include "BinaryWriter.h"
#include "ChatMsgs.h"
#include "Database2.h"

CDatabase *g_pDB = 0;
CMYSQLDatabase *g_pDB2 = 0;
CGameDatabase *g_pGameDatabase = 0;
CWorld *g_pWorld = 0;
CNetwork *g_pNetwork = 0;
TURBINEPORTAL *g_pPortal = 0;
TURBINECELL *g_pCell = 0;
GAMERULES *g_pGameRules = 0;

CPhatServer::CPhatServer(in_addr hostmachine, u_short hostport)
{
	m_lastframe = g_pGlobals->m_CounterTime;
	m_frames = 0;

	//
	memcpy(&m_hostaddr, &hostmachine, sizeof(in_addr));
	m_hostport = hostport;

	g_pGlobals->ResetPackets();

	m_socketCount = 10;
	for (int i = 0; i < m_socketCount; i++)
		m_sockets[i] = INVALID_SOCKET;

	InitializeSocket(hostport);

	g_pPortal = new TURBINEPORTAL();
	g_pCell = new TURBINECELL();
	g_pGameRules = new GAMERULES();
	g_pDB = new CDatabase; // Old, dumb, bad
	g_pDB2 = new CMYSQLDatabase("127.0.0.1", 0, "root", "", "phatac"); // Newer, shiny, makes pancakes in the morning
	g_pGameDatabase = new CGameDatabase();
	g_pWorld = new CWorld();
	g_pNetwork = new CNetwork(m_sockets, m_socketCount);

	g_pGameDatabase->Init();

	LOG(Temp, Normal, "The server is now online.\n");

	int missing = 0;
	int has = 0;
	int missingE = 0;
	int hasE = 0;
	int hasD = 0;
	int missingD = 0;
	int hasUD = 0;
	int hasUDT = 0;
	int hasM = 0;
	int missingM = 0;
	FILE *fp = NULL;

#if 0 // ndef _DEBUG
	if (GetAsyncKeyState(VK_F12))
	{
		fp = fopen("missingCells.txt", "wt");
	}

	for (int i = 0; i <= 0xFFFF; i++)
	{
		if (!g_pCell->FileExists((i << 16) | 0xFFFF))
		{
			missing++;
		}
		else
		{
			has++;

			if (TURBINEFILE *pCell = g_pCell->GetFile((i << 16) | 0xFFFF))
			{
				if (pCell->GetLength() >= 8)
				{
					DWORD value = ((DWORD *)pCell->GetData())[1];

					if (value)
					{
						if (!g_pCell->FileExists((i << 16) | 0xFFFE))
						{
							missingE++;
						}
						else
						{
							hasE++;

							if (TURBINEFILE *pCellData = g_pCell->GetFile((i << 16) | 0xFFFE))
							{
								DWORD value = ((DWORD *)pCellData->GetData())[1];

								if (value >= 10000)
								{
									__asm int 3;
								}

								for (unsigned int j = 0; j < value; j++)
								{
									if (g_pCell->FileExists((i << 16) | (0x100 + j)))
									{
										if (TURBINEFILE *pEnvData = g_pCell->GetFile((i << 16) | (0x100 + j)))
										{
											BYTE *ptr = pEnvData->GetData();

											ptr += 12;

											BYTE numSurface = *ptr;
											ptr += 4;
											ptr += 2 * numSurface;

											WORD envID = *(WORD *)ptr;

											if (g_pPortal->FileExists(0x0D000000UL | envID))
											{
												hasM++;
											}
											else
											{
												missingM++;
											}

											delete pEnvData;
										}

										hasD++;
									}
									else
									{

										if (fp)
										{
											fprintf(fp, "%08X\n", (i << 16) | (0x100 + j));
										}

										missingD++;
									}
								}

								delete pCellData;
							}
						}
					}
				}

				delete pCell;
			}
		}
	}

	if (fp)
	{
		fclose(fp);
	}

	LOG(Temp, Normal, "Has %u landblocks, missing %u (%.2f%%)\n", has, missing, ((float)has / (has+missing)) * 100.00);
	LOG(Temp, Normal, "Has %u landblock environments, missing %u of the ones known (%.2f%%)\n", hasE, missingE, ((float)hasE / (hasE + missingE)) * 100.00);
	LOG(Temp, Normal, "Has %u dungeon cells, missing %u of the ones known (%.2f%%)\n", hasD, missingD, ((float)hasD / (hasD + missingD)) * 100.00);
	LOG(Temp, Normal, "Resolved %u dungeon meshes, missing %u of the ones attempted (%.2f%%)\n", hasM, missingM, ((float)hasM / (hasM + missingM)) * 100.00);
#endif
}

CPhatServer::~CPhatServer()
{
	if (g_pNetwork)
	{
		SystemBroadcast("ATTENTION - This Asheron's Call Server is shutting down NOW!!!!");
		g_pNetwork->Think();
		SafeDelete(g_pNetwork);
	}

	SafeDelete(g_pWorld);
	SafeDelete(g_pGameDatabase);
	SafeDelete(g_pDB2);
	SafeDelete(g_pDB);
	SafeDelete(g_pGameRules);
	SafeDelete(g_pCell);
	SafeDelete(g_pPortal);

	for (int i = 0; i < m_socketCount; i++)
	{
		if (m_sockets[i] != INVALID_SOCKET)
		{
			closesocket(m_sockets[i]);
			m_sockets[i] = INVALID_SOCKET;
		}
	}
}

void CPhatServer::InitializeSocket(u_short port)
{
	SOCKADDR_IN localhost;
	localhost.sin_family = AF_INET;
	localhost.sin_addr.s_addr = INADDR_ANY;

	for (int i = 0; i < m_socketCount; i++)
		m_sockets[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	u_short startport = port;
	while (port < (startport + 30))
	{
		localhost.sin_port = htons(port);
		if (!bind(m_sockets[0], (struct sockaddr *)&localhost, sizeof(SOCKADDR_IN)))
		{
			LOG(Temp, Normal, "Bound to port %u!\n", port);
			break;
		}
		LOG(Temp, Normal, "Failed bind on port %u!\n", port);
		port++;
	}

	if (port == 9080)
	{
		LOG(Temp, Normal, "Failure to bind socket!\n");
	}
	else
	{
		SendMessage(GetDlgItem(g_pGlobals->GetWindowHandle(), IDC_SERVERPORT), WM_SETTEXT, 0, (LPARAM)csprintf("%u", port));

		int basePort = port;

		// Try to bind the other ports
		for (int i = 1; i < m_socketCount; i++)
		{
			localhost.sin_port = htons(basePort + i);
			if (bind(m_sockets[i], (struct sockaddr *)&localhost, sizeof(SOCKADDR_IN)))
			{
				LOG(Temp, Normal, "Failure to bind socket port %d!\n", basePort + i);
			}
		}
	}

	//Non-blocking sockets are more fun
	for (int i = 0; i < m_socketCount; i++)
	{
		unsigned long arg = 1;
		ioctlsocket(m_sockets[i], FIONBIO, &arg);
	}
}

void CPhatServer::SystemBroadcast(char *text)
{
	if (!g_pNetwork || !g_pWorld)
		return;

	LOG(Temp, Normal, "Broadcast, \"%s\"\n", text);
	g_pWorld->BroadcastGlobal(ServerBroadcast("System", text, 1), PRIVATE_MSG);
}

void CPhatServer::Tick(void)
{
	g_pGlobals->Update();

	if ((m_lastframe + g_pGlobals->m_CounterFreq) < g_pGlobals->m_CounterTime)
	{
		double timediff = (g_pGlobals->m_CounterTime - m_lastframe) / (double)g_pGlobals->m_CounterFreq;
		UpdateFramesHUD((UINT64)(m_frames / timediff));

		m_lastframe = g_pGlobals->m_CounterTime;
		m_frames = 0;
	}

	g_pNetwork->Think();
	g_pWorld->Think();
	g_pCell->Think();

	m_frames++;
}

u_short CPhatServer::GetPort()
{
	return m_hostport;
}

void CPhatServer::KickClient(WORD index)
{
	g_pNetwork->KickClient(index);
}

void CPhatServer::BanClient(WORD index)
{
	KickClient(index); //for now
}