Blob Blame Raw
using System;
using System.Collections.Generic;

namespace Assistance {
	public class KeyHistory<T> where T: IComparable, new() {
		public class Holder: IDisposable {
			public readonly KeyHistory<T> history;
			public readonly long ticks;
			public readonly double timeOffset;

			private readonly long heldTicks;
			private bool disposed = false;

			public Holder(KeyHistory<T> 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<T>.Holder get(double time) {
				long dticks = (long)Math.Ceiling(Timer.frequency*(time + timeOffset));
				KeyState<T> state = history.get(ticks + dticks);
				return new KeyState<T>.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<KeyState<T>> states = new List<KeyState<T>>() { new KeyState<T>() };
		private readonly List<long> locks = new List<long>();
		
		public KeyState<T> current
			{ get { return states[ states.Count - 1 ]; } }
		
		public KeyState<T> change(bool press, T value, long ticks)	{
			states.Add(current.change(press, value, ticks));
			autoRemove();
			return current;
		}
		public KeyState<T> press(T value, long ticks)
			{ return change(true, value, ticks); }
		public KeyState<T> 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<T> 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<T>();
			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];
		}
	}
}