import { assert } from "../../../../core/assert.js";
import { combine_hash } from "../../../../core/collection/array/combine_hash.js";
import { invokeObjectEquals } from "../../../../core/model/object/invokeObjectEquals.js";
import { invokeObjectHash } from "../../../../core/model/object/invokeObjectHash.js";
import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";

/**
 * @template T
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class WeightedElement {


    weight = 1;

    /**
     *
     * @type {T}
     */
    data = null;

    /**
     * @template T
     * @param {T} element
     * @param {number} [weight=1]
     * @returns {WeightedElement<T>}
     */
    static from(element, weight = 1) {
        assert.defined(element, 'element');

        assert.isNumber(weight, 'weight');
        assert.notNaN(weight, 'weight');
        assert.isFinite(weight, 'weight');
        assert.greaterThanOrEqual(weight, 0, 'weight');

        const r = new WeightedElement();

        r.data = element;
        r.weight = weight;

        return r;
    }

    /**
     * @template X
     * @param {WeightedElement<X>} object
     * @return {number}
     */
    static getWeight(object) {
        return object.weight;
    }

    /**
     *
     * @param {T} other
     * @return {boolean}
     */
    equals(other) {
        return this.weight === other.weight
            && invokeObjectEquals(this.data, other.element);
    }

    /**
     *
     * @return {number}
     */
    hash() {
        return combine_hash(
            computeHashFloat(this.weight),
            invokeObjectHash(this.data)
        );
    }
}

/**
 * @readonly
 * @type {boolean}
 */
WeightedElement.prototype.isWeightedElement = true;
