/**
 * Чистая математика покадровых клипов (без Pixi): время в «шагах»,
 * режимы loop/once/pingpong, события смены шага и завершения.
 * Тикается от фиксированного шага движка; dt больше периода осознанно
 * «проматывает» промежуточные шаги (сообщается только финальный) —
 * по аналогии с пересечением границы delay в TweenManager.
 */

/** Режим клипа: loop — цикл; once — один раз и стоп на последнем кадре;
 *  pingpong — цикл туда-обратно (крайние кадры без дубля). */
export type ClipLoop = 'loop' | 'once' | 'pingpong';

export interface ClipDef {
    /** Кадров в секунду. */
    fps: number;
    loop?: ClipLoop;
    /** Множитель скорости клипа (перемножается со speedScale аниматора). */
    speedScale?: number;
}

export interface ClipTick {
    /** Индекс шага после продвижения (для once не превышает len-1). */
    step: number;
    /** Индекс шага до продвижения. */
    prevStep: number;
    /** Клип дошёл до конца (once) — ровно один раз при пересечении конца. */
    finished: boolean;
}

/**
 * Продвинуть время клипа (сек) на dt и посчитать новый шаг.
 * Время накапливается в шагах: time += dt * fps * speedScale.
 */
export function stepClip(
    len: number,
    def: ClipDef,
    prevTime: number,
    dt: number
): { time: number; tick: ClipTick } {
    const scale = def.speedScale ?? 1;
    const time = prevTime + dt * def.fps * scale;
    const prevStep = stepAt(len, def.loop ?? 'loop', prevTime);
    const step = stepAt(len, def.loop ?? 'loop', time);
    // finished — только для once и только в тик, когда конец был пересечён.
    const finished = def.loop !== 'loop' && time >= len - 1 && prevTime < len - 1;
    return { time, tick: { step, prevStep, finished } };
}

/** Индекс шага в момент time (в шагах) для заданного режима. */
function stepAt(len: number, loop: ClipLoop, time: number): number {
    if (loop === 'once') return Math.min(Math.max(0, Math.floor(time)), len - 1);
    if (loop === 'pingpong') {
        // Период 2*(len-1): туда 0..len-1, обратно len-1..0; крайние кадры без дубля.
        const period = Math.max(1, 2 * (len - 1));
        const m = ((Math.floor(time) % period) + period) % period;
        return m < len ? m : period - m;
    }
    const m = ((Math.floor(time) % len) + len) % len;
    return m;
}