/**
 * Процедурная локомоция (docs/plan.md): walk-цикл генерируется параметрами,
 * а не авторится кадрами. Два шага за цикл: левая и правая нога в противофазе
 * (sin θ и sin(θ+π)), руки — в противофазе одноимённой ноге, корень приседает
 * дважды за цикл (в фазах полного шага) и качается вбок один раз.
 * Требования к костям: «legL»/«legR» (pitch вокруг X), «armL»/«armR», «root».
 * Отсутствующие кости просто не получают каналов — семплинг это допускает.
 */
import type { AnimClip, ClipKey } from './clip';

export interface WalkParams {
    /** Длительность цикла (сек), > 0. */
    duration: number;
    /** Амплитуда ног (рад, ± вокруг нуля). */
    legSwing: number;
    /** Амплитуда рук (рад, ± вокруг нуля). */
    armSwing: number;
    /** Приседание корня в фазах полного шага (воксельных единиц, ≥ 0). */
    bob: number;
    /** Боковое качание корня (воксельных единиц; 0 — выключено). */
    sway?: number;
}

const KEY_STEPS = 8; // ключей на цикл + 1 (замыкающий) — линейный семплинг гладкости не даёт, но дёшев

/** Walk-цикл из параметров; детерминирован, проходит validateClip (цикл замкнут). */
export function walkClip(params: WalkParams, name = 'walk'): AnimClip {
    const keys: ClipKey[] = [];
    for (let i = 0; i <= KEY_STEPS; i++) {
        const t = (params.duration * i) / KEY_STEPS;
        const th = (2 * Math.PI * i) / KEY_STEPS; // фаза цикла
        const legL = params.legSwing * Math.sin(th);
        const armL = -params.armSwing * Math.sin(th); // рука против одноимённой ноги
        const bobY = -params.bob * Math.abs(Math.sin(th)); // приседание дважды за цикл
        const swayX = (params.sway ?? 0) * Math.sin(th);
        keys.push({
            t,
            bones: {
                root: { pos: [swayX, bobY, 0] },
                legL: { rot: [legL, 0, 0] },
                legR: { rot: [-legL, 0, 0] },
                armL: { rot: [armL, 0, 0] },
                armR: { rot: [-armL, 0, 0] },
            },
        });
    }
    return { name, duration: params.duration, loop: true, keys };
}