import { seededRandom } from "../../../../core/math/random/seededRandom.js";
import { MarkerNodeAction } from "../MarkerNodeAction.js";
import { assert } from "../../../../core/assert.js";
import { binarySearchHighIndex } from "../../../../core/collection/array/binarySearchHighIndex.js";
import { number_compare_ascending } from "../../../../core/primitives/numbers/number_compare_ascending.js";

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

export class MarkerNodeActionSelectWeighted extends MarkerNodeAction {

    /**
     *
     * @type {MarkerNodeActionWeightedElement[]}
     */
    elements = [];

    __random = seededRandom(0);

    /**
     *
     * @returns {MarkerNodeActionSelectWeighted}
     * @param {MarkerNodeActionWeightedElement[]} elements
     */
    static from(elements) {
        assert.isArray(elements, 'elements');

        const r = new MarkerNodeActionSelectWeighted();

        r.elements = elements;

        return r;
    }

    initialize(grid, ecd, seed) {
        this.__random.setCurrentSeed(seed);

        const elements = this.elements;
        const n = elements.length;

        for (let i = 0; i < n; i++) {
            /**
             *
             * @type {MarkerNodeActionWeightedElement}
             */
            const element = elements[i];

            element.initialize(grid, ecd, seed + i);
        }

        super.initialize(grid, ecd, seed);
    }

    execute(grid, ecd, node) {
        const elements = this.elements;
        const n = elements.length;

        let totalWeight = 0;

        for (let i = 0; i < n; i++) {
            /**
             *
             * @type {MarkerNodeActionWeightedElement}
             */
            const weightedElement = elements[i];

            /**
             *
             * @type {CellFilter}
             */
            const weightFilter = weightedElement.weight;

            /**
             *
             * @type {Vector2}
             */
            const nodePosition = node.position;

            const weightValue = weightFilter.execute(grid, nodePosition.x, nodePosition.y, 0);


            if (weightValue > 0) {
                //if the weight is negative, we clamp it to 0

                totalWeight += weightValue;
            }

            evaluatedWeights[i] = totalWeight;

        }

        const targetWeight = this.__random() * totalWeight;

        const index = binarySearchHighIndex(evaluatedWeights, targetWeight, number_compare_ascending, 0, n - 1);

        const targetElement = elements[index];

        targetElement.action.execute(grid, ecd, node);
    }
}
