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

/**
 *
 * @param {BinaryTopology} mesh
 * @param {number} edge_from
 * @param {number} edge_to
 * @param {number} vertex
 */
function edge_link_disk_forward(mesh, edge_from, edge_to, vertex) {
    const from_v1 = mesh.edge_read_vertex1(edge_from);

    let displaced_edge;

    if (from_v1 === vertex) {
        displaced_edge = mesh.edge_read_v1_disk_next(edge_from);

        mesh.edge_write_v1_disk_next(edge_from, edge_to);

    } else {
        displaced_edge = mesh.edge_read_v2_disk_next(edge_from);

        mesh.edge_write_v2_disk_next(edge_from, edge_to);
    }

    return displaced_edge;
}

function edge_link_disk_back(mesh, edge_from, edge_to, vertex) {
    const from_v1 = mesh.edge_read_vertex1(edge_from);

    let displaced_edge;

    if (from_v1 === vertex) {
        displaced_edge = mesh.edge_read_v1_disk_prev(edge_from);

        mesh.edge_write_v1_disk_prev(edge_from, edge_to);

    } else {
        displaced_edge = mesh.edge_read_v2_disk_prev(edge_from);

        mesh.edge_write_v2_disk_prev(edge_from, edge_to);
    }

    return displaced_edge;
}

/**
 *
 * @param {BinaryTopology} mesh
 * @param {number} edge_source anchor edge, new edge is inserted after this one
 * @param {number} edge_new edge that is being introduced
 * @param {number} vertex
 */
function edge_link_disk(mesh, edge_source, edge_new, vertex) {
    const forward_link = edge_link_disk_forward(mesh, edge_source, edge_new, vertex);
    edge_link_disk_forward(mesh, edge_new, forward_link, vertex);

    edge_link_disk_back(mesh, forward_link, edge_new, vertex);
    edge_link_disk_back(mesh, edge_new, edge_source, vertex);
}

/**
 *
 * @param {BinaryTopology} mesh
 * @param {number} v0
 * @param {number} v1
 * @return {number} ID of the newly created edge
 */
export function bt_edge_create(mesh, v0, v1) {
    const edge_id = mesh.edges.allocate();

    mesh.edge_write_vertex1(edge_id, v0);
    mesh.edge_write_vertex2(edge_id, v1);

    // initialize rest of the edge
    mesh.edge_write_loop(edge_id, NULL_POINTER);

    // process disk links
    const first_v0_edge = mesh.vertex_read_edge(v0);

    if (first_v0_edge !== NULL_POINTER) {
        edge_link_disk(mesh, first_v0_edge, edge_id, v0);
    } else {
        // no edges attached yet, attach this one as the first
        mesh.vertex_write_edge(v0, edge_id);
        // link to self
        mesh.edge_write_v1_disk_next(edge_id, edge_id);
        mesh.edge_write_v1_disk_prev(edge_id, edge_id);
    }

    if (v0 === v1) {
        // Self-loop: by convention the edge occupies the v1 slot of v's disk
        // cycle only; the v2 slot is unused. Running the v1 link pass below
        // would re-enter the same slot a second time, scribbling on its own
        // disk_next pointer (typically setting it to self), and corrupting v's
        // cycle. See BinaryTopology.js __edge_pool comment for the convention.
        return edge_id;
    }

    const first_v1_edge = mesh.vertex_read_edge(v1);

    if (first_v1_edge !== NULL_POINTER) {

        edge_link_disk(mesh, first_v1_edge, edge_id, v1);

    } else {
        // no edges attached yet, attach this one as the first
        mesh.vertex_write_edge(v1, edge_id);
        // link to self
        mesh.edge_write_v2_disk_next(edge_id, edge_id);
        mesh.edge_write_v2_disk_prev(edge_id, edge_id);
    }
    return edge_id;
}
