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

const normal = new Float32Array(3);

/**
 * Pokes a face by adding a vertex at the specified coordinates and fan-tessellating the face.
 * Reuses bt_edge_create to handle the complex vertex-disk linking automatically.
 *
 * Coordinates of the new vertex must be co-planar with the face.
 *
 * @param {BinaryTopology} mesh
 * @param {number} face_id
 * @param {number} x
 * @param {number} y
 * @param {number} z
 * @returns {number} vertex ID of the new center vertex, NULL_POINTER if face is degenerate or does not exist.
 */
export function bt_mesh_face_poke(
    mesh,
    face_id,
    x, y, z
) {

    assert.isNumber(x, 'x');
    assert.isNumber(y, 'y');
    assert.isNumber(z, 'z');

    // 1. Validate and Gather Initial State
    // We must collect the original loops/vertices first because we will be modifying the topology.
    const loop_ids = [];
    const first_loop_id = mesh.face_read_loop(face_id);

    if (first_loop_id === NULL_POINTER) {
        return NULL_POINTER;
    }

    let iter_loop = first_loop_id;
    do {
        loop_ids.push(iter_loop);
        iter_loop = mesh.loop_read_next(iter_loop);
    } while (iter_loop !== first_loop_id);

    const count = loop_ids.length;

    if (count < 3) {
        // Degenerate face
        return NULL_POINTER;
    }

    // 2. Create the Central Vertex
    const v_center = mesh.vertices.allocate();
    mesh.vertex_write_coordinate(v_center, [x, y, z], 0);
    mesh.vertex_write_edge(v_center, NULL_POINTER); // Initialize with no edges

    // 3. Create Spoke Edges using bt_edge_create
    // This handles all the complexity of linking edges into the vertices' disk cycles.
    const spoke_edges = new Uint32Array(count);

    for (let i = 0; i < count; i++) {
        const v_curr = mesh.loop_read_vertex(loop_ids[i]);

        // REUSE: bt_edge_create handles the allocation and the disk-cycle linking
        // for both v_curr and the new v_center.
        spoke_edges[i] = bt_edge_create(mesh, v_curr, v_center);
    }

    // 4. Tessellate (Create Faces and Loops)
    for (let i = 0; i < count; i++) {
        const idx_curr = i;
        const idx_next = (i + 1) % count;

        const l_old = loop_ids[idx_curr];
        const v_next = mesh.loop_read_vertex(loop_ids[idx_next]);

        // Determine which face ID to use (reuse original for the first triangle)
        let f_active;
        if (i === 0) {
            f_active = face_id;
            mesh.face_write_loop(f_active, l_old);
        } else {
            f_active = mesh.faces.allocate();
            // Copy normal from original face
            mesh.face_read_normal(normal, 0, face_id);
            mesh.face_write_normal(f_active, normal, 0);
            mesh.face_write_loop(f_active, l_old);
        }

        // Create the two new loops required for the triangle fan
        const l_next_spoke = mesh.loop_create();
        const l_center_spoke = mesh.loop_create();

        // --- Configure Loop 1 (The Original Loop) ---
        // Reuse l_old. It keeps its Vertex and Edge (outer edge).
        // Just update Face and Links.
        mesh.loop_write_face(l_old, f_active);
        mesh.loop_write_next(l_old, l_next_spoke);
        mesh.loop_write_prev(l_old, l_center_spoke);

        // --- Configure Loop 2 (At V_next, pointing to V_center) ---
        mesh.loop_write_vertex(l_next_spoke, v_next);
        mesh.loop_write_face(l_next_spoke, f_active);
        mesh.loop_write_next(l_next_spoke, l_center_spoke);
        mesh.loop_write_prev(l_next_spoke, l_old);

        // Link to the spoke edge (Radial Cycle)
        _link_loop_to_edge_local(mesh, l_next_spoke, spoke_edges[idx_next]);

        // --- Configure Loop 3 (At V_center, pointing to V_curr) ---
        mesh.loop_write_vertex(l_center_spoke, v_center);
        mesh.loop_write_face(l_center_spoke, f_active);
        mesh.loop_write_next(l_center_spoke, l_old);
        mesh.loop_write_prev(l_center_spoke, l_next_spoke);

        // Link to the spoke edge (Radial Cycle)
        _link_loop_to_edge_local(mesh, l_center_spoke, spoke_edges[idx_curr]);
    }

    return v_center;
}

/**
 * Local helper to link a loop to an edge's radial cycle.
 * Replicates logic from 'link_loop_to_edge' since it is not exported.
 * @param {BinaryTopology} mesh
 * @param {number} loop_id
 * @param {number} edge_id
 */
function _link_loop_to_edge_local(mesh, loop_id, edge_id) {
    mesh.loop_write_edge(loop_id, edge_id);

    const existing_loop = mesh.edge_read_loop(edge_id);

    if (existing_loop === NULL_POINTER) {
        // First loop on this edge
        mesh.edge_write_loop(edge_id, loop_id);

        // Link to self
        mesh.loop_write_radial_next(loop_id, loop_id);
        mesh.loop_write_radial_prev(loop_id, loop_id);
    } else {
        // Insert into doubly linked list
        const next = mesh.loop_read_radial_next(existing_loop);

        mesh.loop_write_radial_next(existing_loop, loop_id);
        mesh.loop_write_radial_prev(next, loop_id);

        mesh.loop_write_radial_next(loop_id, next);
        mesh.loop_write_radial_prev(loop_id, existing_loop);
    }
}