context: #region Copyright & License Information
  /*
   * Copyright 2007-2019 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, either version 3 of
   * the License, or (at your option) any later version. For more
   * information, see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using OpenRA.Graphics;
  using OpenRA.Mods.Common.Traits;
  using OpenRA.Widgets;
  
  namespace OpenRA.Mods.Common.Widgets.Logic
  {
  	public class ActorEditLogic : ChromeLogic
  	{
  		// Error states define overlapping bits to simplify panel reflow logic
  		[Flags]
  		enum ActorIDStatus { Normal = 0, Duplicate = 1, Empty = 3 }
  
  		readonly WorldRenderer worldRenderer;
  		readonly EditorActorLayer editorActorLayer;
  		readonly EditorActionManager editorActionManager;
  		readonly EditorViewportControllerWidget editor;
  		readonly BackgroundWidget actorEditPanel;
  		readonly LabelWidget typeLabel;
  		readonly TextFieldWidget actorIDField;
  		readonly LabelWidget actorIDErrorLabel;
  
  		readonly Widget initContainer;
  		readonly Widget buttonContainer;
  
  		readonly Widget checkboxOptionTemplate;
  		readonly Widget sliderOptionTemplate;
  		readonly Widget dropdownOptionTemplate;
  
  		readonly int editPanelPadding; // Padding between right edge of actor and the edit panel.
  		readonly long scrollVisibleTimeout = 100; // Delay after scrolling map before edit widget becomes visible again.
  		long lastScrollTime = 0;
  		int2 lastScrollPosition = int2.Zero;
  
  		ActorIDStatus actorIDStatus = ActorIDStatus.Normal;
  		ActorIDStatus nextActorIDStatus = ActorIDStatus.Normal;
  		string initialActorID;
  
  		EditorActorPreview currentActorInner;
  		EditActorPreview editActorPreview;
  
  		EditorActorPreview CurrentActor
  		{
  			get
  			{
  				return currentActorInner;
  			}
  
  			set
  			{
  				if (currentActorInner == value)
  					return;
  
  				if (currentActorInner != null)
  				{
  					Reset();
  					currentActorInner.Selected = false;
  				}
  
  				currentActorInner = value;
  				if (currentActorInner != null)
  					currentActorInner.Selected = true;
  			}
  		}
  
  		[ObjectCreator.UseCtor]
  		public ActorEditLogic(Widget widget, World world, WorldRenderer worldRenderer, Dictionary<string, MiniYaml> logicArgs)
  		{
  			this.worldRenderer = worldRenderer;
  			editorActorLayer = world.WorldActor.Trait<EditorActorLayer>();
  			editorActionManager = world.WorldActor.Trait<EditorActionManager>();
  
  			editor = widget.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
  			actorEditPanel = editor.Get<BackgroundWidget>("ACTOR_EDIT_PANEL");
  
  			typeLabel = actorEditPanel.Get<LabelWidget>("ACTOR_TYPE_LABEL");
  			actorIDField = actorEditPanel.Get<TextFieldWidget>("ACTOR_ID");
  
  			initContainer = actorEditPanel.Get("ACTOR_INIT_CONTAINER");
  			buttonContainer = actorEditPanel.Get("BUTTON_CONTAINER");
  
  			checkboxOptionTemplate = initContainer.Get("CHECKBOX_OPTION_TEMPLATE");
  			sliderOptionTemplate = initContainer.Get("SLIDER_OPTION_TEMPLATE");
  			dropdownOptionTemplate = initContainer.Get("DROPDOWN_OPTION_TEMPLATE");
  			initContainer.RemoveChildren();
  
  			var deleteButton = actorEditPanel.Get<ButtonWidget>("DELETE_BUTTON");
  			var cancelButton = actorEditPanel.Get<ButtonWidget>("CANCEL_BUTTON");
  			var okButton = actorEditPanel.Get<ButtonWidget>("OK_BUTTON");
  
  			actorIDErrorLabel = actorEditPanel.Get<LabelWidget>("ACTOR_ID_ERROR_LABEL");
  			actorIDErrorLabel.IsVisible = () => actorIDStatus != ActorIDStatus.Normal;
  			actorIDErrorLabel.GetText = () => actorIDStatus == ActorIDStatus.Duplicate ?
  				"Duplicate Actor ID" : "Enter an Actor ID";
  
  			MiniYaml yaml;
  			if (logicArgs.TryGetValue("EditPanelPadding", out yaml))
  				editPanelPadding = FieldLoader.GetValue<int>("EditPanelPadding", yaml.Value);
  
  			okButton.IsDisabled = () => !IsValid() || !editActorPreview.IsDirty;
  			okButton.OnClick = Save;
  			cancelButton.OnClick = Cancel;
  			deleteButton.OnClick = Delete;
  			actorEditPanel.IsVisible = () => CurrentActor != null
  				&& editor.CurrentBrush == editor.DefaultBrush
  				&& Game.RunTime > lastScrollTime + scrollVisibleTimeout;
  
  			actorIDField.OnEscKey = () =>
  			{
  				actorIDField.YieldKeyboardFocus();
  				return true;
  			};
  
  			actorIDField.OnTextEdited = () =>
  			{
  				var actorId = actorIDField.Text.Trim();
  				if (string.IsNullOrWhiteSpace(actorId))
  				{
  					nextActorIDStatus = ActorIDStatus.Empty;
  					return;
  				}
  
  				// Check for duplicate actor ID
  				if (CurrentActor.ID.Equals(actorId, StringComparison.OrdinalIgnoreCase))
  				{
  					if (editorActorLayer[actorId] != null)
  					{
  						nextActorIDStatus = ActorIDStatus.Duplicate;
  						return;
  					}
  				}
  
  				SetActorID(actorId);
  				nextActorIDStatus = ActorIDStatus.Normal;
  			};
  
  			actorIDField.OnLoseFocus = () =>
  			{
  				// Reset invalid IDs back to their starting value
  				if (actorIDStatus != ActorIDStatus.Normal)
  					SetActorID(initialActorID);
  			};
  		}
  
  		void SetActorID(string actorId)
  		{
  			editActorPreview.SetActorID(actorId);
  
  			nextActorIDStatus = ActorIDStatus.Normal;
  		}
  
  		bool IsValid()
  		{
  			return nextActorIDStatus == ActorIDStatus.Normal;
  		}
  
  		public override void Tick()
  		{
  			if (actorIDStatus != nextActorIDStatus)
  			{
  				if ((actorIDStatus & nextActorIDStatus) == 0)
  				{
  					var offset = actorIDErrorLabel.Bounds.Height;
  					if (nextActorIDStatus == ActorIDStatus.Normal)
  						offset *= -1;
  
  					actorEditPanel.Bounds.Height += offset;
  					initContainer.Bounds.Y += offset;
  					buttonContainer.Bounds.Y += offset;
  				}
  
  				actorIDStatus = nextActorIDStatus;
  			}
  
  			var actor = editor.DefaultBrush.SelectedActor;
  			if (actor != null)
  			{
  				var origin = worldRenderer.Viewport.WorldToViewPx(new int2(actor.Bounds.Right, actor.Bounds.Top));
  
  				// If we scrolled, hide the edit box for a moment
  				if (lastScrollPosition.X != origin.X || lastScrollPosition.Y != origin.Y)
  				{
  					lastScrollTime = Game.RunTime;
  					lastScrollPosition = origin;
  				}
  
  				// If we changed actor, move widgets
  				if (CurrentActor != actor)
  				{
  					lastScrollTime = 0; // Ensure visible
  					CurrentActor = actor;
  
  					editActorPreview = new EditActorPreview(CurrentActor);
  
  
gt: 					initialActorID = actorIDField.Text = actor.ID;