/**
 * Event kinds emitted by the manifold diff pass.
 * @readonly
 * @enum {number}
 */
export const ContactEventKind = {
    Begin: 0,
    Stay: 1,
    End: 2,
};

const DEFAULT_INITIAL_CAPACITY = 32;

/**
 * Per-event stride in the Int32 backing array:
 *   0 : kind (ContactEventKind)
 *   1 : entityA
 *   2 : entityB
 *   3 : slot
 * @type {number}
 */
const EVENT_STRIDE = 4;

/**
 * Append-only buffer of contact events produced during one fixedUpdate, ready
 * to be dispatched (and then cleared) at end-of-step.
 *
 * Storage is flat Int32Array (signed: entity ids may legitimately be -1 for a
 * stale body after free) — keeping the metadata side allocation-free; the
 * actual contact payload (normal, depth, world point) is read on demand from
 * the {@link ManifoldStore} by the dispatch code.
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export class ContactEventBuffer {
    /**
     * @param {number} [initial_capacity] events
     */
    constructor(initial_capacity = DEFAULT_INITIAL_CAPACITY) {
        this.__capacity = Math.max(1, initial_capacity);
        this.__count = 0;
        // Int32 so entity ids may legitimately store -1 (stale body after free).
        this.__data = new Int32Array(this.__capacity * EVENT_STRIDE);
    }

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

    clear() {
        this.__count = 0;
    }

    /**
     * @param {ContactEventKind|number} kind
     * @param {number} entityA
     * @param {number} entityB
     * @param {number} slot manifold slot id
     */
    push(kind, entityA, entityB, slot) {
        if (this.__count === this.__capacity) {
            const new_cap = this.__capacity << 1;
            const next = new Int32Array(new_cap * EVENT_STRIDE);
            next.set(this.__data);
            this.__data = next;
            this.__capacity = new_cap;
        }
        const off = this.__count * EVENT_STRIDE;
        this.__data[off] = kind;
        this.__data[off + 1] = entityA;
        this.__data[off + 2] = entityB;
        this.__data[off + 3] = slot;
        this.__count++;
    }

    kind_at(i) { return this.__data[i * EVENT_STRIDE]; }
    entityA_at(i) { return this.__data[i * EVENT_STRIDE + 1]; }
    entityB_at(i) { return this.__data[i * EVENT_STRIDE + 2]; }
    slot_at(i) { return this.__data[i * EVENT_STRIDE + 3]; }
}
