import { assert } from "../../assert.js";
import { binarySearchHighIndex } from "./binarySearchHighIndex.js";
import { number_compare_ascending } from "../../primitives/numbers/number_compare_ascending.js";


/**
 *
 * @type {number[]}
 */
const scratch_weights = [];

/**
 * @template T
 * @param {T[]} array
 * @param {function} random
 * @param {function(T):number} weight
 * @param {*} [weightContext]
 * @param {number} [length]
 * @returns {T|undefined}
 */
export function weightedRandomFromArray(array, random, weight, weightContext, length = array.length) {
    assert.isArray(array, 'array');
    assert.isFunction(random, 'random');
    assert.isFunction(weight, 'weight');

    if (length === 0) {
        return undefined;
    }

    let weight_total = 0;

    for (let i = 0; i < length; i++) {
        const el = array[i];

        const w = weight.call(weightContext, el);

        if (w > 0) {
            // ignore negative weights, clamp them to 0

            weight_total += w;
        }

        scratch_weights[i] = weight_total;
    }

    const target_weight = random() * weight_total;

    const index = binarySearchHighIndex(scratch_weights, target_weight, number_compare_ascending, 0, length - 1);

    return array[index];
}
