using System.IO; using System.Text.Json; using RgbLedStripController.Core; using MediaColor = System.Windows.Media.Color; namespace RgbLedStripController.Services; public sealed class SettingsStore { private readonly string _settingsPath; private readonly SemaphoreSlim _writeLock = new(1, 1); private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public SettingsStore() { var settingsDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RgbLedStripController"); _settingsPath = Path.Combine(settingsDirectory, "settings.json"); } public async Task LoadAsync(CancellationToken cancellationToken = default) { if (!File.Exists(_settingsPath)) { return CreateDefaultSnapshot(); } try { await using var stream = File.OpenRead(_settingsPath); var stored = await JsonSerializer.DeserializeAsync(stream, _jsonOptions, cancellationToken); return new SettingsSnapshot( stored?.KnownDevices ?? [], new Dictionary(stored?.DeviceAliases ?? new Dictionary(), StringComparer.OrdinalIgnoreCase), ParseColor(stored?.SelectedColorHex), stored?.Brightness ?? 100, stored?.EffectSpeed ?? 50, stored?.CurrentMode ?? LightingMode.Rgb, stored?.GrayscaleLevel ?? 100, stored?.TemperatureLevel ?? 50, stored?.SelectedEffectId ?? 0, stored?.IsPoweredOn ?? false); } catch { return CreateDefaultSnapshot(); } } public async Task SaveAsync(SettingsSnapshot snapshot, CancellationToken cancellationToken = default) { await _writeLock.WaitAsync(cancellationToken); try { var directory = Path.GetDirectoryName(_settingsPath); if (!string.IsNullOrWhiteSpace(directory)) { Directory.CreateDirectory(directory); } await using var stream = File.Create(_settingsPath); var stored = new StoredSettings { KnownDevices = snapshot.KnownDevices.ToList(), DeviceAliases = new Dictionary(snapshot.DeviceAliases, StringComparer.OrdinalIgnoreCase), SelectedColorHex = $"#{snapshot.SelectedColor.R:X2}{snapshot.SelectedColor.G:X2}{snapshot.SelectedColor.B:X2}", Brightness = snapshot.Brightness, EffectSpeed = snapshot.EffectSpeed, CurrentMode = snapshot.CurrentMode, GrayscaleLevel = snapshot.GrayscaleLevel, TemperatureLevel = snapshot.TemperatureLevel, SelectedEffectId = snapshot.SelectedEffectId, IsPoweredOn = snapshot.IsPoweredOn }; await JsonSerializer.SerializeAsync(stream, stored, _jsonOptions, cancellationToken); } finally { _writeLock.Release(); } } private static SettingsSnapshot CreateDefaultSnapshot() => new( [], new Dictionary(StringComparer.OrdinalIgnoreCase), MediaColor.FromRgb(255, 106, 78), 100, 50, LightingMode.Rgb, 100, 50, 0, false); private static MediaColor ParseColor(string? value) { if (string.IsNullOrWhiteSpace(value) || value.Length != 7 || value[0] != '#') { return MediaColor.FromRgb(255, 106, 78); } return MediaColor.FromRgb( Convert.ToByte(value.Substring(1, 2), 16), Convert.ToByte(value.Substring(3, 2), 16), Convert.ToByte(value.Substring(5, 2), 16)); } private sealed class StoredSettings { public List KnownDevices { get; set; } = []; public Dictionary DeviceAliases { get; set; } = new(StringComparer.OrdinalIgnoreCase); public string? SelectedColorHex { get; set; } public byte Brightness { get; set; } public byte EffectSpeed { get; set; } public LightingMode CurrentMode { get; set; } public byte GrayscaleLevel { get; set; } public byte TemperatureLevel { get; set; } public byte SelectedEffectId { get; set; } public bool IsPoweredOn { get; set; } } }