import { max2 } from "../../../math/max2.js";
import { min2 } from "../../../math/min2.js";
import { v3_dot } from "../../vec3/v3_dot.js";
import { plane3_compute_convex_3_plane_intersection } from "../plane/plane3_compute_convex_3_plane_intersection.js";

/**
 * Planes used to bound the simplex
 * NOTE: these might be computed for a given set of points instead, to produce a tighter fit, but this was good enough for my needs so far
 *
 * @readonly
 * @type {Float32Array}
 */
const SIMPLEX_PROJECTION_AXES = new Float32Array([
    Math.sqrt(8 / 9), 0, -1 / 3.,
    -Math.sqrt(2 / 9), Math.sqrt(2 / 3), -1 / 3,
    -Math.sqrt(2 / 9), -Math.sqrt(2 / 3), -1 / 3,
    0, 0, 1
]);

/**
 * Compute bounding tetrahedron (3d simplex) for a given set of input points
 * The algorithm is pretty basic, it projects each point onto 4 planes and pushes planes away from origin to the furthest projected point
 * then, we take intersection points between those planes to get our bounding tetrahedron
 *
 * @see https://github.com/bryanmcnett/aabo/blob/master/aabo.cpp
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 *
 * @param {number[]|Float32Array|Float64Array} result
 * @param {number} result_offset
 * @param {number[]|Float32Array|Float64Array} input
 * @param {number} input_count
 * @param {number} padding Planar offset for each of the simple sides, positive padding value will create a larger simplex. Useful for ensuring that input points do not lie directly on the simplex planes
 */
export function compute_bounding_simplex_3d(
    result,
    result_offset,
    input,
    input_count,
    padding
) {
    if (input_count <= 0) {
        // no inputs
        return;
    }

    // initialize
    let x = input[0];
    let y = input[1];
    let z = input[2];

    let a = v3_dot(
        x,
        y,
        z,
        SIMPLEX_PROJECTION_AXES[0],
        SIMPLEX_PROJECTION_AXES[1],
        SIMPLEX_PROJECTION_AXES[2],
    );
    let b = v3_dot(
        x,
        y,
        z,
        SIMPLEX_PROJECTION_AXES[3],
        SIMPLEX_PROJECTION_AXES[4],
        SIMPLEX_PROJECTION_AXES[5],
    );
    let c = v3_dot(
        x,
        y,
        z,
        SIMPLEX_PROJECTION_AXES[6],
        SIMPLEX_PROJECTION_AXES[7],
        SIMPLEX_PROJECTION_AXES[8],
    );
    let d = v3_dot(
        x,
        y,
        z,
        SIMPLEX_PROJECTION_AXES[9],
        SIMPLEX_PROJECTION_AXES[10],
        SIMPLEX_PROJECTION_AXES[11],
    );

    let min_a = a;
    let min_b = b;
    let min_c = c;
    let min_d = d;

    let max_a = a;
    let max_b = b;
    let max_c = c;
    let max_d = d;

    for (let i = 1; i < input_count; i++) {

        const i3 = i * 3;

        x = input[i3];
        y = input[i3 + 1];
        z = input[i3 + 2];

        a = v3_dot(
            x,
            y,
            z,
            SIMPLEX_PROJECTION_AXES[0],
            SIMPLEX_PROJECTION_AXES[1],
            SIMPLEX_PROJECTION_AXES[2],
        );
        b = v3_dot(
            x,
            y,
            z,
            SIMPLEX_PROJECTION_AXES[3],
            SIMPLEX_PROJECTION_AXES[4],
            SIMPLEX_PROJECTION_AXES[5],
        );
        c = v3_dot(
            x,
            y,
            z,
            SIMPLEX_PROJECTION_AXES[6],
            SIMPLEX_PROJECTION_AXES[7],
            SIMPLEX_PROJECTION_AXES[8],
        );
        d = v3_dot(
            x,
            y,
            z,
            SIMPLEX_PROJECTION_AXES[9],
            SIMPLEX_PROJECTION_AXES[10],
            SIMPLEX_PROJECTION_AXES[11],
        );

        min_a = min2(min_a, a);
        min_b = min2(min_b, b);
        min_c = min2(min_c, c);
        min_d = min2(min_d, d);

        max_a = max2(max_a, a);
        max_b = max2(max_b, b);
        max_c = max2(max_c, c);
        max_d = max2(max_d, d);
    }

    // we now have the planar offset for each projection axis

    // apply padding offset
    min_a -= padding;
    min_b -= padding;
    min_c -= padding;
    min_d -= padding;

    // let's compute intersection points for each tri-plane combination to find simplex corners
    plane3_compute_convex_3_plane_intersection(
        result, result_offset,
        SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
        SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
        SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
    );
    plane3_compute_convex_3_plane_intersection(
        result, result_offset + 3,
        SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
        SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
        SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
    );
    plane3_compute_convex_3_plane_intersection(
        result, result_offset + 6,
        SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
        SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
        SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
    );
    plane3_compute_convex_3_plane_intersection(
        result, result_offset + 9,
        SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
        SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
        SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
    );
}
