/**
 * OBB (Oriented Bounding Box) — AABB пересечение через SAT.
 *
 * Используется для столкновения игрока (AABB) с повёрнутыми примитивами (OBB).
 *
 * OBB задаётся:
 *   - центр (cx, cy, cz)
 *   - полуразмеры по локальным осям (hx, hy, hz)
 *   - 3 ортонормированных локальных оси (axes), задающих ориентацию
 *
 * AABB задаётся:
 *   - центр (cx, cy, cz)
 *   - полуразмеры (hw, hh, hd) — по мировым X/Y/Z
 *
 * Возвращает true если есть пересечение.
 *
 * Алгоритм SAT (Separating Axis Theorem): два выпуклых тела не пересекаются
 * тогда и только тогда, когда есть ось, на проекции которой их интервалы
 * не пересекаются. Для AABB×OBB достаточно проверить 15 осей:
 *   - 3 оси AABB (X/Y/Z)
 *   - 3 оси OBB (axes[0..2])
 *   - 9 cross-products пар осей
 */

const EPS = 1e-6;

/**
 * Построить axes для OBB с произвольным rotation (Euler XYZ).
 * Возвращает 3 единичных вектора локальных осей в мировых координатах.
 */
export function buildOBBAxes(rotX, rotY, rotZ) {
    const cx = Math.cos(rotX), sx = Math.sin(rotX);
    const cy = Math.cos(rotY), sy = Math.sin(rotY);
    const cz = Math.cos(rotZ), sz = Math.sin(rotZ);
    // Babylon использует Euler XYZ: M = Rx * Ry * Rz (Right-handed Y-up).
    // Локальная ось X в мире:
    const axisX = [
        cy * cz,
        cy * sz,
        -sy,
    ];
    const axisY = [
        sx * sy * cz - cx * sz,
        sx * sy * sz + cx * cz,
        sx * cy,
    ];
    const axisZ = [
        cx * sy * cz + sx * sz,
        cx * sy * sz - sx * cz,
        cx * cy,
    ];
    return [axisX, axisY, axisZ];
}

/**
 * AABB (cx,cy,cz, hw,hh,hd) пересекается с OBB (ocx,ocy,ocz, hx,hy,hz, axes).
 */
export function aabbIntersectsOBB(
    cx, cy, cz, hw, hh, hd,
    ocx, ocy, ocz, hx, hy, hz, axes
) {
    // Вектор от центра OBB к центру AABB
    const dx = cx - ocx, dy = cy - ocy, dz = cz - ocz;

    // 3 оси AABB — стандартные X/Y/Z
    const aabbAxes = [
        [1, 0, 0],
        [0, 1, 0],
        [0, 0, 1],
    ];

    // Проекция AABB на ось — это всегда hw*|ax| + hh*|ay| + hd*|az|
    const projectAABB = (a) =>
        hw * Math.abs(a[0]) + hh * Math.abs(a[1]) + hd * Math.abs(a[2]);

    // Проекция OBB на ось — h0*|axis0·a| + h1*|axis1·a| + h2*|axis2·a|
    const obbHalves = [hx, hy, hz];
    const projectOBB = (a) =>
        obbHalves[0] * Math.abs(dot(axes[0], a)) +
        obbHalves[1] * Math.abs(dot(axes[1], a)) +
        obbHalves[2] * Math.abs(dot(axes[2], a));

    // Расстояние между центрами по оси
    const distOnAxis = (a) => Math.abs(dx * a[0] + dy * a[1] + dz * a[2]);

    // Проверяем 3 + 3 + 9 = 15 осей
    const allAxes = [];
    allAxes.push(aabbAxes[0], aabbAxes[1], aabbAxes[2]);
    allAxes.push(axes[0], axes[1], axes[2]);
    for (let i = 0; i < 3; i++) {
        for (let j = 0; j < 3; j++) {
            const c = cross(aabbAxes[i], axes[j]);
            if (lenSq(c) > EPS) allAxes.push(c);
        }
    }

    for (const ax of allAxes) {
        const ra = projectAABB(ax);
        const rb = projectOBB(ax);
        const d = distOnAxis(ax);
        if (d > ra + rb + EPS) return false;     // нашли разделяющую ось
    }
    return true;
}

function dot(a, b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
function cross(a, b) {
    return [
        a[1]*b[2] - a[2]*b[1],
        a[2]*b[0] - a[0]*b[2],
        a[0]*b[1] - a[1]*b[0],
    ];
}
function lenSq(a) { return a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; }
