//////////////////////////////////////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN

//////////////////////////////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <objbase.h>
#include <winsock.h>

#include "CINIStrTable.h"
#include "i_net.h"

#include "resource.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////

#define mst_server 100
#define mst_end    101

#define MAX_SERVERS	64
#define MAX_SERVERS_FROM_ONE_IP	4
#define MAX_SERVERS_AGE	60*4 // 1 minute

int       msg;
netadr_t  masteraddr; // address of the master server
char      masterip[] = "localhost";

buf_t   message;
byte    message_buffer[MAX_UDP_PACKET];

netadr_t addr;

//////////////////////////////////////////////////////////////////////////////////////////////////////

CINIStrTable INIServerTable;
CINIStrTable INIMasterServerTable;

//////////////////////////////////////////////////////////////////////////////////////////////////////

HWND hWnd;
HFONT hFont;

//////////////////////////////////////////////////////////////////////////////////////////////////////

int iRequestInfoFromMasters(void)
{
	int x;

	for(x = 0; x < INIMasterServerTable.iGetNumEntries(); x++)
	{
		if(!NET_StringToAdr(INIMasterServerTable.GetEntryColumn(x, 0), &addr))
			return false;
		I_SetPort(addr, MASTERPORT);
		SZ_Clear(&message);
		MSG_WriteLong(&message, LAUNCHER_CHALLENGE);
		NET_SendPacket(message.cursize, message.data, addr);

		INIMasterServerTable.SetEntryColumn(x, 2, NET_AdrToString(addr));
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

int iRequestInfoFromServer(char *cIP)
{
	if(!NET_StringToAdr(cIP, &addr))
		return false;

	SZ_Clear(&message);
	MSG_WriteLong(&message, LAUNCHER_CHALLENGE);

	NET_SendPacket(message.cursize, message.data, addr);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

int ProcessIncomingData(void)
{
	int x;
	char *cAddy;
	bool NeedUpdate = false;

	if(!NET_GetPacket())
		return false;
	
	int challenge = MSG_ReadLong();

	switch(challenge)
	{
	case LAUNCHER_CHALLENGE:
		// From master server
		x = INIMasterServerTable.iFindEntry(2, NET_AdrToString(net_from));
		if(!x || MASTERPORT != htons(net_from.port))
			break;
		// We have data!
		while(MSG_ReadByte() == mst_server)
		{
			cAddy = MSG_ReadString();
			if(INIServerTable.iFindEntry(0, cAddy) < 0)
			{
				INIServerTable.SetEntryColumn(INIServerTable.iGetNumEntries(), 0, cAddy);
				NeedUpdate = true;
				// Now send a request to this server
				iRequestInfoFromServer(cAddy);
			}
		}
		break;
	case CHALLENGE:
		// From a server
		break;
	default:
		break;
	}

	return NeedUpdate;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

int InsertColumn(HWND hLV, char *Name, int Width, BOOL Left)
{
	LVCOLUMN theColumn;
	ZeroMemory(&theColumn,sizeof(theColumn));
	theColumn.mask=LVCF_TEXT|LVCF_WIDTH|LVCF_FMT;
	theColumn.pszText=Name;
	theColumn.cx=Width;
	theColumn.iSubItem=0;
	if(Left)theColumn.fmt=LVCFMT_LEFT;
	else theColumn.fmt=LVCFMT_RIGHT;
	return(ListView_InsertColumn(hLV,0,&theColumn));
}

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

int InsertItem(HWND hLV, char *Name, ULONG itemID)
{
	LVITEM theList;
	ZeroMemory(&theList,sizeof(theList));
	theList.mask=LVIF_PARAM|LVIF_TEXT|LVIF_IMAGE|LVIF_NORECOMPUTE;
	theList.iItem = itemID;
	theList.iSubItem = 0;
	theList.state = 0;//LVIS_OVERLAYMASK;
	theList.stateMask = 0;//LVIS_SELECTED;
	theList.pszText = Name;
	theList.iImage = itemID;
	theList.lParam = itemID;
	return(ListView_InsertItem(hLV,&theList));
}

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

int InsertItemDesc(HWND hLV, int item, int column, char *Desc)
{
	ListView_SetItemText(hLV,item,column,Desc);
	return(1);
}

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

int InsertServerDesc(HWND hLV, char *Name, char *IP, char *Ping, char *Players, ULONG itemID)
{
	int index = InsertItem(hLV, Name, itemID);
	InsertItemDesc(hLV, index, 1, IP);
	InsertItemDesc(hLV, index, 2, Ping);
	InsertItemDesc(hLV, index, 3, Players);
	return(index);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK DlgMain(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	hWnd = hwnd;
	int x;

	switch(msg)
	{
	case WM_INITDIALOG:
		// Load the server list
		INIServerTable.LoadFromFile("server.list");
		INIMasterServerTable.LoadFromFile("master.list");
		// Initialise the server list view
		InsertColumn(GetDlgItem(hwnd, IRC_SERVER_LIST), "Players", 50, true);
		InsertColumn(GetDlgItem(hwnd, IRC_SERVER_LIST), "Ping", 40, true);
		InsertColumn(GetDlgItem(hwnd, IRC_SERVER_LIST), "Address", 131, true);
		InsertColumn(GetDlgItem(hwnd, IRC_SERVER_LIST), "Server Name", 225, true);
		// MAP & WAD?
		ListView_DeleteAllItems(GetDlgItem(hwnd, IRC_SERVER_LIST));
		for(x = 0; x < INIServerTable.iGetNumEntries(); x++)
		{
			InsertServerDesc(GetDlgItem(hwnd, IRC_SERVER_LIST), INIServerTable.GetEntryColumn(x, 1), INIServerTable.GetEntryColumn(x, 0), "????", "?/?", 0);
		}
		// Send out a request to master servers for the latest server list
		iRequestInfoFromMasters();
		// Set up timer for network checks
		SetTimer(hwnd, NULL, 200, NULL);
		break;
	case WM_CLOSE:
		INIServerTable.SaveToFile("server.list");
		INIMasterServerTable.SaveToFile("master.list");
		DeleteFont(hFont);
		KillTimer(hwnd, NULL);
		EndDialog(hwnd, NULL);
		break;
	case WM_TIMER:
		if(ProcessIncomingData())
		{
			ListView_DeleteAllItems(GetDlgItem(hwnd, IRC_SERVER_LIST));

			for(x = 0; x < INIServerTable.iGetNumEntries(); x++)
			{
				InsertServerDesc(GetDlgItem(hWnd, IRC_SERVER_LIST), INIServerTable.GetEntryColumn(x, 1), INIServerTable.GetEntryColumn(x, 0), "????", "?/?", 0);
			}
		}
		break;
	case WM_COMMAND:
		switch(wParam)
		{
		case IDC_UPDATE_LIST:
			ListView_DeleteAllItems(GetDlgItem(hwnd, IRC_SERVER_LIST));
			INIServerTable.ClearEntries();
			iRequestInfoFromMasters();
			break;
		case IDC_JOIN_GAME:
			break;
		case IDC_REFRESH_SERVERS:
			break;
		case IDC_START_SERVER:
			break;
		default:
			break;
		}
	default:
		break;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////

int	WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	DLGPROC dlgprc = MakeProcInstance(DlgMain, hInstance);
	CoInitialize(NULL);

	localport = LAUNCHERPORT;
	InitNetCommon();

    message.data = message_buffer;
	message.maxsize = sizeof(message_buffer);

	int iRet = (int)DialogBox(hInstance, MAKEINTRESOURCE(MAIN_WINDOW), NULL, (DLGPROC)dlgprc);

	CloseNetwork();

	CoUninitialize();
	return iRet;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////