package dsp;

/**
	A view into an Array with an indexing offset.

	Usages include 1-indexed sequences or zero-centered buffers with negative indexing.
**/
@:forward(array, offset)
abstract OffsetArray<T>({
	final array : Array<T>;
	final offset : Int;
}) {
	public inline function new(array:Array<T>, offset:Int)
		this = { array: array, offset: offset };

	public var length(get,never) : Int;
	inline function get_length()
		return this.array.length;

	@:arrayAccess
	public inline function get(index:Int) : T
		return this.array[index - this.offset];

	@:arrayAccess
	public inline function set(index:Int, value:T) : Void
		this.array[index - this.offset] = value;

	/**
		Iterates through items in their original order while providing the altered indexes as keys.
	**/
	public inline function keyValueIterator() : KeyValueIterator<Int,T>
		return new OffsetArrayIterator(this.array, this.offset);

	@:from
	static inline function fromArray<T>(array:Array<T>)
		return new OffsetArray(array, 0);

	@:to
	inline function toArray()
		return this.array;

	/**
		Makes a shifted version of the given `array`, where elements are in the
		same order but shifted by `n` positions (to the right if positive and to
		the left if negative) in **circular** fashion (no elements discarded).
	**/
	public static function circShift<T>(array:Array<T>, n:Int) : Array<T> {
		if (n < 0) return circShift(array, array.length + n);

		var shifted = new Array<T>();

		n = n % array.length;
		for (i in array.length - n ... array.length) shifted.push(array[i]);
		for (i in 0 ... array.length - n) shifted.push(array[i]);

		return shifted;
	}
}

private class OffsetArrayIterator<T> {
	private final array : Array<T>;
	private final offset : Int;
	private var enumeration : Int;

	public inline function new(array:Array<T>, offset:Int) {
		this.array = array;
		this.offset = offset;
		this.enumeration = 0;
	}

	public inline function next() : {key:Int, value:T} {
		final i = this.enumeration++;
		return { key: i + this.offset, value: this.array[i] };
	}

	public inline function hasNext() : Bool
		return this.enumeration < this.array.length;
}