import { is_function, noop, not_equal, run_all } from 'svelte/internal';
const subscriber_queue = [];
/**
 * Creates a `Readable` store that allows reading by subscription.
 * @param value initial value
 * @param {StartStopNotifier}start start and stop notifications for subscriptions
 */
export function readable(value, start) {
    const { get, subscribe } = writable(value, start);
    return {
        get,
        subscribe,
    };
}
/**
 * Create a `Writable` store that allows both updating and reading by subscription.
 * @param {*=}value initial value
 * @param {StartStopNotifier=}start start and stop notifications for subscriptions
 */
export function writable(value, start = noop) {
    let stop;
    const subscribers = [];
    function get() {
        if (!subscribers.length)
            (start(set) || noop)();
        return value;
    }
    function set(new_value) {
        if (not_equal(value, new_value)) {
            const promises = [];
            value = new_value;
            if (stop) {
                // store is ready
                const run_queue = !subscriber_queue.length;
                for (let i = 0; i < subscribers.length; i += 1) {
                    const s = subscribers[i];
                    s[1]();
                    subscriber_queue.push(s, value);
                }
                if (run_queue) {
                    for (let i = 0; i < subscriber_queue.length; i += 2) {
                        promises.push(subscriber_queue[i][0](subscriber_queue[i + 1]));
                    }
                    subscriber_queue.length = 0;
                }
            }
            return Promise.all(promises).then(() => { });
        }
        return Promise.resolve();
    }
    function update(fn) {
        return set(fn(value));
    }
    function subscribe(run, invalidate = noop) {
        const subscriber = [run, invalidate];
        subscribers.push(subscriber);
        if (subscribers.length === 1) {
            stop = start(set) || noop;
        }
        run(value);
        return () => {
            const index = subscribers.indexOf(subscriber);
            if (index !== -1) {
                subscribers.splice(index, 1);
            }
            if (subscribers.length === 0) {
                stop();
                stop = null;
            }
        };
    }
    return { get, set, update, subscribe };
}
export function immutableWritable(value, start = noop) {
    const { get, update, set, subscribe } = writable(value, start);
    function immutableSet(value) {
        return set({ ...value });
    }
    return { get, update, set: immutableSet, subscribe };
}
/**
 * Derived value store by synchronizing one or more readable stores and
 * applying an aggregation function over its input values.
 * @param {Stores} stores input stores
 * @param {function(Stores=, function(*)=):*}fn function callback that aggregates the values
 * @param {*=}initial_value when used asynchronously
 */
export function derived(stores, fn, initial_value) {
    const single = !Array.isArray(stores);
    const stores_array = single ? [stores] : stores;
    const auto = fn.length < 2;
    return readable(initial_value, set => {
        let inited = false;
        const values = [];
        let pending = 0;
        let cleanup = noop;
        const sync = () => {
            if (pending) {
                return;
            }
            cleanup();
            const result = fn(single ? values[0] : values.slice(), set);
            if (auto) {
                set(result);
            }
            else {
                cleanup = is_function(result) ? result : noop;
            }
        };
        const unsubscribers = stores_array.map((store, i) => store.subscribe(value => {
            pending &= ~(1 << i);
            if (not_equal(values[i], value)) {
                values[i] = value;
                if (inited) {
                    sync();
                }
            }
        }, () => {
            pending |= 1 << i;
        }));
        inited = true;
        sync();
        return function stop() {
            run_all(unsubscribers);
            cleanup();
        };
    });
}
