"use strict";
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
Object.defineProperty(exports, "__esModule", { value: true });
exports.BetterEventEmitter = void 0;
const LISTENERS_SUFFIX = '\0listeners';
class BetterEventEmitter {
    listeners = new Map();
    stop = Symbol();
    on(event, listener, { warnAfter = 10, callNow = false } = {}) {
        if (typeof event === 'string')
            event = [event];
        for (const e of event) {
            let cbs = this.listeners.get(e);
            if (!cbs)
                this.listeners.set(e, cbs = new Set());
            cbs.add(listener);
            if (cbs.size > warnAfter)
                console.warn("Warning: many event listeners for ", e);
            this.emit(e + LISTENERS_SUFFIX, cbs, listener);
        }
        if (callNow)
            try {
                listener();
            }
            catch { }
        return () => {
            for (const e of event) {
                const cbs = this.listeners.get(e);
                if (!cbs)
                    continue;
                cbs.delete(listener);
                this.emit(e + LISTENERS_SUFFIX, cbs);
            }
        };
    }
    // call me when listeners for the event have changed
    onListeners(event, listener) {
        return this.on(event + LISTENERS_SUFFIX, listener);
    }
    // returns the unsubscriber function, which is also a PromiseLike with the array of arguments received by the listener
    once(event, listener) {
        let off;
        const pro = new Promise(resolve => {
            off = this.on(event, function (...args) {
                off();
                resolve(args.slice(0, -1)); // remove the extra argument at the end of our emit()
                return listener?.(...args);
            });
        });
        return Object.assign(off, { then: pro.then.bind(pro) });
    }
    multi(map, options) {
        const cbs = Object.entries(map).map(([name, cb]) => this.on(name.split(' '), cb, options));
        return () => {
            for (const cb of cbs)
                cb();
        };
    }
    anyListener(event) {
        return Boolean(this.listeners.get(event)?.size);
    }
    emit(event, ...args) {
        let cbs = this.listeners.get(event);
        if (!cbs?.size)
            return;
        const output = [];
        let prevented = false;
        const extra = {
            event,
            output,
            preventDefault() { prevented = true; }
        };
        for (const cb of cbs) {
            const res = cb(...args, extra);
            if (res === this.stop)
                extra.preventDefault();
            else if (res !== undefined)
                output.push(res);
        }
        return Object.assign(output, {
            isDefaultPrevented: () => prevented,
        });
    }
    async emitAsync(event, ...args) {
        const syncRet = this.emit(event, ...args);
        if (!syncRet)
            return;
        const asyncRet = await Promise.all(syncRet);
        return Object.assign(asyncRet, {
            isDefaultPrevented: () => syncRet.isDefaultPrevented()
                || asyncRet.some((r) => r === this.stop)
        });
    }
}
exports.BetterEventEmitter = BetterEventEmitter;
// app-wide events
exports.default = new BetterEventEmitter;
