import { assert } from "../../../../assert.js";
import { INVALID_NEIGHBOUR } from "../TetrahedralMesh.js";
import { in_sphere3d_robust } from "../../sphere/in_sphere3d_robust.js";

/**
 *
 * @param {TetrahedralMesh} mesh
 * @param {Cavity} cavity
 * @param {number[]|Float32Array} points
 * @param {BitSet} visited_flags
 * @param {number} containing_tetra tetrahedron that contains point
 * @param {number} point_index point that forms the cavity
 */
export function tetrahedral_mesh_compute_cavity(
    mesh,
    points,
    visited_flags,
    cavity,
    containing_tetra,
    point_index
) {
    assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');

    // add the tet to cavity
    cavity.push_deleted(containing_tetra);
    visited_flags.set(containing_tetra, true);

    for (let start = cavity.__deleted_size - 1; start < cavity.__deleted_size; start++) {
        const cur_tet = cavity.__deleted[start];

        for (let i = 0; i < 4; i++) {
            const encoded_neighbour = mesh.getNeighbour(cur_tet, i);

            const k0 = (1 << i) & 3;
            const k1 = (i + 2) % 3;
            const k2 = (~((i + 1) >> 1) & 2) + 1;

            if (encoded_neighbour === INVALID_NEIGHBOUR) {
                // no neighbour, include this side into boundary

                cavity.push_boundary(
                    point_index,
                    mesh.getVertexIndex(cur_tet, k0),
                    mesh.getVertexIndex(cur_tet, k1),
                    mesh.getVertexIndex(cur_tet, k2),
                    encoded_neighbour,
                );

                continue;
            }

            const neighbour_index = encoded_neighbour >> 2;

            if (visited_flags.get(neighbour_index)) {
                // already visited
                continue;
            }

            if (in_sphere3d_robust(
                points,
                mesh.getVertexIndex(neighbour_index, 0),
                mesh.getVertexIndex(neighbour_index, 1),
                mesh.getVertexIndex(neighbour_index, 2),
                mesh.getVertexIndex(neighbour_index, 3),
                point_index
            ) < 0) {
                // outside the cavity, meaning it defines boundary

                cavity.push_boundary(
                    point_index,
                    mesh.getVertexIndex(cur_tet, k0),
                    mesh.getVertexIndex(cur_tet, k1),
                    mesh.getVertexIndex(cur_tet, k2),
                    encoded_neighbour,
                );

            } else {
                // assert.greaterThanOrEqual(visited_flags.length, neighbour_index * 4 + 3, 'subdet array is too small');

                //part of the cavity, remove
                cavity.push_deleted(neighbour_index);
                visited_flags.set(neighbour_index, true);
            }
        }

    }

    // debug_validate_cavity_boundary(mesh, cavity);
}
