//


import { max2 } from "../../../math/max2.js";
import { min2 } from "../../../math/min2.js";
import { v3_length } from "../../vec3/v3_length.js";

/**
 *
 * @param {number[]|ArrayLike<number>|Float32Array} result hit 6-tuple is written here as follows: [hit_position_x, hit_position_y, hit_position_z, hit_normal_x, hit_normal_y, hit_normal_z]
 * @param {number} result_offset offset into result array where to write the hit data
 * @param {number} x0
 * @param {number} y0
 * @param {number} z0
 * @param {number} x1
 * @param {number} y1
 * @param {number} z1
 * @param {number} origin_x ray origin
 * @param {number} origin_y ray origin
 * @param {number} origin_z ray origin
 * @param {number} direction_x ray direction
 * @param {number} direction_y ray direction
 * @param {number} direction_z ray direction
 * @returns {boolean} true if ray hit the box, false otherwise
 *
 *
 * @see https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/
 * @see https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_aabb.html
 * @see https://blog.johnnovak.net/2016/10/22/the-nim-ray-tracer-project-part-4-calculating-box-normals/
 */
export function aabb3_raycast(
    result, result_offset,
    x0, y0, z0, x1, y1, z1,
    origin_x, origin_y, origin_z,
    direction_x, direction_y, direction_z
) {

    // first find intersection
    const dir_inv_x = 1 / direction_x;
    const dir_inv_y = 1 / direction_y;
    const dir_inv_z = 1 / direction_z;

    const t1_x = (x0 - origin_x) * dir_inv_x;
    const t2_x = (x1 - origin_x) * dir_inv_x;

    const t1_y = (y0 - origin_y) * dir_inv_y;
    const t2_y = (y1 - origin_y) * dir_inv_y;

    const t1_z = (z0 - origin_z) * dir_inv_z;
    const t2_z = (z1 - origin_z) * dir_inv_z;

    let tMax = max2(t1_x, t2_x);
    tMax = min2(tMax, max2(t1_y, t2_y));
    tMax = min2(tMax, max2(t1_z, t2_z));

    if (tMax < 0) {
        // if tMax < 0, AABB is behind the ray origin in the opposite direction to the ray's
        return false;
    }

    let tMin = min2(t1_x, t2_x);
    tMin = max2(tMin, min2(t1_y, t2_y));
    tMin = max2(tMin, min2(t1_z, t2_z));

    if (tMin > tMax) {
        // if tMin > tMax, ray does not intersect the AABB
        return false;
    }

    // ray hit detected, figure out offset along the ray from origin to hit
    const ray_offset = tMin < 0 ? tMax : tMin;

    const hit_x = origin_x + direction_x * ray_offset;
    const hit_y = origin_y + direction_y * ray_offset;
    const hit_z = origin_z + direction_z * ray_offset;

    // figure out normal
    const center_x = (x0 + x1) * 0.5;
    const center_y = (y0 + y1) * 0.5;
    const center_z = (z0 + z1) * 0.5;

    const p_x = hit_x - center_x;
    const p_y = hit_y - center_y;
    const p_z = hit_z - center_z;

    const d_x = (x0 - x1) * 0.5;
    const d_y = (y0 - y1) * 0.5;
    const d_z = (z0 - z1) * 0.5;

    // bias is necessary to deal with rounding errors
    const bias = 1.000001;

    const normal_x = (p_x / Math.abs(d_x) * bias) | 0;
    const normal_y = (p_y / Math.abs(d_y) * bias) | 0;
    const normal_z = (p_z / Math.abs(d_z) * bias) | 0;

    // normalize surface normal vector
    const normal_inv_length = 1 / v3_length(normal_x, normal_y, normal_z);

    result[result_offset] = hit_x;
    result[result_offset + 1] = hit_y;
    result[result_offset + 2] = hit_z;

    result[result_offset + 3] = normal_x * normal_inv_length;
    result[result_offset + 4] = normal_y * normal_inv_length;
    result[result_offset + 5] = normal_z * normal_inv_length;

    return true;
}
