import { deref, MaybeDeref } from "@thi.ng/api";
import type { Reducer, Transducer } from "../api";
import { Reducer, Transducer } from "../api";
import { compR } from "../func/compr";
import { $iter } from "../iterator";
/**
* Sliding window transducer, similar to `partition(size, 1)`, but
* supports initially partially filled windows, if `partial` is set to
* true (default). Each emitted window is a shallow copy of the internal
* accumulation buffer.
*
* @remarks
* If `size` implements {@link IDeref}, the window size will be
* re-evaluated for each new input and therefore can be used as
* mechanism to dynamically adjust the window size.
*
* @example
* ```ts
* [...window(3, range(5))]
* // [ [ 0 ], [ 0, 1 ], [ 0, 1, 2 ], [ 1, 2, 3 ], [ 2, 3, 4 ] ]
*
* [...window(3, false, range(5))]
* // [ [ 0, 1, 2 ], [ 1, 2, 3 ], [ 2, 3, 4 ] ]
* ```
*
* @param size -
* @param partial -
* @param src -
*/
export function slidingWindow<T>(
size: MaybeDeref<number>,
size: number,
partial?: boolean
): Transducer<T, T[]>;
export function slidingWindow<T>(
size: MaybeDeref<number>,
size: number,
src: Iterable<T>
): IterableIterator<T[]>;
export function slidingWindow<T>(
size: MaybeDeref<number>,
size: number,
partial: boolean,
src: Iterable<T>
): IterableIterator<T[]>;
export function slidingWindow<T>(...args: any[]): any {
const iter = $iter(slidingWindow, args);
if (iter) return iter;
const size: MaybeDeref<number> = args[0];
if (iter) {
return iter;
}
const size: number = args[0];
const partial: boolean = args[1] !== false;
return (rfn: Reducer<any, T[]>) => {
const reduce = rfn[2];
let buf: T[] = [];
return compR(rfn, (acc, x: T) => {
buf.push(x);
const _size = deref(size);
if (partial || buf.length >= _size) {
if (partial || buf.length === size) {
acc = reduce(acc, buf);
buf = buf.slice(buf.length >= _size ? 1 : 0, _size);
buf = buf.slice(buf.length === size ? 1 : 0);
}
return acc;
});
};
}