import { assert } from "../../../../assert.js";
import { orient3d_robust } from "../../plane/orient3d_robust.js";
import { INVALID_NEIGHBOUR } from "../TetrahedralMesh.js";

/**
 * Walk from a given tetrahedron in the mesh towards tetrahedron that contains input point
 * This is an essential method for incrementally finding the next tetrahedron for cavity computation
 *
 * @see https://git.immc.ucl.ac.be/hextreme/hxt_seqdel/-/blob/master/src/hxt_tetrahedra.c#L330
 * @see p3 "One machine, one minute, three billion tetrahedra" by Célestin Marot, Jeanne Pellerin, Jean-François Remacle
 * @param {TetrahedralMesh} mesh
 * @param {number[]|Float32Array} points
 * @param {number} hint_tetrahedron we will start here and walk towards our target
 * @param {number} search_vertex_index resolved by indexing into {@link points} array
 * @returns {number}
 */
export function tetrahedral_mesh_walk_towards_containing_tetrahedron(
    mesh,
    points,
    hint_tetrahedron,
    search_vertex_index
) {

    assert.isNonNegativeInteger(search_vertex_index, 'search_vertex_index');

    let entering_face = 4;

    let cur_tet = hint_tetrahedron;

    let i;
    while (true) {

        for (i = 0; i < 4; i++) {

            // we walk whenever the volume is positive
            const a_i = (i + 1) & 3;
            const b_i = (i & 2) ^ 3;
            const c_i = (i + 3) & 2;

            const a_index = mesh.getVertexIndex(cur_tet, a_i);
            const b_index = mesh.getVertexIndex(cur_tet, b_i);
            const c_index = mesh.getVertexIndex(cur_tet, c_i);

            if (i !== entering_face && orient3d_robust(points, a_index, b_index, c_index, search_vertex_index) < 0.0) {

                // point is outside the tet on the neighbour's side, move in that direction
                const neighbour = mesh.getNeighbour(cur_tet, i);

                assert.notEqual(neighbour, INVALID_NEIGHBOUR, 'walked outside of the mesh');

                cur_tet = neighbour >>> 2;
                entering_face = neighbour & 3;

                break;

            }
        }

        if (i === 4) {
            // point is inside the tet
            return cur_tet;
        }
    }

}
