/**
 * Whether we are attempting to join or host a game.
 */
var g_IsConnecting = false;

/**
 * "server" or "client"
 */
var g_GameType;

/**
 * Server title shown in the lobby gamelist.
 */
var g_ServerName = "";

/**
 * Cached to pass it to the gamesetup of the controller to report the game to the lobby.
 */
var g_ServerPort;

var g_IsRejoining = false;
var g_GameAttributes; // used when rejoining
var g_PlayerAssignments; // used when rejoining
var g_UserRating;

function init(attribs)
{
	g_UserRating = attribs.rating;

	switch (attribs.multiplayerGameType)
	{
	case "join":
	{
		if (Engine.HasXmppClient())
		{
			if (startJoin(attribs.name, attribs.ip, getValidPort(attribs.port)))
				switchSetupPage("pageConnecting");
		}
		else
			switchSetupPage("pageJoin");
		break;
	}
	case "host":
	{
		if (Engine.HasXmppClient())
		{
			Engine.GetGUIObjectByName("hostPlayerName").caption = attribs.name;
			Engine.GetGUIObjectByName("hostServerName").caption =
				sprintf(translate("%(name)s's game"), { "name": attribs.name });
		}

		switchSetupPage("pageHost");
		break;
	}
	default:
		error("Unrecognised multiplayer game type: " + attribs.multiplayerGameType);
		break;
	}
}

function cancelSetup()
{
	if (g_IsConnecting)
		Engine.DisconnectNetworkGame();

	if (Engine.HasXmppClient())
		Engine.LobbySetPlayerPresence("available");

	// Keep the page open if an attempt to join/host by ip failed
	if (!g_IsConnecting || (Engine.HasXmppClient() && g_GameType == "client"))
	{
		Engine.PopGuiPage();
		return;
	}

	g_IsConnecting = false;
	Engine.GetGUIObjectByName("hostFeedback").caption = "";

	if (g_GameType == "client")
		switchSetupPage("pageJoin");
	else if (g_GameType == "server")
		switchSetupPage("pageHost");
	else
		error("cancelSetup: Unrecognised multiplayer game type: " + g_GameType);
}

function confirmSetup()
{
	if (!Engine.GetGUIObjectByName("pageJoin").hidden)
	{
		let joinPlayerName = Engine.GetGUIObjectByName("joinPlayerName").caption;
		let joinServer = Engine.GetGUIObjectByName("joinServer").caption;
		let joinPort = Engine.GetGUIObjectByName("joinPort").caption;

		if (startJoin(joinPlayerName, joinServer, getValidPort(joinPort)))
			switchSetupPage("pageConnecting");
	}
	else if (!Engine.GetGUIObjectByName("pageHost").hidden)
	{
		let hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption;
		let hostServerName = Engine.GetGUIObjectByName("hostServerName").caption;
		let hostPort = Engine.GetGUIObjectByName("hostPort").caption;

		if (startHost(hostPlayerName, hostServerName, getValidPort(hostPort)))
			switchSetupPage("pageConnecting");
	}
}

function startConnectionStatus(type)
{
	g_GameType = type;
	g_IsConnecting = true;
	g_IsRejoining = false;
	Engine.GetGUIObjectByName("connectionStatus").caption = translate("Connecting to server...");
}

function onTick()
{
	if (!g_IsConnecting)
		return;

	pollAndHandleNetworkClient();
}

