"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const PubSub = require("pubsub-js");
const loggingService_1 = require("./loggingService");
const utilsInternal_1 = require("./utilsInternal");
/**
 * A Pub/Sub event system that enables you to subscribe to various events published by Arc.js.
 * See [Events](/Events).
 */
class PubSubEventService {
    /**
     * Send the given payload to subscribers of the given topic.
     * @param topic See [subscribe](PubSubEventService#subscribe)
     * @param payload Sent in the subscription callback.
     * @returns True if there are any subscribers
     */
    static publish(topic, payload) {
        loggingService_1.LoggingService.debug(`PubSubEventService: publishing ${topic}`);
        return PubSub.publish(topic, payload);
    }
    /**
     * Subscribe to the given topic or array of topics.
     * @param topics Identifies the event(s) to which you wish to subscribe
     * @param callback The function to call when the requested events are published
     * @returns An interface with `.unsubscribe()`.  Be sure to call it!
     */
    static subscribe(topics, callback) {
        return Array.isArray(topics) ?
            PubSubEventService.aggregate(topics, callback) :
            new EventSubscription(PubSub.subscribe(topics, callback));
    }
    /**
     * Remove all subscriptions
     */
    static clearAllSubscriptions() {
        PubSub.clearAllSubscriptions();
    }
    /**
     * Unsubscribes after optional timeout.
     * When passed a token, removes a specific subscription,
     * when passed a callback, removes all subscriptions for that callback,
     * when passed a topic, removes all subscriptions for the topic hierarchy.
     *
     * @param key - A token, function or topic to unsubscribe.
     * @param milliseconds number of milliseconds to timeout.
     * Default is -1 which means not to timeout at all.
     */
    static unsubscribe(key, milliseconds = -1) {
        // timeout to allow lingering events to be handled before unsubscribing
        if (milliseconds === -1) {
            PubSub.unsubscribe(key);
            return Promise.resolve();
        }
        // timeout to allow lingering events to be handled before unsubscribing
        return new Promise((resolve) => {
            setTimeout(() => {
                PubSub.unsubscribe(key);
                resolve();
            }, milliseconds);
        });
    }
    /**
     * Return whether topic is specified by matchTemplates.
     *
     * Examples:
     *
     * matchTemplates: ["foo"]
     * topic: "foo.bar"
     * result: true
     *
     * matchTemplates: ["foo.bar"]
     * topic: "foo"
     * result: false
     *
     * Or a wildcard:
     *
     * matchTemplates: "*"
     * topic: "foo"
     * result: true
     *
     * @param matchTemplates
     * @param topic
     */
    static isTopicSpecifiedBy(matchTemplates, topic) {
        if (!topic) {
            return false;
        }
        if (!matchTemplates) {
            return false;
        }
        if ((typeof matchTemplates === "string") && (matchTemplates === "*")) {
            return true;
        }
        matchTemplates = utilsInternal_1.UtilsInternal.ensureArray(matchTemplates);
        const topicWords = topic.split(".");
        for (const template of matchTemplates) {
            if (!template) {
                continue;
            }
            if (template === topic) {
                return true;
            }
            if (template.length > topic.length) {
                continue;
            }
            if (template[0] === ".") {
                continue;
            }
            const templateWords = template.split(".");
            if (templateWords.length > topicWords.length) {
                continue;
            }
            let matches = false;
            for (let i = 0; i < templateWords.length; ++i) {
                const templateWord = templateWords[i];
                const topicWord = topicWords[i];
                if ((templateWord === "*") || (templateWord === topicWord)) {
                    matches = true;
                }
                else {
                    matches = false;
                    break;
                }
            }
            if (!matches) {
                continue;
            }
            // else matches
            return true;
        }
        return false;
    }
    /**
     * Subscribe to multiple topics with the single given callback.
     * @param topics topic or collection of topics
     * @param callback Callback to handle them all
     * @returns An interface with `.unsubscribe()`.  Be sure to call it!
     */
    static aggregate(topics, callback) {
        return new SubscriptionCollection(topics, callback);
    }
}
exports.PubSubEventService = PubSubEventService;
/**
 * Creates a collection of subscriptions to which one can unsubscribe all at once.
 */
class SubscriptionCollection {
    constructor(topics, callback) {
        this.subscriptions = new Set();
        if (topics) {
            if (!callback) {
                throw new Error("SubscriptionCollection: callback is not set");
            }
            this.subscribe(topics, callback);
        }
    }
    /**
     * Subscribe a single callback to a set of events
     * @param topics
     * @param callback
     */
    subscribe(topics, callback) {
        topics = utilsInternal_1.UtilsInternal.ensureArray(topics);
        topics.forEach((topic) => {
            const subscriptionKey = PubSub.subscribe(topic, callback);
            this.subscriptions.add(new EventSubscription(subscriptionKey));
        });
    }
    /**
     * Unsubscribe from all of the events
     * @param milliseconds number of milliseconds to timeout.
     * Default is -1 which means not to timeout at all.
     */
    unsubscribe(milliseconds = -1) {
        const promises = new Array();
        this.subscriptions.forEach((s) => {
            promises.push(s.unsubscribe.call(s, milliseconds));
        });
        return Promise.all(promises).then(() => {
            this.subscriptions.clear();
        });
    }
}
exports.SubscriptionCollection = SubscriptionCollection;
class EventSubscription {
    constructor(key) {
        this.key = key;
    }
    /**
     * Unsubscribes after optional timeout.
     * @param milliseconds number of milliseconds to timeout.
     * Default is -1 which means not to timeout at all.
     */
    unsubscribe(milliseconds = -1) {
        if (milliseconds === -1) {
            PubSub.unsubscribe(this.key);
            return Promise.resolve();
        }
        // timeout to allow lingering events to be handled before unsubscribing
        return new Promise((resolve) => {
            setTimeout(() => {
                PubSub.unsubscribe(this.key);
                resolve();
            }, milliseconds);
        });
    }
}
exports.EventSubscription = EventSubscription;
//# sourceMappingURL=pubSubEventService.js.map