using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenNest.Engine.Strategies
{
///
/// The built-in fill strategies in pipeline order, with one process-wide disabled set that the
/// desktop Options form sets. Every fill that runs the strategy pipeline reads it.
///
public static class FillStrategyRegistry
{
// Pipeline order, ascending by each strategy's Order value.
private static readonly IReadOnlyList builtIn = Array.AsReadOnly(
new IFillStrategy[]
{
new PairsFillStrategy(),
new RowFillStrategy(),
new ColumnFillStrategy(),
new CircleFillStrategy(),
new RectBestFitStrategy(),
new ExtentsFillStrategy(),
new LinearFillStrategy(),
new TiltedRemnantStrategy(),
}
);
private static IReadOnlyList strategies = builtIn;
private static List sorted;
private static HashSet enabledFilter;
private static readonly HashSet disabled = new(StringComparer.OrdinalIgnoreCase);
public static IReadOnlyList Strategies => sorted ??= FilterStrategies();
///
/// Returns all strategies regardless of enabled/disabled state.
///
public static IReadOnlyList AllStrategies => strategies;
///
/// Returns the names of all permanently disabled strategies.
///
public static IReadOnlyCollection DisabledNames => disabled;
private static List FilterStrategies()
{
var source =
enabledFilter != null
? strategies.Where(s => enabledFilter.Contains(s.Name))
: strategies.Where(s => !disabled.Contains(s.Name));
return source.ToList();
}
///
/// Permanently disables strategies by name. They remain listed
/// but are excluded from the default pipeline.
///
public static void Disable(params string[] names)
{
foreach (var name in names)
disabled.Add(name);
sorted = null;
}
///
/// Re-enables a previously disabled strategy.
///
public static void Enable(params string[] names)
{
foreach (var name in names)
disabled.Remove(name);
sorted = null;
}
///
/// Restricts the active strategies to only those whose names are listed.
/// Pass null to restore all strategies.
///
public static void SetEnabled(params string[] names)
{
enabledFilter =
names != null && names.Length > 0
? new HashSet(names, StringComparer.OrdinalIgnoreCase)
: null;
sorted = null;
}
///
/// Replaces the built-in list, in the given order, until the result is disposed. For tests
/// that drive fills through a host that builds its own filler, such as the plate view.
///
internal static IDisposable UseForTesting(IReadOnlyList replacement)
{
var prior = strategies;
strategies = replacement;
sorted = null;
return new Restore(prior);
}
private sealed class Restore : IDisposable
{
private IReadOnlyList prior;
public Restore(IReadOnlyList prior) => this.prior = prior;
public void Dispose()
{
if (prior == null)
return;
strategies = prior;
sorted = null;
prior = null;
}
}
}
}