#include eventtarget.js
#include utility.js

const MamiConnectionManager = function(urls) {
    if(!Array.isArray(urls))
        throw 'urls must be an array';

    const eventTarget = new MamiEventTarget('mami:conn');
    const delays = [0, 2000, 2000, 2000, 5000, 5000, 5000, 5000, 5000, 10000, 10000, 10000, 10000, 10000, 15000, 30000, 45000, 60000, 120000, 300000];

    let timeout;
    let attempts, started, delay, url;
    let attemptConnect;
    let startResolve;

    const resetTimeout = () => {
        if(timeout !== undefined) {
            clearTimeout(timeout);
            timeout = undefined;
        }
    };

    const clear = () => {
        resetTimeout();
        attempts = started = delay = 0;
        url = undefined;
    };

    $as(urls);

    const onFailure = ex => {
        ++attempts;
        delay = attempts < delays.length ? delays[attempts] : delays[delays.length - 1];

        eventTarget.dispatch('fail', {
            url: url,
            started: started,
            attempt: attempts,
            delay: delay,
            error: ex,
        });

        attempt();
    };

    const attempt = () => {
        started = Date.now();
        url = urls[attempts % urls.length];

        const attempt = attempts + 1;

        timeout = setTimeout(() => {
            resetTimeout();

            eventTarget.dispatch('attempt', {
                url: url,
                started: started,
                attempt: attempt,
            });

            attemptConnect(url).then(result => {
                if(typeof result === 'boolean' && !result) {
                    onFailure();
                    return;
                }

                eventTarget.dispatch('success', {
                    url: url,
                    started: started,
                    attempt: attempt,
                });

                startResolve();
                startResolve = undefined;
                attemptConnect = undefined;
            }).catch(ex => onFailure(ex));
        }, delay);
    };

    const isActive = () => timeout !== undefined || startResolve !== undefined;

    return {
        isActive: isActive,
        start: body => {
            return new Promise(resolve => {
                if(typeof body !== 'function')
                    throw 'body must be a function';
                if(isActive())
                    throw 'already attempting to connect';

                attemptConnect = body;
                startResolve = resolve;

                clear();
                attempt();
            });
        },
        force: () => {
            if(!isActive())
                return;

            resetTimeout();
            delay = 0;
            attempt();
        },
        clear: clear,
        watch: eventTarget.watch,
        unwatch: eventTarget.unwatch,
    };
};
