using System;
namespace OpenNest.Desktop.Interaction;
///
/// Keeps one plate tool active, as the WinForms view's action manager does: Select by default;
/// Escape first cancels what the active tool is doing, then ends the tool and returns to Select,
/// and an Escape from an idle Select brings back the tool Escape ended (when that tool allows it).
/// is the active tool's name and hint for the status bar.
///
public sealed class PlateToolManager
{
private readonly IPlateToolHost host;
private PlateTool previous;
public PlateToolManager(IPlateToolHost host)
{
this.host = host ?? throw new ArgumentNullException(nameof(host));
Current = new SelectTool(host);
Current.HintChanged += OnHintChanged;
Current.Activate();
Status = StatusOf(Current);
}
/// The active tool.
public PlateTool Current { get; private set; }
/// The active tool's name, and its hint when it has one.
public string Status { get; private set; }
/// Raised when the active tool or its status changes.
public event EventHandler StatusChanged;
///
/// Makes active, ending the current one. Ending another tool for
/// Select keeps it for Escape to resume; any other switch forgets the resumable tool.
///
public void SetTool(PlateTool tool)
{
ArgumentNullException.ThrowIfNull(tool);
previous = tool is SelectTool && Current is not SelectTool && Current.ResumeOnEscape ? Current : null;
Switch(tool);
}
/// Returns to Select (as a fresh tool, so nothing selected is kept).
public void Select() => SetTool(new SelectTool(host));
///
/// Ends the active tool at the user's explicit request (a panel's Close button, say). Unlike
/// Escape, the ended tool is not kept for Escape to resume.
///
public void EndTool()
{
Select();
previous = null;
}
/// Handles Escape; see the class summary.
public void Escape()
{
if (Current.IsBusy)
Current.Cancel();
else if (Current is SelectTool && previous != null)
{
var resumed = previous;
previous = null;
Switch(resumed);
}
else
Select();
host.Invalidate();
}
/// Called when the host shows another plate: tools that do not survive it end.
public void OnPlateChanged()
{
if (!Current.SurvivesPlateChange)
Select();
else
Current.OnPlateChanged();
}
private void Switch(PlateTool tool)
{
var old = Current;
old.Cancel();
old.Deactivate();
old.HintChanged -= OnHintChanged;
Current = tool;
tool.HintChanged += OnHintChanged;
tool.Activate();
RefreshStatus();
host.Invalidate();
}
private void OnHintChanged(object sender, EventArgs e)
{
if (ReferenceEquals(sender, Current))
RefreshStatus();
}
private void RefreshStatus()
{
Status = StatusOf(Current);
StatusChanged?.Invoke(this, EventArgs.Empty);
}
private static string StatusOf(PlateTool tool) =>
string.IsNullOrEmpty(tool.Hint) ? tool.Name : $"{tool.Name}: {tool.Hint}";
}