import { assert } from "../../../../../../assert.js";
import { NULL_POINTER } from "../BinaryTopology.js";

/**
 * Given a face, get a list of its attached faces.
 * Two faces are attached if they share a vertex.
 * @param {number[]|Uint32Array} output
 * @param {number} output_offset
 * @param {BinaryTopology} mesh
 * @param {number} face_id
 * @returns {number} number of faces written to output
 * @see bt_face_get_neighbour_faces
 */
export function bt_face_get_attached_faces(
    output,
    output_offset,
    mesh,
    face_id
) {

    assert.isArrayLike(output, "output");
    assert.isNonNegativeInteger(output_offset, "output_offset");
    assert.isNonNegativeInteger(face_id, "face_id");
    assert.defined(mesh, "mesh");
    assert.notNull(mesh, "mesh");
    assert.isObject(mesh, "mesh");
    assert.equal(mesh.isBinaryTopology, true, "mesh.isBinaryTopology !== true");

    let count = 0;
    const first_loop = mesh.face_read_loop(face_id);

    // Safety check for malformed faces
    if (first_loop === NULL_POINTER) {
        return 0;
    }

    let curr_loop = first_loop;

    // 1. Iterate through every loop (vertex) of the given face
    do {
        const v = mesh.loop_read_vertex(curr_loop);
        const first_edge = mesh.vertex_read_edge(v);

        if (first_edge !== NULL_POINTER) {
            let curr_edge = first_edge;

            // 2. Iterate through the disk cycle (all edges connected to this vertex)
            do {
                const first_radial_loop = mesh.edge_read_loop(curr_edge);

                if (first_radial_loop !== NULL_POINTER) {
                    let curr_radial_loop = first_radial_loop;

                    // 3. Iterate through the radial cycle (all faces sharing this edge)
                    do {
                        const adj_face = mesh.loop_read_face(curr_radial_loop);

                        // Ignore the original face and null pointers
                        if (adj_face !== face_id && adj_face !== NULL_POINTER) {

                            // Check for duplicates (linear scan is fastest for small n)
                            let is_duplicate = false;
                            for (let i = 0; i < count; i++) {
                                if (output[output_offset + i] === adj_face) {
                                    is_duplicate = true;
                                    break;
                                }
                            }

                            // Write to output if it's uniquely discovered
                            if (!is_duplicate) {
                                output[output_offset + count] = adj_face;
                                count++;
                            }
                        }

                        curr_radial_loop = mesh.loop_read_radial_next(curr_radial_loop);
                    } while (curr_radial_loop !== first_radial_loop && curr_radial_loop !== NULL_POINTER);
                }

                // Move to the next edge in the vertex's disk cycle
                if (mesh.edge_read_vertex1(curr_edge) === v) {
                    curr_edge = mesh.edge_read_v1_disk_next(curr_edge);
                } else {
                    curr_edge = mesh.edge_read_v2_disk_next(curr_edge);
                }

            } while (curr_edge !== first_edge && curr_edge !== NULL_POINTER);
        }

        // Move to the next loop in the face
        curr_loop = mesh.loop_read_next(curr_loop);
    } while (curr_loop !== first_loop && curr_loop !== NULL_POINTER);

    return count;
}