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

/**
 * A watchdog timer that executes a callback function if `kick()` is not called within a specified timeout period.
 */
export class WatchDog {

    /**
     * @private
     * @type {number}
     */
    timeoutId = -1;

    /**
     * Timeout duration in milliseconds
     * @type {number}
     */
    timeout = 0;

    /**
     *
     * @type {number}
     * @private
     */
    __timeLastKicked = 0;

    /**
     *
     * @param {function} action Callback function to execute on timeout
     * @param {*} [actionContext] `this` context for the callback function.
     * @constructor
     */
    constructor(action, actionContext) {
        assert.isFunction(action,'action');

        /**
         * Callback function to be executed when the watchdog timer expires.
         * @type {function}
         */
        this.action = action;

        /**
         * The context (`this` value) for the {@link action} callback.
         * @type {*}
         */
        this.actionContext = actionContext;
    }
    /**
     * @private
     * Executes the callback function and clears the timer.  Called when the watchdog timer expires.
     */
    bark() {

            console.warn('WatchDog.bark');

            this.action.call(this.actionContext);

            this.timeoutId = -1;
        }
    /**
     * Starts the watchdog timer.
     * If the timer is already running, this does nothing.
     */
    start() {
        //console.warn(`WatchDog.start`);

        if (this.isActive()) {
            //do nothing
        } else {
            this.timeoutId = setTimeout(this.bark.bind(this), this.timeout);
            this.__timeLastKicked = Date.now();
        }
    }

    /**
     * Stops the watchdog timer.
     * If the timer is not running, this does nothing.
     */
    stop() {
        //console.warn(`WatchDog.stop`);

        if (this.isActive()) {
            clearTimeout(this.timeoutId);
            this.timeoutId = -1;
        } else {
            //do nothing, no active timeout
        }
    }

    /**
     * Checks if the watchdog timer is currently active.
     * @returns {boolean} `true` if the timer is running, `false` otherwise.
     */
    isActive() {
        return this.timeoutId !== -1;
    }

    /**
     * Resets the watchdog timer.  Throws an error if the timer is not active.
     * @throws {Error} If the watchdog is not active.
     */
    kick() {
        // const timeLastKicked = this.__timeLastKicked;

        this.__timeLastKicked = Date.now();

        // const delta = timeNow - timeLastKicked;

        //TODO: remember callstack

        // console.warn(`WatchDog.kick. ${delta}ms since last`);

        if (this.isActive()) {
            clearTimeout(this.timeoutId);
            this.timeoutId = setTimeout(this.bark.bind(this), this.timeout);
        } else {
            throw new Error(`WatchDog is not active`);
        }
    }

    /**
     * Sets the timeout duration. Does not start or restart the timer.
     * @param {number} delay Timeout duration in milliseconds.
     */
    setTimeout(delay) {
        this.timeout = delay;
    }
}
