import { assert } from "../../../../assert.js";
import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../../collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
import { aabb3_intersects_ray } from "../../../../geom/3d/aabb/aabb3_intersects_ray.js";
import { COLUMN_CHILD_1, COLUMN_CHILD_2, ELEMENT_WORD_COUNT, NULL_NODE } from "../../BVH.js";

const stack = SCRATCH_UINT32_TRAVERSAL_STACK;


/**
 * Traverses bvh starting from a given node, but can back-track all the way up to the root
 * @param {BVH} bvh
 * @param {number} starting_node
 * @param {number[]} result
 * @param {number} result_offset
 * @param {number} origin_x
 * @param {number} origin_y
 * @param {number} origin_z
 * @param {number} direction_x
 * @param {number} direction_y
 * @param {number} direction_z
 * @returns {number}
 */
export function bvh_backtracking_query_leaves_ray(
    bvh, starting_node,
    result, result_offset,
    origin_x, origin_y, origin_z,
    direction_x, direction_y, direction_z
) {

    assert.isNonNegativeInteger(starting_node, 'starting_node');

    /*
     For performance, we bind data directly to avoid extra copies required to read out AABB
     */
    const float32 = bvh.__data_float32;
    const uint32 = bvh.__data_uint32;

    let result_cursor = result_offset;

    let came_from = NULL_NODE;

    // starting point
    let entry = starting_node;

    do {

        const address = entry * ELEMENT_WORD_COUNT;

        const intersects = aabb3_intersects_ray(
            float32[address], float32[address + 1], float32[address + 2],
            float32[address + 3], float32[address + 4], float32[address + 5],
            origin_x, origin_y, origin_z,
            direction_x, direction_y, direction_z
        );

        /**
         *
         * @type {number}
         */
        const stack_top = stack.pointer;

        if (intersects) {
            const child1 = bvh.node_get_child1(entry);

            if (child1 === NULL_NODE) {
                // leaf node
                result[result_cursor++] = entry;
            } else {

                const child2 = bvh.node_get_child2(entry);

                if (came_from !== child2) {
                    stack[stack.pointer++] = child2;
                }

                if (came_from !== child1) {
                    stack[stack.pointer++] = child1;
                }

            }
        }


        while (stack.pointer > stack_top) {
            stack.pointer--;

            /**
             *
             * @type {number}
             */
            const node = stack[stack.pointer];

            const address = node * ELEMENT_WORD_COUNT;

            // test node against the ray
            const intersects = aabb3_intersects_ray(
                float32[address], float32[address + 1], float32[address + 2],
                float32[address + 3], float32[address + 4], float32[address + 5],
                origin_x, origin_y, origin_z,
                direction_x, direction_y, direction_z
            );

            if (!intersects) {
                continue;
            }

            // get fist child to check if this is a leaf node or not
            const child_1 = uint32[address + COLUMN_CHILD_1];

            if (child_1 !== NULL_NODE) {

                // this is not a leaf node, push children onto traversal stack
                stack[stack.pointer++] = uint32[address + COLUMN_CHILD_2];
                stack[stack.pointer++] = child_1;

            } else {
                // leaf node

                result[result_cursor++] = node;
            }
        }

        // backtrack
        came_from = entry;
        entry = bvh.node_get_parent(entry);

    } while (entry !== NULL_NODE);

    return result_cursor - result_offset;
}