using System; using System.Collections.Generic; namespace Assistance { public class KeyHistory where T: IComparable, new() { public class Holder: IDisposable { public readonly KeyHistory history; public readonly long ticks; public readonly double timeOffset; private readonly long heldTicks; private bool disposed = false; public Holder(KeyHistory history, long ticks, double timeOffset = 0.0) { this.history = history; this.ticks = ticks; this.timeOffset = timeOffset; heldTicks = history.hold(ticks); } public Holder offset(double timeOffset) { return Geometry.isEqual(timeOffset, 0.0) ? this : new Holder(history, ticks, this.timeOffset + timeOffset); } public KeyState.Holder get(double time) { long dticks = (long)Math.Ceiling(Timer.frequency*(time + timeOffset)); KeyState state = history.get(ticks + dticks); return new KeyState.Holder(state, ticks, timeOffset + time); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposed) return; history.release(heldTicks); disposed = true; } ~Holder() { Dispose(false); } } private readonly List> states = new List>() { new KeyState() }; private readonly List locks = new List(); public KeyState current { get { return states[ states.Count - 1 ]; } } public KeyState change(bool press, T value, long ticks) { states.Add(current.change(press, value, ticks)); autoRemove(); return current; } public KeyState press(T value, long ticks) { return change(true, value, ticks); } public KeyState release(T value, long ticks) { return change(false, value, ticks); } private int findLock(long ticks) { // locks[a] <= ticks < locks[b] if (locks.Count == 0) return -1; int a = 0; int b = locks.Count - 1; if (locks[a] < ticks) return -1; if (ticks >= locks[b]) return b; while(true) { int c = (a + b)/2; if (a == c) break; if (ticks < locks[c]) b = c; else a = c; } return a; } private void autoRemove() { long ticks = locks.Count > 0 ? locks[0] : long.MaxValue; while(states.Count > 1 && (states[0].ticks < ticks || states[0].isEmpty)) states.RemoveAt(0); } private long hold(long ticks) { long heldTicks = Math.Max(ticks, states[0].ticks); locks.Insert(findLock(heldTicks) + 1, heldTicks); return heldTicks; } private void release(long heldTicks) { int i = findLock(heldTicks); if (i >= 0 && locks[i] == heldTicks) locks.RemoveAt(i); autoRemove(); } private KeyState get(long ticks) { // state[a].ticks <= ticks < state[b].ticks int a = 0; int b = states.Count - 1; if (states[a].ticks < ticks) return new KeyState(); if (ticks >= states[b].ticks) return states[b]; while(true) { int c = (a + b)/2; if (a == c) break; if (ticks < states[c].ticks) b = c; else a = c; } return states[a]; } } }