const MszWatcher = function() {
    const handlers = [];

    const watch = (handler, ...args) => {
        if(typeof handler !== 'function')
            throw 'handler must be a function';
        if(handlers.includes(handler))
            throw 'handler already registered';

        handlers.push(handler);
        args.push(true);
        handler(...args);
    };

    const unwatch = handler => {
        $ari(handlers, handler);
    };

    return {
        watch: watch,
        unwatch: unwatch,
        call: (...args) => {
            args.push(false);

            for(const handler of handlers)
                handler(...args);
        },
    };
};

const MszWatchers = function() {
    const watchers = new Map;

    const getWatcher = name => {
        const watcher = watchers.get(name);
        if(watcher === undefined)
            throw 'undefined watcher name';
        return watcher;
    };

    const watch = (name, handler, ...args) => {
        getWatcher(name).watch(handler, ...args);
    };

    const unwatch = (name, handler) => {
        getWatcher(name).unwatch(handler);
    };

    return {
        watch: watch,
        unwatch: unwatch,
        define: names => {
            if(typeof names === 'string')
                watchers.set(names, new MszWatcher);
            else if(Array.isArray(names))
                for(const name of names)
                    watchers.set(name, new MszWatcher);
            else
                throw 'names must be an array of names or a single name';
        },
        call: (name, ...args) => {
            getWatcher(name).call(...args);
        },
    };
};