import type { Timed } from 'Player'; export default class ListWalker { /* Pointer to the "current" item */ private p = 0; constructor(private _list: Array = []) {} append(m: T): void { if (this.length > 0 && this.last && m.time < this.last.time) { console.error( 'Trying to append message with the less time then the list tail:', m.time, 'vs', this.last.time, m, this.last, this, ); return; } this.list.push(m); } unshift(m: T): void { this.list.unshift(m); } insert(m: T): void { let index = this.list.findIndex((om) => om.time > m.time); if (index === -1) { index = this.length; } const oldList = this.list; this._list = [...oldList.slice(0, index), m, ...oldList.slice(index)]; } reset(): void { this.p = 0; } sort(comparator: (a: T, b: T) => number): void { // @ts-ignore this.list.sort((m1, m2) => comparator(m1, m2) || m1._index - m2._index); // indexes for sort stability (TODO: fix types???) } forEach(f: (item: T) => void): void { this.list.forEach(f); } get last(): T | null { if (this.list.length === 0) { return null; } return this.list.slice(-1)[0]; } get current(): T | null { if (this.p === 0) { return null; } return this.list[this.p - 1]; } get timeNow(): number { if (this.p === 0) { return 0; } return this.list[this.p - 1].time; } get length(): number { return this.list.length; } get maxTime(): number { if (this.length === 0) { return 0; } return this.list[this.length - 1].time; } get minTime(): number { if (this.length === 0) { return 0; } return this.list[0].time; } get listNow(): Array { return this.list.slice(0, this.p); } get list(): Array { return this._list; } get count(): number { return this.length; } get countNow(): number { return this.p; } private hasNext() { return this.p < this.length; } private hasPrev() { return this.p > 0; } protected moveNext(): T | null { return this.hasNext() ? this.list[this.p++] : null; } protected movePrev(): T | null { return this.hasPrev() ? this.list[--this.p] : null; } /** * @returns last message with the time <= t. * Assumed that the current message is already handled so * if pointer doesn't change is returned. */ moveGetLast(t: number, index?: number, force?: boolean, debug?: boolean): T | null { const key: string = index ? '_index' : 'time'; const val = index ? index : t; let changed = false; // @ts-ignore while (this.p < this.length && this.list[this.p][key] <= val) { this.moveNext(); changed = true; } // @ts-ignore while (this.p > 0 && this.list[this.p - 1][key] > val) { this.movePrev(); changed = true; } if (debug) { console.log(this.list[this.p - 1]) } return changed || force ? this.list[this.p - 1] : null; } prevTs = 0; getNew(t: number, index?: number): T | null { const key: string = 'time'; // TODO const val = t; let changed = this.prevTs > t; this.prevTs = t; // @ts-ignore while (this.p < this.length && this.list[this.p][key] <= val) { this.moveNext(); changed = true; } // @ts-ignore while (this.p > 0 && this.list[this.p - 1][key] > val) { this.movePrev(); changed = true; } return changed ? this.list[this.p - 1] : null; } findLast(t: number): T | null { let left = 0; let right = this.list.length - 1; let result: T | null = null; while (left <= right) { const mid = Math.floor((left + right) / 2); const currentItem = this.list[mid]; if (currentItem.time <= t) { result = currentItem; left = mid + 1; } else { right = mid - 1; } } return result; } /** * Moves over the messages starting from the current+1 to the last one with the time <= t * applying callback on each of them * @param t - max message time to move to; will move & apply callback while msg.time <= t * @param callback - a callback to apply on each message passing by while moving */ moveApply(t: number, callback: (msg: T) => void): void { // Applying only in increment order for now if (t < this.timeNow) { this.reset(); } const { list } = this; while (list[this.p] && list[this.p].time <= t) { callback(this.list[this.p++]); } } }