Blob Blame Raw
using System;

namespace Assistance {
	public class KeyState<T> where T: IComparable, new() {
		public struct Holder {
			public KeyState<T> state;
			public long ticks;
			public double timeOffset;
			
			public Holder(KeyState<T> state, long ticks = 0, double timeOffset = 0.0) {
				this.state = state;
				this.ticks = ticks;
				this.timeOffset = timeOffset;
			}
			
			public KeyState<T> find(T value)
				{ return state == null ? null : state.find(value); }
			public bool isEmpty
				{ get { return state == null || state.isEmpty; } }
			public bool isPressed(T value)
				{ return find(value) != null; }
			public double howLongPressed(T value)
				{ return howLongPressed(find(value), ticks, timeOffset); }

			public static double howLongPressed(KeyState<T> state, long ticks, double timeOffset) {
				return state == null ? 0.0
				     : Math.Max(Timer.step, (ticks - state.ticks)*Timer.step + timeOffset);
			}
		}
		
		public static readonly T none = new T();
		public static readonly KeyState<T> empty = new KeyState<T>();
		
		public readonly KeyState<T> previous;
		public readonly long ticks;
		public readonly T value;

		public KeyState(): this(null, 0, none) { }

		private KeyState(KeyState<T> previous, long ticks, T value) {
			this.previous = previous;
			this.ticks = ticks;
			this.value = value;
		}
		
		public KeyState<T> find(T value) {
			if (value.CompareTo(none) == 0)
				return null;
			if (value.CompareTo(this.value) == 0)
				return this;
			if (previous == null)
				return null;
			return previous.find(value);
		}
		
		private KeyState<T> makeChainWithout(KeyState<T> ks) {
			if (this == ks || previous == null) return previous;
			return new KeyState<T>(previous.makeChainWithout(ks), ticks, value);
		}
		
		public KeyState<T> change(bool press, T value, long ticks) {
			if (value.CompareTo(none) == 0)
				return this;
			if (ticks <= this.ticks)
				ticks = this.ticks + 1;

			KeyState<T> p = find(value);
			if (press) {
				if (p != null) return this;
				return new KeyState<T>(isEmpty ? null : this, ticks, value);
			}

			if (p == null) return this;
			KeyState<T> chain = makeChainWithout(p);
			return chain == null ? new KeyState<T>() : chain;
		}
		
		public bool isEmpty
			{ get { return value.CompareTo(none) == 0 && (previous == null || previous.isEmpty); } }
		public bool isPressed(T value)
			{ return find(value) != null; }

		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); }
	}
}