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

/**
 * Sort consequent 4 nodes to reduce SAH among each consequent pair.
 * Useful for optimizing topology for traversal.
 * @param {BVH} bvh
 * @param {number[]|Uint32Array} nodes
 * @param {number} offset
 * @param {number} distance
 * @param {number} count
 */
export function ebvh_nodes_sort_sah_local4(
    bvh,
    nodes,
    offset,
    distance,
    count
) {
    assert.isNonNegativeInteger(offset, 'offset');
    assert.isNonNegativeInteger(count, 'count');

    const swap_pair_limit = Math.floor((count - (distance + 2)) / 2);
    const end = offset + swap_pair_limit * 2;

    let swap_count = 0;

    for (let index_a = offset; index_a < end; index_a += 2) {
        const index_c = index_a + distance;

        const index_b = index_a + 1;
        const index_d = index_c + 1;

        const node_a = nodes[index_a];
        const node_b = nodes[index_b];

        const node_c = nodes[index_c];
        const node_d = nodes[index_d];

        /*
         possible combinations:
         AB CD
         AC BD
         AD CB

         Other combinations are not of interest as order among each pair is irrelevant for traversal performance
         */

        const area_ab = bvh.node_get_combined_surface_area(node_a, node_b);
        const area_cd = bvh.node_get_combined_surface_area(node_c, node_d);

        const area_ac = bvh.node_get_combined_surface_area(node_a, node_c);
        const area_bd = bvh.node_get_combined_surface_area(node_b, node_d);

        const area_ad = bvh.node_get_combined_surface_area(node_a, node_d);
        const area_cb = bvh.node_get_combined_surface_area(node_c, node_b);

        const cost_ab = area_ab;
        const cost_cd = area_cd;

        const cost_ac = area_ac;
        const cost_bd = area_bd;

        const cost_ad = area_ad;
        const cost_cb = area_cb;

        const cost0 = cost_ab + cost_cd;
        const cost1 = cost_ac + cost_bd;
        const cost2 = cost_ad + cost_cb;

        if (cost0 <= cost1 && cost0 <= cost2) {
            // no change

        } else if (cost1 <= cost2) {
            // swap C and B
            nodes[index_b] = node_c;
            nodes[index_c] = node_b;

            swap_count++;

        } else {
            // swap B and D
            nodes[index_b] = node_d;
            nodes[index_d] = node_b;


            swap_count++;

        }


    }

    return swap_count;
}