var g_hasCallback = false;
/**
 * This array holds the data to populate the general section with.
 * Data is in the form [Title, Tooltip, {ActionType:Action}, InputType].
 */
var options = {
	"generalSetting":
	[
		[translate("Windowed Mode"), translate("Start 0 A.D. in a window"), {"config":"windowed"}, "boolean"],
		[translate("Background Pause"), translate("Pause single player games when window loses focus"), {"config":"pauseonfocusloss"}, "boolean"],
		[translate("Disable Welcome Screen"), translate("If you disable this screen completely, you may miss important announcements.\nYou can still launch it using the main menu."), {"config":"splashscreendisable"}, "boolean"],
		[translate("Detailed Tooltips"), translate("Show detailed tooltips for trainable units in unit-producing buildings."), {"config":"showdetailedtooltips"}, "boolean"],
		[translate("FPS Overlay"), translate("Show frames per second in top right corner."), {"config":"overlay.fps"}, "boolean"],
		[translate("Realtime Overlay"), translate("Show current system time in top right corner."), {"config":"overlay.realtime"}, "boolean"],
		[translate("Gametime Overlay"), translate("Show current simulation time in top right corner."), {"config":"gui.session.timeelapsedcounter"}, "boolean"],
		[translate("Persist match settings"), translate("Save and restore match settings for quick reuse when hosting another game"), {"config":"persistmatchsettings"}, "boolean"],
	],
	"graphicsSetting":
	[
		[translate("Prefer GLSL"), translate("Use OpenGL 2.0 shaders (recommended)"), {"renderer":"PreferGLSL", "config":"preferglsl"}, "boolean"],
		[translate("Post Processing"), translate("Use screen-space postprocessing filters (HDR, Bloom, DOF, etc)"), {"renderer":"Postproc", "config":"postproc"}, "boolean"],
		[translate("Shadows"), translate("Enable shadows"), {"renderer":"Shadows", "config":"shadows"}, "boolean"],
		[translate("Particles"), translate("Enable particles"), {"renderer":"Particles", "config":"particles"}, "boolean"],
		[translate("Show Sky"), translate("Render Sky"), {"renderer":"ShowSky", "config":"showsky"}, "boolean"],
		[translate("Smooth LOS"), translate("Lift darkness and fog-of-war smoothly"), {"renderer":"SmoothLOS", "config":"smoothlos"}, "boolean"],
		[translate("Unit Silhouettes"), translate("Show outlines of units behind buildings"), {"renderer":"Silhouettes", "config":"silhouettes"}, "boolean"],
		[translate("Shadow Filtering"), translate("Smooth shadows"), {"renderer":"ShadowPCF", "config":"shadowpcf"}, "boolean"],
		[translate("Fast & Ugly Water"), translate("Use the lowest settings possible to render water. This makes other settings irrelevant."), {"renderer":"WaterUgly", "config":"waterugly"}, "boolean"],
		[translate("HQ Water Effects"), translate("Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails."), {"renderer":"WaterFancyEffects", "config":"waterfancyeffects"}, "boolean"],
		[translate("Real Water Depth"), translate("Use actual water depth in rendering calculations"), {"renderer":"WaterRealDepth", "config":"waterrealdepth"}, "boolean"],
		[translate("Water Reflections"), translate("Allow water to reflect a mirror image"), {"renderer":"WaterReflection", "config":"waterreflection"}, "boolean"],
		[translate("Water Refraction"), translate("Use a real water refraction map and not transparency"), {"renderer":"WaterRefraction", "config":"waterrefraction"}, "boolean"],
		[translate("Shadows on Water"), translate("Cast shadows on water"), {"renderer":"WaterShadows", "config":"watershadows"}, "boolean"],
		[translate("VSync"), translate("Run vertical sync to fix screen tearing. REQUIRES GAME RESTART"), {"config":"vsync"}, "boolean"],
	],
	"soundSetting":
	[
		[translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"],
		[translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
		[translate("Ambient Gain"), translate("In game ambient sound gain"), {"config":"sound.ambientgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
		[translate("Action Gain"), translate("In game unit action sound gain"), {"config":"sound.actiongain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
		[translate("UI Gain"), translate("UI sound gain"), {"config":"sound.uigain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
	],
	"lobbySetting":
	[
		[translate("Chat Backlog"), translate("Number of backlogged messages to load when joining the lobby"), {"config":"lobby.history"}, "number"],
		[translate("Chat Timestamp"), translate("Show time that messages are posted in the lobby chat"), {"config":"lobby.chattimestamp"}, "boolean"],
	],
};

function init(data)
{
	if (data && data.callback)
		g_hasCallback = true;

	// WARNING: We assume a strict formatting of the XML and do minimal checking.
	for each (var prefix in Object.keys(options))
	{
		var lastSize;
		for (var i = 0; i < options[prefix].length; i++)
		{
			var body = Engine.GetGUIObjectByName(prefix + "[" + i + "]");
			var label = Engine.GetGUIObjectByName(prefix + "Label[" + i + "]");
			// Setup control.
			setupControl(options[prefix][i], i, prefix);
			// Setup label.
			label.caption = options[prefix][i][0];
			label.tooltip = options[prefix][i][1];
			// Move each element to the correct place.
			if (i > 0)
			{
				var newSize = new GUISize();
				newSize.left = lastSize.left;
				newSize.rright = lastSize.rright;
				newSize.top = lastSize.bottom;
				newSize.bottom = newSize.top + 25;
				body.size = newSize;
				lastSize = newSize;
			}
			else
			{
				lastSize = body.size;
			}
			// Show element.
			body.hidden = false;
		}
	}
}

/**
 * Setup the apropriate control for a given option.
 *
 * @param option Structure containing the data to setup an option.
 * @param prefix Prefix to use when accessing control, for example "generalSetting" when the tickbox name is generalSettingTickbox[i].
 */
function setupControl(option, i, prefix)
{
	switch (option[3])
	{
		case "boolean":
			// More space for the label
			let text = Engine.GetGUIObjectByName(prefix + "Label[" + i + "]");
			let size = text.size;
			size.rright = 87;
			text.size = size;
			var control = Engine.GetGUIObjectByName(prefix + "Tickbox[" + i + "]");
			var checked;
			var onPress = function(){};
			// Different option action load and save differently, so this switch is needed.
			for each (var action in Object.keys(option[2]))
			{
				switch (action)
				{
					case "config":
						// Load initial value if not yet loaded.
						if (!checked || typeof checked != "boolean")
							checked = Engine.ConfigDB_GetValue("user", option[2][action]) === "true" ? true : false;
						// Hacky macro to create the callback.
						var callback = function(key)
						{
							return function()
								Engine.ConfigDB_CreateValue("user", key, String(this.checked));
						}(option[2][action]);
						// Merge the new callback with any existing callbacks.
						onPress = mergeFunctions(callback, onPress);
						// TODO: Remove this once GLSL works without GenTangents (#2506)
						if (option[2][action] == "preferglsl")
						{
							callback = function()
								Engine.ConfigDB_CreateValue("user", "gentangents", String(this.checked));
							onPress = mergeFunctions(callback, onPress);
						}
						break;
					case "renderer":
						// Load initial value if not yet loaded.
						if (!checked || typeof checked != "boolean")
							checked = eval("Engine.Renderer_Get" + option[2][action] + "Enabled()");
						// Hacky macro to create the callback.
						var callback = function(key)
						{
							return function()
								eval("Engine.Renderer_Set" + key + "Enabled(" + this.checked + ")");
						}(option[2][action]);
						// Merge the new callback with any existing callbacks.
						onPress = mergeFunctions(callback, onPress);
						// TODO: Remove this once GLSL works without GenTangents (#2506)
						if (option[2][action] == "PreferGLSL")
						{
							callback = function()
								eval("Engine.Renderer_SetGenTangentsEnabled(" + this.checked + ")");
							onPress = mergeFunctions(callback, onPress);
						}
						break;
					case "function":
						// This allows for doing low-level actions, like hiding/showing UI elements.
						onPress = mergeFunctions(eval("function(){" + option[2][action] + "}"), onPress);
						break;
					default:
						warn("Unknown option source type '" + action + "'");
				}
			}
			// Load final data to the control element.
			control.checked = checked;
			control.onPress = onPress;
			break;
		case "number":
			// TODO: Slider
		case "string":
			var control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
			var caption;
			var onPress = function(){};
			for each (var action in Object.keys(option[2]))
			{
				switch (action)
				{
					case "config":
						// Load initial value if not yet loaded.
						if (!checked || typeof checked != boolean)
							caption = Engine.ConfigDB_GetValue("user", option[2][action]);;
						// Hacky macro to create the callback.
						var callback = function(key)
						{
							return function()
								Engine.ConfigDB_CreateValue("user", key, String(this.caption));
						}(option[2][action]);
						// Merge the new callback with any existing callbacks.
						onPress = mergeFunctions(callback, onPress);
						break;
					case "function":
						// This allows for doing low-level actions, like hiding/showing UI elements.
						onPress = mergeFunctions(function(){eval(option[2][action])}, onPress);
						break;
					default:
						warn("Unknown option source type '" + action + "'");
				}
			}
			control.caption = caption;
			control.onPress = onPress;
			break;
		default:
			warn("Unknown option type '" + options[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
			var control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
			break;
	}
	control.hidden = false;
	control.tooltip = option[1];
	return control;
}

/**
 * Merge two functions which don't expect arguments.
 *
 * @return Merged function.
 */
function mergeFunctions(function1, function2)
{
	return function()
	{
		function1.apply(this);
		function2.apply(this);
	};
}

/**
 * Close GUI page and call callbacks if they exist.
 **/
function closePage()
{
	if (g_hasCallback)
		Engine.PopGuiPageCB();
	else
		Engine.PopGuiPage();
}