function pollAndHandleNetworkClient()
{
	while (true)
	{
		var message = Engine.PollNetworkClient();
		if (!message)
			break;

		log(sprintf(translate("Net message: %(message)s"), { "message": uneval(message) }));

		// If we're rejoining an active game, we don't want to actually display
		// the game setup screen, so perform similar processing to gamesetup.js
		// in this screen
		if (g_IsRejoining)
		{
			switch (message.type)
			{
			case "netstatus":
				switch (message.status)
				{
				case "disconnected":
					cancelSetup();
					reportDisconnect(message.reason, false);
					return;

				default:
					error("Unrecognised netstatus type: " + message.status);
					break;
				}
				break;

			case "gamesetup":
				g_GameAttributes = message.data;
				break;

			case "players":
				g_PlayerAssignments = message.newAssignments;
				break;

			case "start":

				// Copy playernames from initial player assignment to the settings
				for (let guid in g_PlayerAssignments)
				{
					let player = g_PlayerAssignments[guid];
					if (player.player > 0)	// not observer or GAIA
						g_GameAttributes.settings.PlayerData[player.player - 1].Name = player.name;
				}

				Engine.SwitchGuiPage("page_loading.xml", {
					"attribs": g_GameAttributes,
					"isNetworked" : true,
					"isRejoining" : g_IsRejoining,
					"playerAssignments": g_PlayerAssignments
				});
				break;

			case "chat":
				break;

			case "netwarn":
				break;

			default:
				error("Unrecognised net message type: " + message.type);
			}
		}
		else
		{
			// Not rejoining - just trying to connect to server

			switch (message.type)
			{
			case "netstatus":
				switch (message.status)
				{
				case "connected":
					Engine.GetGUIObjectByName("connectionStatus").caption = translate("Registering with server...");
					break;

				case "authenticated":
					if (message.rejoining)
					{
						Engine.GetGUIObjectByName("connectionStatus").caption = translate("Game has already started, rejoining...");
						g_IsRejoining = true;
						return; // we'll process the game setup messages in the next tick
					}
					else
					{
						Engine.SwitchGuiPage("page_gamesetup.xml", {
							"type": g_GameType,
							"serverName": g_ServerName,
							"serverPort": g_ServerPort
						});
						return; // don't process any more messages - leave them for the game GUI loop
					}

				case "disconnected":
					cancelSetup();
					reportDisconnect(message.reason, false);
					return;

				default:
					error("Unrecognised netstatus type: " + message.status);
					break;
				}
				break;

			case "netwarn":
				break;

			default:
				error("Unrecognised net message type: " + message.type);
				break;
			}
		}
	}
}

function switchSetupPage(newPage)
{
	for (let page of Engine.GetGUIObjectByName("multiplayerPages").children)
		if (page.name.substr(0,4) == "page")
			page.hidden = true;

	Engine.GetGUIObjectByName(newPage).hidden = false;

	Engine.GetGUIObjectByName("hostPlayerNameWrapper").hidden = Engine.HasXmppClient();
	Engine.GetGUIObjectByName("hostServerNameWrapper").hidden = !Engine.HasXmppClient();

	Engine.GetGUIObjectByName("continueButton").hidden = newPage == "pageConnecting";
}

function startHost(playername, servername, port)
{
	startConnectionStatus("server");

	// Save player name
	Engine.ConfigDB_CreateValue("user", "playername.multiplayer", playername);
	Engine.ConfigDB_WriteValueToFile("user", "playername.multiplayer", playername, "config/user.cfg");

	// Save port
	Engine.ConfigDB_CreateValue("user", "multiplayerhosting.port", port);
	Engine.ConfigDB_WriteValueToFile("user", "multiplayerhosting.port", port, "config/user.cfg");

	// Disallow identically named games in the multiplayer lobby
	if (Engine.HasXmppClient() &&
	    Engine.GetGameList().some(game => game.name == servername))
	{
		cancelSetup();
		Engine.GetGUIObjectByName("hostFeedback").caption =
			translate("Game name already in use.");
		return false;
	}

	try
	{
		if (g_UserRating)
			Engine.StartNetworkHost(playername + " (" + g_UserRating + ")", port);
		else
			Engine.StartNetworkHost(playername, port);
	}
	catch (e)
	{
		cancelSetup();
		messageBox(
			400, 200,
			sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }),
			translate("Error")
		);
		return false;
	}

	g_ServerName = servername;
	g_ServerPort = port;

	if (Engine.HasXmppClient())
		Engine.LobbySetPlayerPresence("playing");

	return true;
}

function startJoin(playername, ip, port)
{
	try
	{
		if (g_UserRating)
			Engine.StartNetworkJoin(playername + " (" + g_UserRating + ")", ip, port);
		else
			Engine.StartNetworkJoin(playername, ip, port);
	}
	catch (e)
	{
		cancelSetup();
		messageBox(
			400, 200,
			sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }),
			translate("Error")
		);
		return false;
	}

	startConnectionStatus("client");

	if (Engine.HasXmppClient())
		Engine.LobbySetPlayerPresence("playing");
	else
	{
		// Only save the player name and host address if they're valid and we're not in the lobby
		Engine.ConfigDB_CreateValue("user", "playername.multiplayer", playername);
		Engine.ConfigDB_WriteValueToFile("user", "playername.multiplayer", playername, "config/user.cfg");
		Engine.ConfigDB_CreateValue("user", "multiplayerserver", ip);
		Engine.ConfigDB_WriteValueToFile("user", "multiplayerserver", ip, "config/user.cfg");
		Engine.ConfigDB_CreateValue("user", "multiplayerjoining.port", port);
		Engine.ConfigDB_WriteValueToFile("user", "multiplayerjoining.port", port, "config/user.cfg");
	}
	return true;
}

function getDefaultGameName()
{
	return sprintf(translate("%(playername)s's game"), {
		"playername": multiplayerName()
	});
}
