/**
 * Минимальная аффинная математика анимации — чистая, без three.js
 * (урок №3: математика отдельно от вьюх, тестируется без рендера).
 * Конвенция колонок: точка справа, p' = M·p + t. Порядок Эйлера —
 * применяем X, потом Y, потом Z (R = Rz·Ry·Rx).
 */
export type Vec3 = readonly [number, number, number];
export type Mat3 = readonly [number, number, number, number, number, number, number, number, number];

/** Аффинный трансформ: поворот m плюс перенос t. */
export interface Affine {
    m: Mat3;
    t: Vec3;
}

export const IDENTITY_AFFINE: Affine = { m: [1, 0, 0, 0, 1, 0, 0, 0, 1], t: [0, 0, 0] };

/** Матрица поворота из эйлеровых углов (рад), порядок X → Y → Z. */
export function eulerMat(rot: Vec3): Mat3 {
    const [rx, ry, rz] = rot;
    const cx = Math.cos(rx), sx = Math.sin(rx);
    const cy = Math.cos(ry), sy = Math.sin(ry);
    const cz = Math.cos(rz), sz = Math.sin(rz);
    // Rz·Ry·Rx, развёрнуто (row-major)
    return [
        cz * cy, cz * sy * sx - sz * cx, cz * sy * cx + sz * sx,
        sz * cy, sz * sy * sx + cz * cx, sz * sy * cx - cz * sx,
        -sy, cy * sx, cy * cx,
    ];
}

/** Композиция: сначала local, потом parent (p → parent(local(p))). */
export function composeAffine(parent: Affine, local: Affine): Affine {
    // parent(local(p)) = parent.m·local.m·p + (parent.m·local.t + parent.t)
    return { m: mat3Mul(parent.m, local.m), t: affinePoint(parent, local.t) };
}

/** Умножение матриц a·b. */
export function mat3Mul(a: Mat3, b: Mat3): Mat3 {
    const out: number[] = [];
    for (let r = 0; r < 3; r++)
        for (let c = 0; c < 3; c++)
            out.push(a[r * 3] * b[c] + a[r * 3 + 1] * b[3 + c] + a[r * 3 + 2] * b[6 + c]);
    return out as unknown as Mat3;
}

/** Применение аффинного трансформа к точке. */
export function affinePoint(a: Affine, p: Vec3): Vec3 {
    const m = a.m;
    return [
        m[0] * p[0] + m[1] * p[1] + m[2] * p[2] + a.t[0],
        m[3] * p[0] + m[4] * p[1] + m[5] * p[2] + a.t[1],
        m[6] * p[0] + m[7] * p[1] + m[8] * p[2] + a.t[2],
    ];
}

/** Сложение векторов. */
export function addVec(a: Vec3, b: Vec3): Vec3 {
    return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
}

/** Аффинный трансформ «повернуть вокруг точки pivot и сдвинуть на pos». */
export function rotateAround(rot: Vec3, pivot: Vec3, pos: Vec3): Affine {
    const m = eulerMat(rot);
    // p → R·(p − pivot) + pivot + pos = R·p + (pivot + pos − R·pivot)
    const rp = affinePoint({ m, t: [0, 0, 0] }, pivot);
    return { m, t: addVec(addVec(pivot, pos), [-rp[0], -rp[1], -rp[2]]) };
}