using System.Collections.Generic; namespace Salmon.Core.Depth; public class StateMachine { public Persistence? History { get; set; } public EventManager? Events { get; set; } class ImplValue { public object? Value { get; set; } public DateTime When { get; set; } = DateTime.Now; public ImplValue(object? value, DateTime? when = null) { Value = value; When = when ?? DateTime.Now; } } object Mutex = new object(); Dictionary> Implementation = new (); public bool Populated { get; private set; } = false; public async Task PopulateWithHistory() { Populated = true; long count = 0; if (History is not null) await foreach (var i in History.GetState()) { Push(i); count++; } return count; } public void Push(Triplet triplet) { Push(triplet.key, triplet.predicate, triplet.value, triplet.LastFlush); } /// /// Update string value in dictionary /// Must be thread safe /// /// May not be null /// May be null public void Push(string key, string predicate, object? value, DateTime when) { if(key == null) throw new ArgumentNullException(nameof(key)); if (Get(key, predicate) == value) return; var implVal = new ImplValue(value, when); lock (Mutex) { if(!Implementation.ContainsKey(key)) { Dictionary dic = new(); dic.Add(predicate, implVal); Implementation.Add(key, dic); } var keyDic = Implementation[key]; if(!keyDic.TryAdd(predicate, implVal)) { var old = keyDic[predicate]; if (old.When > when) return; keyDic[predicate] = implVal; } } if(History is not null) History.PushStateChange(key, predicate, value, when); if (Events is not null) Events.Push(Event.FromStateChange(key, value, when)); } public IEnumerable GetIds() { lock (Mutex) { if (!Populated) PopulateWithHistory().Wait(); foreach (var kv in Implementation) yield return kv.Key; } } public IEnumerable Get() { lock (Mutex) { if (!Populated) PopulateWithHistory().Wait(); foreach (var kv in Implementation) foreach (var kvv in kv.Value) yield return new Triplet { key = kv.Key, predicate = kvv.Key, value = kvv.Value.Value, LastFlush = kvv.Value.When }; } } public IEnumerable Get(string key) { Dictionary dic; lock (Mutex) { if (!Populated) PopulateWithHistory().Wait(); if (!Implementation.TryGetValue(key, out dic)) yield break; } foreach(var kv in dic) yield return new Triplet { key = key, predicate = kv.Key, value = kv.Value.Value, LastFlush = kv.Value.When }; } public DateTime? GetLastUpdate(string key) { Dictionary dic; lock (Mutex) { if (!Populated) PopulateWithHistory().Wait(); if (!Implementation.TryGetValue(key, out dic)) return null; } DateTime mostrecent = DateTime.MinValue; foreach (var kv in dic) if(kv.Value.When > mostrecent) mostrecent = kv.Value.When; if (mostrecent == DateTime.MinValue) return null; return mostrecent; } public object? Get(string key, string predicate) { lock (Mutex) { if (!Populated) PopulateWithHistory().Wait(); if (Implementation.TryGetValue(key, out var value)) if (value.TryGetValue(predicate, out var ret)) return ret; } return null; } public long CountTriplets() { lock(Mutex) { if (!Populated) PopulateWithHistory().Wait(); long c = 0; foreach (var kv in Implementation) foreach (var i in kv.Key) c++; return c; } } }