namespace Nonemm.Session; /// The order stacked calls come off, which is what N1MM's digital window calls /// its call stacking modes. public enum StackOrder { /// Multipliers first, then the order they arrived in. MultipliersFirst, /// The order they arrived in. FirstIn, /// Newest first. LastIn, /// Nothing is stacked. Disabled, } /// The calls waiting to be worked while running. A station calling CQ hears /// more than one answer, and this holds the ones it has not got to yet, so the /// operator takes them one at a time instead of asking each to call again. /// N1MM's single-operator call stacking. /// /// The order calls come off is `Order`: multipliers first is what N1MM uses on /// CW and phone, and the digital window offers the other three. public sealed class CallStack { /// N1MM stacks nothing shorter than this. public const int ShortestCall = 2; private readonly List calls = []; /// How calls come off. The digital window sets this from its call stacking /// menu; everything else leaves it alone. public StackOrder Order { get; set; } = StackOrder.MultipliersFirst; /// The calls in the order they come off, so the front is next. public IReadOnlyList Calls => calls; public int Count => calls.Count; public bool IsEmpty => calls.Count == 0; /// The call that comes off next, or empty when the stack is. public string Top => calls.Count > 0 ? calls[0] : ""; /// Puts a call on the stack. A call already there is moved rather than /// repeated, and where it goes is up to `Order`. public void Add(string call, bool isMultiplier) { string wanted = call.Trim().ToUpperInvariant(); if (wanted.Length < ShortestCall || Order == StackOrder.Disabled) { return; } Remove(wanted); bool toFront = Order switch { StackOrder.LastIn => true, StackOrder.MultipliersFirst => isMultiplier, _ => false, }; if (toFront) { calls.Insert(0, wanted); } else { calls.Add(wanted); } } /// Takes the next call off, or returns empty when there is nothing on the /// stack. public string Next() { if (calls.Count == 0) { return ""; } string call = calls[0]; calls.RemoveAt(0); return call; } public bool Remove(string call) => calls.RemoveAll(c => string.Equals(c, call.Trim(), StringComparison.OrdinalIgnoreCase)) > 0; /// Puts a call already on the stack at the front, which is what /// double-clicking it in N1MM's window does. public void MoveToTop(string call) { if (Remove(call)) { calls.Insert(0, call.Trim().ToUpperInvariant()); } } public void Clear() => calls.Clear(); }