import { assert } from "../../assert.js";

/**
 * Worker manager that will terminate the worker after a timeout if it is idle.
 *
 */
export class OnDemandWorkerManager {

    /**
     * Number of active pending requests
     * @type {number}
     * @private
     */
    __request_count = 0;

    /**
     * How long to wait before terminating the worker after it becomes idle
     * in milliseconds
     * @type {number}
     * @private
     */
    __timeout_ms = 1000;

    /**
     * ID of the timer set via setTimeout
     * @type {number}
     * @private
     */
    __pending_termination = -1;

    /**
     *
     * @param {WorkerProxy} worker
     */
    constructor(worker) {
        assert.defined(worker, 'worker');

        /**
         * @type {WorkerProxy}
         */
        this.worker = worker;
    }

    /**
     * Configure how long to wait after processing a request until deciding to shut down the worker.
     * The browser limits the number of workers we can have open at once, so releasing them when not in use is critical.
     *
     * @param {number} delay_ms In milliseconds
     */
    setTimeout(delay_ms) {
        assert.isNonNegativeInteger(delay_ms, 'delay_ms');

        this.__timeout_ms = delay_ms;
    }

    /**
     * @private
     */
    terminate = () => {
        this.cancelScheduledTermination();

        this.worker.stop();

    }

    /**
     * @private
     */
    cancelScheduledTermination() {

        if (this.__pending_termination < 0) {
            // no pending termination
            return;
        }

        clearInterval(this.__pending_termination);
        this.__pending_termination = -1;
    }

    /**
     *
     * @param {number} timeout
     */
    scheduleTermination(timeout) {
        this.cancelScheduledTermination();

        this.__pending_termination = setTimeout(this.terminate, timeout);
    }

    /**
     * @private
     */
    decrement = () => {
        this.__request_count--;

        if (this.__request_count <= 0 && this.worker.isRunning()) {
            this.scheduleTermination(this.__timeout_ms);
        }
    }

    /**
     * @private
     */
    increment() {
        this.__request_count++;

        if (!this.worker.isRunning()) {

            // worker is down, start it

            this.worker.start();

        } else {

            // worker is up, ensure we're not waiting for it to terminate
            this.cancelScheduledTermination();

        }
    }


    /**
     * Submit a request to the worker
     * @template T
     * @param {string} name
     * @param {Array} [parameters]
     * @return {Promise<T>}
     */
    request(name, parameters) {

        this.increment();

        const promise = this.worker.$submitRequest(name, parameters);

        promise.finally(this.decrement);

        return promise;
    }
}
