namespace OpenNest.Data;
///
/// Decision logic behind the desktop Clamps dialog: edits one nest's clamp rail in that
/// nest's units. Three explicit boundaries: only fills
/// the editor, changes the nest (the dialog's OK), and
/// is what "Save as Machine Defaults" writes immediately.
///
public sealed class ClampRailEditor
{
private readonly List _clamps = new();
/// Starts from the nest's rail, or the default clamp size with no clamps.
public ClampRailEditor(ClampRail? rail, Units units, bool applyToNewNests)
{
Units = units;
ApplyToNewNests = applyToNewNests;
var initial = ClampSettings.FromRail(rail, units, applyToNewNests);
Width = initial.Width;
Depth = initial.Depth;
RemovalMinutes = initial.RemovalMinutes;
_clamps.AddRange(initial.Clamps.Order());
}
/// The nest's units; every value is in these units.
public Units Units { get; }
/// Clamp extent along its edge.
public double Width { get; set; }
/// Clamp intrusion into the sheet from its edge.
public double Depth { get; set; }
/// Minutes to remove one clamp and put it back after cutting (D9); not a
/// length, so it is never converted between units.
public double RemovalMinutes { get; set; }
/// Whether saved machine defaults give new nests these clamps.
public bool ApplyToNewNests { get; set; }
/// Clamps ordered by edge, then center from the plate origin; distinct.
public IReadOnlyList Clamps => _clamps;
/// Origin-side clamp centers along X from the plate origin, ascending.
public IReadOnlyList Centers =>
_clamps.Where(c => c.Edge == ClampEdge.OriginSide).Select(c => c.Center).ToList();
/// Adds an origin-side clamp; see .
public bool TryAddCenter(double center, out string? error) =>
TryAddClamp(new RailClamp(ClampEdge.OriginSide, center), out error);
///
/// Adds a clamp, keeping the list sorted. Refuses (with an operator-facing reason)
/// centers that are not finite, negative, or already listed on that edge.
///
public bool TryAddClamp(RailClamp clamp, out string? error)
{
if (!double.IsFinite(clamp.Center) || clamp.Center < 0)
{
error = "Enter a clamp center measured from the plate origin (zero or more).";
return false;
}
var index = _clamps.BinarySearch(clamp);
if (index >= 0)
{
error = $"A clamp is already centered at {clamp.Center:0.####} on that edge.";
return false;
}
_clamps.Insert(~index, clamp);
error = null;
return true;
}
/// Removes the clamp at in .
public void RemoveCenterAt(int index)
{
if (index >= 0 && index < _clamps.Count)
_clamps.RemoveAt(index);
}
///
/// Replaces the editor's values with the machine defaults converted to this nest's
/// units. Nothing is applied or saved.
///
public void LoadMachineDefaults(ClampSettings settings)
{
ArgumentNullException.ThrowIfNull(settings);
var converted = settings.ConvertedTo(Units);
Width = converted.Width;
Depth = converted.Depth;
RemovalMinutes = converted.RemovalMinutes;
ApplyToNewNests = settings.Enabled;
_clamps.Clear();
_clamps.AddRange(converted.Clamps.Distinct().Order());
}
///
/// The edited rail, or null when no clamp is listed. Throws
/// for a non-positive width or depth, or a
/// negative/non-finite removal time.
///
public ClampRail? BuildRail() =>
_clamps.Count == 0 ? null : new ClampRail(Width, Depth, _clamps, RemovalMinutes);
/// The dialog's OK: makes the edited rail the nest's, re-resolving every plate.
public void ApplyTo(Nest nest)
{
ArgumentNullException.ThrowIfNull(nest);
nest.SetClampRail(BuildRail());
}
///
/// The machine settings "Save as Machine Defaults" writes: these values in this nest's
/// units, enabled for new nests only when requested and at least one clamp is listed.
///
public ClampSettings ToMachineDefaults()
{
// Validate before anything is persisted.
if (!double.IsFinite(Width) || Width <= 0 || !double.IsFinite(Depth) || Depth <= 0)
throw new ArgumentOutOfRangeException(
nameof(Width),
"Clamp width and depth must be positive."
);
if (!double.IsFinite(RemovalMinutes) || RemovalMinutes < 0)
throw new ArgumentOutOfRangeException(
nameof(RemovalMinutes),
"Clamp removal time must be finite and zero or more."
);
var settings = new ClampSettings
{
Enabled = ApplyToNewNests && _clamps.Count > 0,
Units = Units,
Width = Width,
Depth = Depth,
RemovalMinutes = RemovalMinutes,
};
settings.SetClamps(_clamps);
return settings;
}
}