"use strict";
/*
 * @adonisjs/events
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Emitter = void 0;
/// <reference path="../../adonis-typings/events.ts" />
const emittery_1 = __importDefault(require("emittery"));
const FakeEmitter_1 = require("../FakeEmitter");
const IocResolver_1 = require("../IocResolver");
/**
 * Emitter class exposes the API for async event emitter built on top of
 * Emittery. It also exposes an API to pre-define the Typescript types
 * for different events.
 */
class Emitter {
    constructor(app) {
        this.transport = new emittery_1.default();
        /**
         * Deprecated properties to manage trapping events
         */
        this.trappingEvents = false;
        this.traps = new Map();
        /**
         * Fakes
         */
        this.eventsToFake = new Set();
        this.fakeEmitter = new FakeEmitter_1.FakeEmitter();
        if (app) {
            this.iocResolver = new IocResolver_1.IocResolver(app);
        }
    }
    /**
     * Returns reference to the IoC resolver. Do not call this method until
     * handler is not a string
     */
    getResolver(handler) {
        if (!this.iocResolver) {
            throw new Error(`Cannot resolve string based event handler "${handler}". IoC container is not provided to the event emitter`);
        }
        return this.iocResolver;
    }
    /**
     * Define a custom error handler
     */
    onError(handler) {
        this.errorHandler = handler;
        return this;
    }
    /**
     * Define event handler for a given event
     */
    on(event, handler) {
        if (typeof handler === 'string') {
            handler = this.getResolver(handler).getEventHandler(event, handler);
        }
        this.transport.on(event, handler);
        return this;
    }
    /**
     * Define event handler for a given event and to be called
     * only once.
     */
    once(event, handler) {
        this.transport.once(event).then((data) => {
            if (typeof handler === 'string') {
                this.getResolver(handler).getEventHandler(event, handler)(data);
                this.getResolver(handler).removeEventHandler(event, handler);
            }
            else {
                handler(data);
            }
        });
        return this;
    }
    /**
     * Define catch all event handler to listen for all events.
     */
    onAny(handler) {
        if (typeof handler === 'string') {
            handler = this.getResolver(handler).getAnyHandler(handler);
        }
        this.transport.onAny(handler);
        return this;
    }
    /**
     * Emit event
     */
    async emit(event, data) {
        try {
            let shouldEmitEvent = true;
            /**
             * Register event with the fake emitter
             */
            if (this.eventsToFake.has('*') || this.eventsToFake.has(event)) {
                shouldEmitEvent = false;
                this.fakeEmitter.events.push({ name: event, data });
            }
            if (this.trappingEvents) {
                /**
                 * Give preference to the handler for a specific event
                 */
                if (this.traps.has(event)) {
                    shouldEmitEvent = false;
                    await this.traps.get(event)(data);
                }
                else if (this.trapAllHandler) {
                    shouldEmitEvent = false;
                    await this.trapAllHandler(event, data);
                }
            }
            if (shouldEmitEvent) {
                return await this.transport.emit(event, data);
            }
        }
        catch (error) {
            if (this.errorHandler) {
                return this.errorHandler(event, error, data);
            }
            throw error;
        }
    }
    /**
     * Remove existing event listener
     */
    off(event, handler) {
        if (typeof handler === 'string') {
            const offHandler = this.getResolver(handler).removeEventHandler(event, handler);
            if (offHandler) {
                this.transport.off(event, offHandler);
            }
            return;
        }
        this.transport.off(event, handler);
    }
    /**
     * Remove existing event listener for catch all handler
     */
    offAny(handler) {
        if (typeof handler === 'string') {
            const offHandler = this.getResolver(handler).removeAnyHandler(handler);
            if (offHandler) {
                this.transport.offAny(offHandler);
            }
            return;
        }
        this.transport.offAny(handler);
    }
    /**
     * Remove existing event listener.
     * @alias off
     */
    clearListener(event, handler) {
        this.off(event, handler);
    }
    /**
     * Clear all listeners for a given event
     */
    clearListeners(event) {
        this.transport.clearListeners(event);
    }
    /**
     * Clear all listeners for all events
     */
    clearAllListeners() {
        this.transport.clearListeners();
    }
    /**
     * Returns count of listeners for a given event or all
     * events.
     */
    listenerCount(event) {
        return this.transport.listenerCount(event ? event : undefined);
    }
    /**
     * Returns a boolean telling if listeners count for a given
     * event or all events is greater than 0 or not.
     */
    hasListeners(event) {
        return this.listenerCount(event) > 0;
    }
    /**
     * Define custom namespace for event listeners. It is set to `App/Listeners`
     * by default.
     */
    namespace(namespace) {
        if (this.iocResolver) {
            this.iocResolver.namespace(namespace);
        }
        return this;
    }
    /**
     * Trap event instead of emitting it
     */
    trap(event, handler) {
        process.emitWarning('DeprecationWarning', '"Event.trap" is deprecated. Instead use "Event.fake" method');
        this.trappingEvents = true;
        this.traps.set(event, handler);
        return this;
    }
    /**
     * Trap all events instead of emitting them
     */
    trapAll(handler) {
        process.emitWarning('DeprecationWarning', '"Event.trapAll" is deprecated. Instead use "Event.fake" method');
        this.trappingEvents = true;
        this.trapAllHandler = handler;
        return this;
    }
    /**
     * Fake event emitter to collect events in-memory vs
     * emitting them
     */
    fake(events) {
        /**
         * If no events have been mentioned, then fake
         * all the events
         */
        if (!events) {
            this.eventsToFake.add('*');
            return this.fakeEmitter;
        }
        /**
         * Only track event names when wildcard is not added
         */
        if (!this.eventsToFake.has('*')) {
            events.forEach((event) => this.eventsToFake.add(event));
        }
        return this.fakeEmitter;
    }
    /**
     * Restore fakes
     */
    restore() {
        this.trappingEvents = false;
        this.trapAllHandler = undefined;
        this.traps.clear();
        this.eventsToFake.clear();
        this.fakeEmitter.restore();
    }
}
exports.Emitter = Emitter;
