using Dinah.Core.Collections.Generic; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; #nullable enable namespace LibationFileManager { public static class QuickFilters { public static event EventHandler? Updated; public static event EventHandler? UseDefaultChanged; public class FilterState { public bool UseDefault { get; set; } public List Filters { get; set; } = new(); } public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "QuickFilters.json"); // load json into memory. if file doesn't exist, nothing to do. save() will create if needed public static FilterState? InMemoryState { get; set; } public static bool UseDefault { get => InMemoryState?.UseDefault ?? false; set { if (InMemoryState is null || UseDefault == value) return; lock (locker) { InMemoryState.UseDefault = value; save(false); } UseDefaultChanged?.Invoke(null, EventArgs.Empty); } } // Note that records overload equality automagically, so should be able to // compare these the same way as comparing simple strings. public record NamedFilter(string Filter, string? Name) { public string Filter { get; set; } = Filter; public string? Name { get; set; } = Name; } public static IEnumerable Filters => InMemoryState?.Filters.AsReadOnly() ?? Enumerable.Empty(); public static void Add(NamedFilter namedFilter) { if (namedFilter == null) throw new ArgumentNullException(nameof(namedFilter)); if (string.IsNullOrWhiteSpace(namedFilter.Filter)) return; namedFilter.Filter = namedFilter.Filter?.Trim() ?? string.Empty; namedFilter.Name = namedFilter.Name?.Trim() ?? null; lock (locker) { InMemoryState ??= new(); // check for duplicates if (InMemoryState.Filters.Select(x => x.Filter).ContainsInsensative(namedFilter.Filter)) return; InMemoryState.Filters.Add(namedFilter); save(); } } public static void Remove(NamedFilter filter) { lock (locker) { if (InMemoryState is null) return; InMemoryState.Filters.Remove(filter); save(); } } public static void Edit(NamedFilter oldFilter, NamedFilter newFilter) { lock (locker) { if (InMemoryState is null || InMemoryState.Filters.IndexOf(oldFilter) < 0) return; InMemoryState.Filters = InMemoryState.Filters.Select(f => f == oldFilter ? newFilter : f).ToList(); save(); } } public static void ReplaceAll(IEnumerable filters) { filters = filters .Where(f => !string.IsNullOrWhiteSpace(f.Filter)) .Distinct(); foreach (var filter in filters) filter.Filter = filter.Filter.Trim(); lock (locker) { InMemoryState ??= new(); InMemoryState.Filters = new List(filters); save(); } } private static object locker { get; } = new(); // ONLY call this within lock() private static void save(bool invokeUpdatedEvent = true) { // create json if not exists void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(InMemoryState, Formatting.Indented)); try { resave(); } catch (IOException) { try { resave(); } catch (IOException ex) { Serilog.Log.Logger.Error(ex, "Error saving QuickFilters.json"); throw; } } if (invokeUpdatedEvent) Updated?.Invoke(null, EventArgs.Empty); } } }