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

/**
 * Step one position around the disk cycle of `vertex`, starting from `edge`.
 * @param {BinaryTopology} mesh
 * @param {number} edge
 * @param {number} vertex
 * @returns {number} next edge in the disk cycle around `vertex`
 */
function disk_next(mesh, edge, vertex) {
    if (mesh.edge_read_vertex1(edge) === vertex) {
        return mesh.edge_read_v1_disk_next(edge);
    }
    return mesh.edge_read_v2_disk_next(edge);
}

/**
 * Find the unique boundary edge incident to `vertex` other than `from_edge`.
 * Throws if the vertex does not have exactly 2 boundary edges (a 2-manifold
 * boundary vertex must have exactly 2; fewer or more means the boundary is
 * not a simple cycle — e.g., a figure-8 / bridged pinch).
 *
 * @param {BinaryTopology} mesh
 * @param {number} vertex
 * @param {number} from_edge
 * @returns {number}
 */
function next_boundary_edge_at_vertex(mesh, vertex, from_edge) {
    const first_edge = mesh.vertex_read_edge(vertex);

    let found = NULL_POINTER;
    let extras = 0;

    let edge = first_edge;
    do {
        if (edge !== from_edge && bt_query_edge_is_boundary(mesh, edge)) {
            if (found === NULL_POINTER) {
                found = edge;
            } else {
                extras++;
            }
        }
        edge = disk_next(mesh, edge, vertex);
    } while (edge !== first_edge);

    if (found === NULL_POINTER) {
        throw new Error(`boundary at vertex ${vertex} is malformed: only one boundary edge attached (dead-end)`);
    }
    if (extras > 0) {
        throw new Error(`boundary at vertex ${vertex} is malformed: ${extras + 2} boundary edges attached (pinch / figure-8 loops are not supported)`);
    }

    return found;
}

/**
 * Enumerate the boundary edge cycles of a BinaryTopology mesh as ordered
 * sequences of edge IDs. Each cycle traces a single hole in the surface, in
 * the order the edges appear along the boundary.
 *
 * Throws if any boundary vertex has more than two boundary edges (figure-8 /
 * bridged loops are not supported in this pass — repair the mesh first).
 *
 * @param {BinaryTopology} mesh
 * @returns {number[][]} array of cycles; each cycle is an array of boundary edge IDs
 */
export function bt_mesh_walk_boundary_loops(mesh) {
    const cycles = [];
    const edges = mesh.edges;
    const edge_count = edges.size;

    const visited = new Uint8Array(edge_count);

    for (let seed = 0; seed < edge_count; seed++) {
        if (visited[seed]) {
            continue;
        }
        if (!edges.is_allocated(seed)) {
            continue;
        }
        if (!bt_query_edge_is_boundary(mesh, seed)) {
            continue;
        }

        const cycle = [];
        let edge = seed;
        let from_vertex = mesh.edge_read_vertex1(edge);

        do {
            visited[edge] = 1;
            cycle.push(edge);

            const v1 = mesh.edge_read_vertex1(edge);
            const v2 = mesh.edge_read_vertex2(edge);
            const next_vertex = (v1 === from_vertex) ? v2 : v1;

            const next_edge = next_boundary_edge_at_vertex(mesh, next_vertex, edge);

            edge = next_edge;
            from_vertex = next_vertex;
        } while (edge !== seed);

        cycles.push(cycle);
    }

    return cycles;
}
