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.Drawing;
  using System.Linq;
  using System.Threading;
  using OpenRA.Graphics;
  using OpenRA.Mods.Common.Traits;
  using OpenRA.Network;
  using OpenRA.Traits;
  using OpenRA.Widgets;
  
  namespace OpenRA.Mods.Common.Widgets.Logic
  {
  	public class LobbyLogic : ChromeLogic
  	{
  		static readonly Action DoNothing = () => { };
  
  		public MapPreview Map = MapCache.UnknownMap;
  
  		readonly Action onStart;
  		readonly Action onExit;
  		readonly OrderManager orderManager;
  		readonly bool skirmishMode;
  		readonly Ruleset modRules;
  		readonly World shellmapWorld;
  
  		enum PanelType { Players, Options, Music, Kick, ForceStart }
  		PanelType panel = PanelType.Players;
  
  		enum ChatPanelType { Lobby, Global }
  		ChatPanelType chatPanel = ChatPanelType.Lobby;
  
  		readonly Widget lobby;
  		readonly Widget editablePlayerTemplate;
  		readonly Widget nonEditablePlayerTemplate;
  		readonly Widget emptySlotTemplate;
  		readonly Widget editableSpectatorTemplate;
  		readonly Widget nonEditableSpectatorTemplate;
  		readonly Widget newSpectatorTemplate;
  
  		readonly ScrollPanelWidget lobbyChatPanel;
  		readonly Widget chatTemplate;
  
  		readonly ScrollPanelWidget players;
  
  		readonly Dictionary<string, LobbyFaction> factions = new Dictionary<string, LobbyFaction>();
  
  		readonly ColorPreviewManagerWidget colorPreview;
  
  		readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
  
  		readonly LabelWidget chatLabel;
  		bool teamChat;
  
  		int lobbyChatUnreadMessages;
  		int globalChatLastReadMessages;
  		int globalChatUnreadMessages;
  
  		// Listen for connection failures
  		void ConnectionStateChanged(OrderManager om)
  		{
  			if (om.Connection.ConnectionState == ConnectionState.NotConnected)
  			{
  				// Show connection failed dialog
  				CloseWindow();
  
  				Action onConnect = () =>
  				{
  					Game.OpenWindow("SERVER_LOBBY", new WidgetArgs()
  					{
  						{ "onExit", onExit },
  						{ "onStart", onStart },
  						{ "skirmishMode", false }
  					});
  				};
  
  				Action<string> onRetry = password => ConnectionLogic.Connect(om.Host, om.Port, password, onConnect, onExit);
  
  				Ui.OpenWindow("CONNECTIONFAILED_PANEL", new WidgetArgs()
  				{
  					{ "orderManager", om },
  					{ "onAbort", onExit },
  					{ "onRetry", onRetry }
  				});
  			}
  		}
  
  		void CloseWindow()
  		{
  			orderManager.AddChatLine -= AddChatLine;
  			Game.LobbyInfoChanged -= UpdateCurrentMap;
  			Game.LobbyInfoChanged -= UpdatePlayerList;
  			Game.BeforeGameStart -= OnGameStart;
  			Game.ConnectionStateChanged -= ConnectionStateChanged;
  
  			Ui.CloseWindow();
  		}
  
  		[ObjectCreator.UseCtor]
  		internal LobbyLogic(Widget widget, WorldRenderer worldRenderer, OrderManager orderManager,
  			Action onExit, Action onStart, bool skirmishMode, Ruleset modRules)
  		{
  			lobby = widget;
  			this.orderManager = orderManager;
  			this.onStart = onStart;
  			this.onExit = onExit;
  			this.skirmishMode = skirmishMode;
  			this.modRules = modRules;
  			shellmapWorld = worldRenderer.World;
  
  			orderManager.AddChatLine += AddChatLine;
  			Game.LobbyInfoChanged += UpdateCurrentMap;
  			Game.LobbyInfoChanged += UpdatePlayerList;
  			Game.BeforeGameStart += OnGameStart;
  			Game.ConnectionStateChanged += ConnectionStateChanged;
  
  			var name = lobby.GetOrNull<LabelWidget>("SERVER_NAME");
  			if (name != null)
  				name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName;
  
  			Ui.LoadWidget("LOBBY_MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
  			{
  				{ "orderManager", orderManager },
  				{ "lobby", this }
  			});
  
  			UpdateCurrentMap();
  
  			var playerBin = Ui.LoadWidget("LOBBY_PLAYER_BIN", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs());
  			playerBin.IsVisible = () => panel == PanelType.Players;
  
  			players = playerBin.Get<ScrollPanelWidget>("LOBBY_PLAYERS");
  			editablePlayerTemplate = players.Get("TEMPLATE_EDITABLE_PLAYER");
  			nonEditablePlayerTemplate = players.Get("TEMPLATE_NONEDITABLE_PLAYER");
  			emptySlotTemplate = players.Get("TEMPLATE_EMPTY");
  			editableSpectatorTemplate = players.Get("TEMPLATE_EDITABLE_SPECTATOR");
  			nonEditableSpectatorTemplate = players.Get("TEMPLATE_NONEDITABLE_SPECTATOR");
  			newSpectatorTemplate = players.Get("TEMPLATE_NEW_SPECTATOR");
  			colorPreview = lobby.Get<ColorPreviewManagerWidget>("COLOR_MANAGER");
  			colorPreview.Color = Game.Settings.Player.Color;
  
  			foreach (var f in modRules.Actors["world"].TraitInfos<FactionInfo>())
  				factions.Add(f.InternalName, new LobbyFaction { Selectable = f.Selectable, Name = f.Name, Side = f.Side, Description = f.Description });
  
  			var gameStarting = false;
  			Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting ||
  				panel == PanelType.Kick || panel == PanelType.ForceStart ||
  				orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
  
  			var mapButton = lobby.GetOrNull<ButtonWidget>("CHANGEMAP_BUTTON");
  			if (mapButton != null)
  			{
  				mapButton.IsDisabled = () => gameStarting || panel == PanelType.Kick || panel == PanelType.ForceStart ||
  					orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
  				mapButton.OnClick = () =>
  				{
  					var onSelect = new Action<string>(uid =>
  					{
  						// Don't select the same map again
  						if (uid == Map.Uid)
  							return;
  
  						orderManager.IssueOrder(Order.Command("map " + uid));
  						Game.Settings.Server.Map = uid;
  						Game.Settings.Save();
  					});
  
  					Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
  					{
  						{ "initialMap", Map.Uid },
  						{ "initialTab", MapClassification.System },
  						{ "onExit", DoNothing },
  						{ "onSelect", Game.IsHost ? onSelect : null },
  						{ "filter", MapVisibility.Lobby },
  					});
  				};
  			}
  
  			var slotsButton = lobby.GetOrNull<DropDownButtonWidget>("SLOTS_DROPDOWNBUTTON");
  			if (slotsButton != null)
  			{
  				slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players ||
  					Map.RuleStatus != MapRuleStatus.Cached || !orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots || !s.LockTeam);
  
  				var botNames = modRules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name);
  				slotsButton.OnMouseDown = _ =>
  				{
  					var options = new Dictionary<string, IEnumerable<DropDownOption>>();
  
  					var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
  					if (orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots))
  					{
  						var botOptions = new List<DropDownOption>()
  						{
  							new DropDownOption()
  							{
  								Title = "Add",
  								IsSelected = () => false,
  
gt: 								OnClick = () =>