using System;
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace Junction.App.Converters
{
///
/// Maps a machine's ProtocolId ("mtconnect", future "opcua"/"fanuc", …) to a vector
/// glyph () for a PathIcon. Keeps view-models framework-agnostic:
/// the icon decision lives entirely in the view layer, mirroring
/// . Unknown/empty ids fall back to a generic glyph.
/// Glyphs are line-only 24×24 path data (no arcs/curves) so they always parse and read
/// as distinct filled shapes. No external image assets — net48 packaging stays a single exe set.
///
public sealed class ProtocolIdToIconConverter : IValueConverter
{
public static readonly ProtocolIdToIconConverter Instance = new ProtocolIdToIconConverter();
// Signal-strength bars — a live telemetry feed.
private const string MtconnectData = "M4,14 H7 V20 H4 Z M10,10 H13 V20 H10 Z M16,5 H19 V20 H16 Z";
// Stacked diamonds — the OPC UA layered address space.
private const string OpcuaData = "M12,2 L20,7 L12,12 L4,7 Z M12,13 L20,18 L12,23 L4,18 Z";
// Plus/cross — an industrial CNC controller marker.
private const string FanucData = "M10,3 H14 V10 H21 V14 H14 V21 H10 V14 H3 V10 H10 Z";
// Octagon node — generic / unknown protocol.
private const string FallbackData = "M8,4 H16 L20,8 V16 L16,20 H8 L4,16 V8 Z";
private static Geometry? _mtconnect;
private static Geometry? _opcua;
private static Geometry? _fanuc;
private static Geometry? _fallback;
///
/// Pure lookup of the raw path-data string for a protocol id. View-framework-free and
/// side-effect-free so it is unit-testable without an Avalonia platform. Known ids return a
/// distinct non-empty string; anything else returns the generic fallback. Never throws.
///
public static string PathDataFor(string? protocolId)
{
switch (protocolId == null ? null : protocolId.Trim().ToLowerInvariant())
{
case "mtconnect":
return MtconnectData;
case "opcua":
return OpcuaData;
case "fanuc":
return FanucData;
default:
return FallbackData;
}
}
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
switch (PathDataFor(value as string))
{
case MtconnectData:
return _mtconnect ?? (_mtconnect = Geometry.Parse(MtconnectData));
case OpcuaData:
return _opcua ?? (_opcua = Geometry.Parse(OpcuaData));
case FanucData:
return _fanuc ?? (_fanuc = Geometry.Parse(FanucData));
default:
return _fallback ?? (_fallback = Geometry.Parse(FallbackData));
}
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
}