context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using OpenRA.Graphics;
  using OpenRA.Widgets;
  
  namespace OpenRA.Mods.Common.Widgets.Logic
  {
  	public class SettingsLogic
  	{
  		enum PanelType { Display, Audio, Input, Advanced }
  		Dictionary<PanelType, Action> leavePanelActions = new Dictionary<PanelType, Action>();
  		Dictionary<PanelType, Action> resetPanelActions = new Dictionary<PanelType, Action>();
  		PanelType settingsPanel = PanelType.Display;
  		Widget panelContainer, tabContainer;
  
  		WorldRenderer worldRenderer;
  		SoundDevice soundDevice;
  
  		static readonly string OriginalSoundDevice;
  		static readonly string OriginalSoundEngine;
  		static readonly WindowMode OriginalGraphicsMode;
  		static readonly string OriginalGraphicsRenderer;
  		static readonly int2 OriginalGraphicsWindowedSize;
  		static readonly int2 OriginalGraphicsFullscreenSize;
  
  		static SettingsLogic()
  		{
  			var original = Game.Settings;
  			OriginalSoundDevice = original.Sound.Device;
  			OriginalSoundEngine = original.Sound.Engine;
  			OriginalGraphicsMode = original.Graphics.Mode;
  			OriginalGraphicsRenderer = original.Graphics.Renderer;
  			OriginalGraphicsWindowedSize = original.Graphics.WindowedSize;
  			OriginalGraphicsFullscreenSize = original.Graphics.FullscreenSize;
  		}
  
  		[ObjectCreator.UseCtor]
  		public SettingsLogic(Widget widget, Action onExit, WorldRenderer worldRenderer)
  		{
  			this.worldRenderer = worldRenderer;
  
  			panelContainer = widget.Get("SETTINGS_PANEL");
  			tabContainer = widget.Get("TAB_CONTAINER");
  
  			RegisterSettingsPanel(PanelType.Display, InitDisplayPanel, ResetDisplayPanel, "DISPLAY_PANEL", "DISPLAY_TAB");
  			RegisterSettingsPanel(PanelType.Audio, InitAudioPanel, ResetAudioPanel, "AUDIO_PANEL", "AUDIO_TAB");
  			RegisterSettingsPanel(PanelType.Input, InitInputPanel, ResetInputPanel, "INPUT_PANEL", "INPUT_TAB");
  			RegisterSettingsPanel(PanelType.Advanced, InitAdvancedPanel, ResetAdvancedPanel, "ADVANCED_PANEL", "ADVANCED_TAB");
  
  			panelContainer.Get<ButtonWidget>("BACK_BUTTON").OnClick = () =>
  			{
  				leavePanelActions[settingsPanel]();
  				var current = Game.Settings;
  				current.Save();
  
  				Action closeAndExit = () => { Ui.CloseWindow(); onExit(); };
  				if (OriginalSoundDevice != current.Sound.Device ||
  					OriginalSoundEngine != current.Sound.Engine ||
  					OriginalGraphicsMode != current.Graphics.Mode ||
  					OriginalGraphicsRenderer != current.Graphics.Renderer ||
  					OriginalGraphicsWindowedSize != current.Graphics.WindowedSize ||
  					OriginalGraphicsFullscreenSize != current.Graphics.FullscreenSize)
  					ConfirmationDialogs.PromptConfirmAction(
  						"Restart Now?",
  						"Some changes will not be applied until\nthe game is restarted. Restart now?",
  						Game.Restart,
  						closeAndExit,
  						"Restart Now",
  						"Restart Later");
  				else
  					closeAndExit();
  			};
  
  			panelContainer.Get<ButtonWidget>("RESET_BUTTON").OnClick = () =>
  			{
  				resetPanelActions[settingsPanel]();
  				Game.Settings.Save();
  			};
  		}
  
  		static void BindCheckboxPref(Widget parent, string id, object group, string pref)
  		{
  			var field = group.GetType().GetField(pref);
  			if (field == null)
  				throw new InvalidOperationException("{0} does not contain a preference type {1}".F(group.GetType().Name, pref));
  
  			var cb = parent.Get<CheckboxWidget>(id);
  			cb.IsChecked = () => (bool)field.GetValue(group);
  			cb.OnClick = () => field.SetValue(group, cb.IsChecked() ^ true);
  		}
  
  		static void BindSliderPref(Widget parent, string id, object group, string pref)
  		{
  			var field = group.GetType().GetField(pref);
  			if (field == null)
  				throw new InvalidOperationException("{0} does not contain a preference type {1}".F(group.GetType().Name, pref));
  
  			var ss = parent.Get<SliderWidget>(id);
  			ss.Value = (float)field.GetValue(group);
  			ss.OnChange += x => field.SetValue(group, x);
  		}
  
  		static void BindHotkeyPref(KeyValuePair<string, string> kv, KeySettings ks, Widget template, Widget parent)
  		{
  			var key = template.Clone() as Widget;
  			key.Id = kv.Key;
  			key.IsVisible = () => true;
  
  			var field = ks.GetType().GetField(kv.Key);
  			if (field == null)
  				throw new InvalidOperationException("Game.Settings.Keys does not contain {1}".F(kv.Key));
  
  			key.Get<LabelWidget>("FUNCTION").GetText = () => kv.Value + ":";
  
  			var textBox = key.Get<HotkeyEntryWidget>("HOTKEY");
  			textBox.Key = (Hotkey)field.GetValue(ks);
  			textBox.OnLoseFocus = () => field.SetValue(ks, textBox.Key);
  			parent.AddChild(key);
  		}
  
  		void RegisterSettingsPanel(PanelType type, Func<Widget, Action> init, Func<Widget, Action> reset, string panelID, string buttonID)
  		{
  			var panel = panelContainer.Get(panelID);
  			var tab = tabContainer.Get<ButtonWidget>(buttonID);
  
  			panel.IsVisible = () => settingsPanel == type;
  			tab.IsHighlighted = () => settingsPanel == type;
  			tab.OnClick = () => { leavePanelActions[settingsPanel](); Game.Settings.Save(); settingsPanel = type; };
  
  			leavePanelActions.Add(type, init(panel));
  			resetPanelActions.Add(type, reset(panel));
  		}
  
  		Action InitDisplayPanel(Widget panel)
  		{
  			var ds = Game.Settings.Graphics;
  			var gs = Game.Settings.Game;
  
  			BindCheckboxPref(panel, "HARDWARECURSORS_CHECKBOX", ds, "HardwareCursors");
  			BindCheckboxPref(panel, "PIXELDOUBLE_CHECKBOX", ds, "PixelDouble");
  			BindCheckboxPref(panel, "CURSORDOUBLE_CHECKBOX", ds, "CursorDouble");
  			BindCheckboxPref(panel, "FRAME_LIMIT_CHECKBOX", ds, "CapFramerate");
  			BindCheckboxPref(panel, "SHOW_SHELLMAP", gs, "ShowShellmap");
  			BindCheckboxPref(panel, "ALWAYS_SHOW_STATUS_BARS_CHECKBOX", gs, "AlwaysShowStatusBars");
  			BindCheckboxPref(panel, "DISPLAY_TARGET_LINES_CHECKBOX", gs, "DrawTargetLine");
  			BindCheckboxPref(panel, "TEAM_HEALTH_COLORS_CHECKBOX", gs, "TeamHealthColors");
  
  			var languageDropDownButton = panel.Get<DropDownButtonWidget>("LANGUAGE_DROPDOWNBUTTON");
  			languageDropDownButton.OnMouseDown = _ => ShowLanguageDropdown(languageDropDownButton);
  			languageDropDownButton.GetText = () => FieldLoader.Translate(ds.Language);
  
  			var windowModeDropdown = panel.Get<DropDownButtonWidget>("MODE_DROPDOWN");
  			windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, ds);
  			windowModeDropdown.GetText = () => ds.Mode == WindowMode.Windowed ?
  				"Windowed" : ds.Mode == WindowMode.Fullscreen ? "Fullscreen" : "Pseudo-Fullscreen";
  
  			// Update zoom immediately
  			var pixelDoubleCheckbox = panel.Get<CheckboxWidget>("PIXELDOUBLE_CHECKBOX");
  			var pixelDoubleOnClick = pixelDoubleCheckbox.OnClick;
  			pixelDoubleCheckbox.OnClick = () =>
  			{
  				pixelDoubleOnClick();
  				worldRenderer.Viewport.Zoom = ds.PixelDouble ? 2 : 1;
  			};
  
  			// Cursor doubling is only supported with software cursors and when pixel doubling is enabled
  			var cursorDoubleCheckbox = panel.Get<CheckboxWidget>("CURSORDOUBLE_CHECKBOX");
  			cursorDoubleCheckbox.IsDisabled = () => !ds.PixelDouble || Game.Cursor is HardwareCursor;
  
  			var cursorDoubleIsChecked = cursorDoubleCheckbox.IsChecked;
  			cursorDoubleCheckbox.IsChecked = () => !cursorDoubleCheckbox.IsDisabled() && cursorDoubleIsChecked();
  
  			panel.Get("WINDOW_RESOLUTION").IsVisible = () => ds.Mode == WindowMode.Windowed;
  			var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH");
  			windowWidth.Text = ds.WindowedSize.X.ToString();
  
  			var windowHeight = panel.Get<TextFieldWidget>("WINDOW_HEIGHT");
  			windowHeight.Text = ds.WindowedSize.Y.ToString();
  
  			var frameLimitTextfield = panel.Get<TextFieldWidget>("FRAME_LIMIT_TEXTFIELD");
  			frameLimitTextfield.Text = ds.MaxFramerate.ToString();
  			var escPressed = false;
  			frameLimitTextfield.OnLoseFocus = () =>
  			{
  				if (escPressed)
  				{
  					escPressed = false;
  					return;
  				}
  
  				int fps;
  				Exts.TryParseIntegerInvariant(frameLimitTextfield.Text, out fps);
  				ds.MaxFramerate = fps.Clamp(1, 1000);
  
gt: 				frameLimitTextfield.Text = ds.MaxFramerate.ToString();