using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using OpenNest.Data; using OpenNest.Engine.Jobs; using OpenNest.Engine.Jobs.Placement; namespace OpenNest.Forms { /// /// App-scoped nesting-engine selection. The selected name addresses a whole-job /// INestingEngine resolved through NestingEngineRegistry at call time; single-plate /// interactive fill uses FillStrategy, which maps a built-in engine to its placement /// strategy and falls back to Fill for jobs-only engines and plug-ins. /// public static class EngineSelection { public const string DefaultEngineName = EngineSelectionSettings.DefaultEngineName; private static string engineName = DefaultEngineName; /// Current selection. User changes are persisted for the next launch. public static string EngineName { get { return engineName; } set { engineName = string.IsNullOrWhiteSpace(value) ? DefaultEngineName : value.Trim(); try { new EngineSelectionSettings { EngineName = engineName } .Save(EngineSelectionSettings.DefaultPath); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) { // A read-only profile must not prevent selecting an engine for this session. Debug.WriteLine($"[EngineSelection] Could not save engine selection: {ex.Message}"); } } } /// /// Call after NestingEngineRegistry.LoadPlugins and before populating the desktop combo. /// Returns a status-bar warning when the saved engine is unavailable, otherwise empty. /// Loading never saves the fallback over a temporarily missing plug-in preference. /// public static string LoadSavedSelection() { var settings = EngineSelectionSettings.Load(EngineSelectionSettings.DefaultPath); engineName = settings.Resolve(UiEngineNames, out var statusMessage, NestingEngineRegistry.ResolveName); return statusMessage ?? string.Empty; } /// Desktop combo contents: every registered jobs engine. public static IEnumerable UiEngineNames => NestingEngineRegistry.AvailableEngines.Select(e => e.Name).ToList(); /// Placement strategy for single-plate fill: the selection when it names a /// built-in strategy, otherwise Fill. public static string FillStrategy => IsFillStrategy(engineName) ? engineName : PlateFillService.DefaultStrategy; public static bool IsFillStrategy(string name) { var resolved = NestingEngineRegistry.ResolveName(name); return resolved != null && PlateFillService.BuiltInStrategies.Any( s => s.Equals(resolved, StringComparison.OrdinalIgnoreCase) ); } /// The plain description shown beside an engine's name; empty for plug-ins. public static string Description(string name) => NestingEngineRegistry.AvailableEngines .FirstOrDefault(e => e.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) ?.Description ?? string.Empty; /// The description plus what Fill Area uses, for a tooltip on the selection. public static string ToolTip(string name) { var description = Description(name); return IsFillStrategy(name) ? description : $"{description}\nFill Area uses {PlateFillService.DefaultStrategy} with this engine.".TrimStart(); } } }