/**
 * Growable Uint32Array list of `(idA, idB)` body-id pairs. Stride is 2 words
 * per pair. `count` tracks the number of pairs; capacity grows by doubling.
 *
 * No allocation happens during {@link push} until capacity is exceeded. The
 * intended usage is `clear()` at the top of each step and `push` for each
 * candidate pair encountered during broadphase.
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class PairList {

    /**
     * @param {number} [initial_capacity] pairs (not words)
     */
    constructor(initial_capacity = 64) {
        this.__capacity = Math.max(1, initial_capacity);
        this.__count = 0;
        this.__data = new Uint32Array(this.__capacity * 2);
    }

    /**
     * @returns {number} number of pairs currently stored
     */
    get count() {
        return this.__count;
    }

    /**
     * @returns {number} pair capacity (doubles on overflow)
     */
    get capacity() {
        return this.__capacity;
    }

    /**
     * Raw word buffer. `data[i*2 + 0]` is idA, `data[i*2 + 1]` is idB for the
     * `i`-th pair, where `i < count`. Exposed for hot-loop iteration; valid
     * until the next {@link push} that triggers a grow.
     * @returns {Uint32Array}
     */
    get data() {
        return this.__data;
    }

    /**
     * Reset to empty without releasing capacity.
     */
    clear() {
        this.__count = 0;
    }

    /**
     * Append a pair. Caller is expected to have already canonicalised
     * `idA < idB` if symmetric semantics are needed.
     *
     * @param {number} idA
     * @param {number} idB
     */
    push(idA, idB) {
        if (this.__count === this.__capacity) {
            this.__grow();
        }
        const off = this.__count * 2;
        this.__data[off] = idA;
        this.__data[off + 1] = idB;
        this.__count++;
    }

    /**
     * @param {number} i pair index, `[0, count)`
     * @returns {number}
     */
    get_a(i) {
        return this.__data[i * 2];
    }

    /**
     * @param {number} i pair index, `[0, count)`
     * @returns {number}
     */
    get_b(i) {
        return this.__data[i * 2 + 1];
    }

    /**
     * @private
     */
    __grow() {
        const new_capacity = this.__capacity << 1;
        const next = new Uint32Array(new_capacity * 2);
        next.set(this.__data);
        this.__data = next;
        this.__capacity = new_capacity;
    }
}
