import { COLUMN_CHILD_1, COLUMN_HEIGHT, COLUMN_PARENT, ELEMENT_WORD_COUNT, NULL_NODE } from "./BVH.js";

/**
 * Sort live nodes in the traversal order.
 * This makes most queries have linear access, resulting in near-optimal cache usage
 * NOTE: assumes that there are no free nodes
 * @param {BVH} bvh
 */
export function ebvh_sort_for_traversal_depth_first(bvh) {
    const stack = [];

    stack[0] = 0;
    stack[1] = bvh.__size - 1;

    let stack_pointer = 2;
    let i, j;

    const uint32 = bvh.__data_uint32;
    const float32 = bvh.__data_float32;

    while (stack_pointer > 0) {
        stack_pointer -= 2;

        const right = stack[stack_pointer + 1];
        const left = stack[stack_pointer];

        i = left;
        j = right;

        const pivotIndex = (left + right) >> 1;

        /* partition */
        while (i <= j) {

            while (traversal_sort_compare_nodes(uint32, float32, i, pivotIndex) > 0) {
                i++;
            }

            while (traversal_sort_compare_nodes(uint32, float32, j, pivotIndex) < 0) {
                j--;
            }

            if (i <= j) {

                if (i !== j) {
                    //do swap
                    bvh.swap_nodes(i, j);
                }

                i++;
                j--;
            }
        }

        /* recursion */
        if (left < j) {
            stack[stack_pointer++] = left;
            stack[stack_pointer++] = j;
        }
        if (i < right) {
            stack[stack_pointer++] = i;
            stack[stack_pointer++] = right;
        }
    }
}

/**
 *
 * @param {Uint32Array} uint32
 * @param {number} node_index
 * @return {number}
 */
function compute_traversal_index_upper_bound(uint32, node_index) {
    let n = node_index;

    let parent = uint32[node_index * ELEMENT_WORD_COUNT + COLUMN_PARENT];

    let result = 0;
    let x = 0;

    while (parent !== NULL_NODE) {

        if (uint32[parent * ELEMENT_WORD_COUNT + COLUMN_CHILD_1] === n) {
            result += Math.pow(2, x);
        }

        x++;

        n = parent;
        parent = uint32[n * ELEMENT_WORD_COUNT + COLUMN_PARENT];
    }

    return result;
}

/**
 *
 * @param {Uint32Array} uint32
 * @param {Float32Array} float32
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */
function traversal_sort_compare_nodes(uint32, float32, a, b) {
    const a_height = uint32[a * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];
    const b_height = uint32[b * ELEMENT_WORD_COUNT + COLUMN_HEIGHT];

    const height_delta = a_height - b_height;

    if (height_delta !== 0) {
        return height_delta;
    }

    return compute_traversal_index_upper_bound(uint32, a) - compute_traversal_index_upper_bound(uint32, b);

}
