// FIXME: Once we upgrade to Node.js 20, use the global CustomEvent class
class CustomEvent extends Event {
    constructor(type, detail) {
        super(type);
        this._detail = detail;
    }
    _detail;
    get detail() {
        return this._detail;
    }
}
/**
 * A type-safe EventEmitter replacement that internally uses the portable _eventTarget API.
 *
 * **Usage:**
 *
 * 1.) Define event signatures
 * ```ts
 * interface TestEvents {
 * 	test1: (arg1: number) => void;
 * 	test2: () => void;
 * }
 * ```
 *
 * 2a.) direct inheritance:
 * ```ts
 * class Test extends TypedEventTarget<TestEvents> {
 * 	// class implementation
 * }
 * ```
 * 2b.) as a mixin
 * ```ts
 * interface Test extends TypedEventTarget<TestEvents> {}
 * Mixin([EventEmitter]) // This is a decorator - prepend it with an <at> sign
 * class Test extends OtherClass implements TypedEventTarget<TestEvents> {
 * 	// class implementation
 * }
 * ```
 */
export class TypedEventTarget {
    // We lazily initialize the instance properties, so they can be used in mixins
    _eventTarget;
    get eventTarget() {
        this._eventTarget ??= new EventTarget();
        return this._eventTarget;
    }
    _listeners;
    get listeners() {
        this._listeners ??= new Map();
        return this._listeners;
    }
    _wrappers;
    get wrappers() {
        this._wrappers ??= new WeakMap();
        return this._wrappers;
    }
    getWrapper(event, callback, once = false) {
        if (this.wrappers.has(callback)) {
            return this.wrappers.get(callback);
        }
        else {
            const wrapper = (e) => {
                const detail = e
                    .detail;
                // @ts-expect-error
                callback(...detail);
                if (once)
                    this.listeners.get(event)?.delete(callback);
            };
            this.wrappers.set(callback, wrapper);
            return wrapper;
        }
    }
    rememberListener(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, new Set());
        }
        this.listeners.get(event).add(callback);
    }
    on(event, callback) {
        this.eventTarget.addEventListener(event, this.getWrapper(event, callback));
        this.rememberListener(event, callback);
        return this;
    }
    once(event, callback) {
        this.eventTarget.addEventListener(event, this.getWrapper(event, callback, true), { once: true });
        return this;
    }
    removeListener(event, callback) {
        if (this.wrappers.has(callback)) {
            this.eventTarget.removeEventListener(event, this.wrappers.get(callback));
            this.wrappers.delete(callback);
        }
        if (this.listeners.has(event)) {
            this.listeners.get(event).delete(callback);
        }
        return this;
    }
    removeAllListeners(event) {
        if (event) {
            if (this.listeners.has(event)) {
                for (const callback of this.listeners.get(event)) {
                    this.removeListener(event, callback);
                }
            }
        }
        else {
            for (const event of this.listeners.keys()) {
                this.removeAllListeners(event);
            }
        }
        return this;
    }
    off(event, callback) {
        return this.removeListener(event, callback);
    }
    emit(event, ...args) {
        return this.eventTarget.dispatchEvent(new CustomEvent(event, args));
    }
}
//# sourceMappingURL=EventTarget.js.map