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

/**
 * From a freshly-assigned bisection `vertex_partition[]` (containing only 0
 * and 1), populate the 2-way refinement bookkeeping: per-partition weights,
 * per-vertex internal/external degree, boundary list, and total cut.
 *
 * Mirrors `Compute2WayPartitionParams` from libmetis/refine.c.
 *
 * For 2-way refinement, the neighbour-partition list is trivially "the other
 * partition", so we don't allocate any cnbr pool — only the parallel
 * `internal_degree[v]` and `external_degree[v]` arrays are needed.
 *
 * @param {import('../MetisGraph.js').MetisGraph} graph
 */
export function compute_2way_params(graph) {
    const vertex_count = graph.vertex_count;
    const edge_addresses = graph.edge_addresses;
    const adjacency = graph.adjacency;
    const edge_weights = graph.edge_weights;
    const vertex_weights = graph.vertex_weights;
    const vertex_partition = graph.vertex_partition;
    const partition_weights = graph.partition_weights;
    const internal_degree = graph.internal_degree;
    const external_degree = graph.external_degree;
    const boundary_vertices = graph.boundary_vertices;
    const boundary_position_of_vertex = graph.boundary_position_of_vertex;

    assert.equal(vertex_partition !== null, true, 'graph.vertex_partition must be allocated');
    assert.equal(partition_weights.length, 2, 'partition_weights must be sized for 2 partitions');

    partition_weights[0] = 0;
    partition_weights[1] = 0;
    // internal_degree and external_degree do not need pre-clearing — every
    // vertex in [0, vertex_count) gets written in the per-vertex loop below,
    // and we never read past vertex_count.
    // boundary_position_of_vertex IS read conditionally (only boundary vertices
    // get set), so it does need a clear — but bounded to the active subgraph
    // range, since the scratch may be sized for a larger ancestor graph.
    boundary_position_of_vertex.fill(-1, 0, vertex_count);

    for (let v = 0; v < vertex_count; v++) {
        partition_weights[vertex_partition[v]] += vertex_weights[v];
    }

    let boundary_count = 0;
    let cut_doubled = 0;

    for (let vertex = 0; vertex < vertex_count; vertex++) {
        const my_partition = vertex_partition[vertex];

        let v_internal_degree = 0;
        let v_external_degree = 0;

        const edge_start = edge_addresses[vertex];
        const edge_end = edge_addresses[vertex + 1];
        for (let edge_offset = edge_start; edge_offset < edge_end; edge_offset++) {
            const neighbor_partition = vertex_partition[adjacency[edge_offset]];
            const edge_weight = edge_weights[edge_offset];
            if (neighbor_partition === my_partition) {
                v_internal_degree += edge_weight;
            } else {
                v_external_degree += edge_weight;
            }
        }

        internal_degree[vertex] = v_internal_degree;
        external_degree[vertex] = v_external_degree;

        if (v_external_degree > 0 || edge_start === edge_end) {
            // Add isolated vertices too — they're "boundary" in the degenerate sense
            // that moving them is free. Matches METIS behaviour.
            if (v_external_degree > 0) {
                cut_doubled += v_external_degree;
                boundary_vertices[boundary_count] = vertex;
                boundary_position_of_vertex[vertex] = boundary_count;
                boundary_count++;
            }
        }
    }

    graph.current_cut = cut_doubled >> 1;
    graph.boundary_count = boundary_count;
}
