using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Xml.Linq; using Microsoft.Extensions.Logging; namespace ImageCatalog; /// /// Modern parameter storage service using XML for persistence. /// Thread-safe key-value store for application settings. /// public class ParametriSetup { private readonly object _lock = new(); private readonly ILogger _logger; private Dictionary _parameters = new(StringComparer.OrdinalIgnoreCase); /// /// Gets or sets the XML file path for settings storage. /// public string? NomeFileSetup { get; set; } /// /// Initializes a new instance with the specified settings file. /// /// Path to the XML settings file. /// Optional logger for diagnostics. public ParametriSetup(string? fileSetup = null, ILogger? logger = null) { _logger = logger ?? Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance; NomeFileSetup = fileSetup; if (!string.IsNullOrWhiteSpace(fileSetup)) { CaricaParametriSetup(); } } /// /// Loads settings from the XML file. /// public void CaricaParametriSetup() { lock (_lock) { if (string.IsNullOrEmpty(NomeFileSetup) || !File.Exists(NomeFileSetup)) { _parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); return; } try { var doc = XDocument.Load(NomeFileSetup); var setupElements = doc.Descendants("Setup"); _parameters = setupElements .Where(e => e.Element("Nome") != null) .ToDictionary( e => e.Element("Nome")!.Value, e => e.Element("Valore")?.Value ?? string.Empty, StringComparer.OrdinalIgnoreCase ); _logger.LogInformation("Loaded {Count} parameters from {FilePath}", _parameters.Count, NomeFileSetup); } catch (Exception ex) { _logger.LogError(ex, "Failed to load settings from {FilePath}", NomeFileSetup); _parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); } } } /// /// Saves settings to the XML file. /// /// Thrown when NomeFileSetup is not set. public void SalvaParametriSetup() { if (string.IsNullOrWhiteSpace(NomeFileSetup)) throw new InvalidOperationException("NomeFileSetup is not set."); lock (_lock) { try { // Ensure directory exists var directory = Path.GetDirectoryName(NomeFileSetup); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Create XML document with DataSet-compatible structure var doc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XElement("NewDataSet", _parameters.Select(kvp => new XElement("Setup", new XElement("Nome", kvp.Key), new XElement("Valore", kvp.Value) ) ) ) ); doc.Save(NomeFileSetup); _logger.LogInformation("Saved {Count} parameters to {FilePath}", _parameters.Count, NomeFileSetup); } catch (Exception ex) { _logger.LogError(ex, "Failed to save settings to {FilePath}", NomeFileSetup); throw; } } } /// /// Reads a parameter as a string. /// /// Parameter name. /// Parameter value or empty string if not found. public string LeggiParametroString(string nomeParametro) { lock (_lock) { return _parameters.TryGetValue(nomeParametro, out var value) ? value : string.Empty; } } /// /// Reads a parameter as a boolean. /// /// Parameter name. /// True if value is truthy (TRUE, OK, SI, 1, YES, VERO), false otherwise. public bool LeggiParametroBoolean(string nomeParametro) { var raw = LeggiParametroString(nomeParametro); return raw.ToUpperInvariant() switch { "TRUE" or "OK" or "SI" or "1" or "YES" or "VERO" => true, _ => false }; } /// /// Updates or creates a parameter. /// /// Parameter name. /// Parameter value. public void AggiornaParametro(string nomeParametro, object? valoreParametro) { lock (_lock) { _parameters[nomeParametro] = valoreParametro?.ToString() ?? string.Empty; } } /// /// Reads a parameter and converts it to the specified type. /// /// Target type. /// Parameter name. /// Default value if conversion fails. /// Converted value or default value. public T LeggiParametro(string nomeParametro, T defaultValue = default!) { var raw = LeggiParametroString(nomeParametro); if (string.IsNullOrEmpty(raw)) return defaultValue; try { // Handle nullable types var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); // Special handling for common types if (targetType == typeof(bool)) { return (T)(object)LeggiParametroBoolean(nomeParametro); } return (T)Convert.ChangeType(raw, targetType, CultureInfo.InvariantCulture); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to convert parameter {ParameterName} to {TypeName}, using default value", nomeParametro, typeof(T).Name); return defaultValue; } } /// /// Checks if a parameter exists. /// /// Parameter name. /// True if parameter exists, false otherwise. public bool ParametroExists(string nomeParametro) { lock (_lock) { return _parameters.ContainsKey(nomeParametro); } } /// /// Removes a parameter. /// /// Parameter name. /// True if parameter was removed, false if it didn't exist. public bool RimuoviParametro(string nomeParametro) { lock (_lock) { return _parameters.Remove(nomeParametro); } } /// /// Gets all parameter names. /// /// Collection of parameter names. public IReadOnlyCollection GetParameterNames() { lock (_lock) { return _parameters.Keys.ToList(); } } /// /// Clears all parameters. /// public void ClearAll() { lock (_lock) { _parameters.Clear(); } } }