/**
 * Copyright schukai GmbH and contributors 2022. All Rights Reserved.
 * Node module: @schukai/monster
 * This file is licensed under the AGPLv3 License.
 * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
 */


import {internalSymbol} from "../constants.mjs";

import {Base} from "../types/base.mjs";
import {isInteger} from "../types/is.mjs";
import {validateFunction, validateInteger} from "../types/validate.mjs";

export {DeadMansSwitch}

/**
 * The dead man's switch allows to set a timer which can be reset again and again within a defined period of time.
 *
 * ```
 * <script type="module">
 * import {DeadMansSwitch} from '@schukai/monster/source/util/deadmansswitch.mjs';
 * new DeadMansSwitch();
 * </script>
 * ```
 *
 * @example
 * import {DeadMansSwitch} from '@schukai/monster/source/util/deadmansswitch.mjs';
 *
 * const deadmansswitch = new DeadMansSwitch(100, ()=>{
 *   console.log('yeah!')
 *   // ↦ "yeah!"
 * })
 *
 * deadmansswitch.touch(); // from here wait again 100 ms
 * deadmansswitch.touch(200); // from here wait 200 ms
 *
 * @copyright schukai GmbH
 * @license AGPLv3
 * @since 1.29.0
 * @memberOf Monster.Util
 * @summary Class to be able to execute function chains
 */
 class DeadMansSwitch extends Base {

    /**
     * Create new dead man's switch
     *
     * @param {Integer} delay
     * @param {function} callback
     * @throw {TypeError} the arguments must be either integer or functions
     * @throws {TypeError} value is not an integer
     */
    constructor(delay, callback) {
        super();

        init.call(this, validateInteger(delay), validateFunction(callback));
    }

    /**
     *
     * @param {Integer|undefined} [delay]
     */
    touch(delay) {

        if (this[internalSymbol]['isAlreadyRun'] === true) {
            throw new Error('has already run')
        }

        if (isInteger(delay)) {
            this[internalSymbol]['delay'] = delay
        } else if (delay !== undefined) {
            throw new Error('unsupported argument')
        }

        clearTimeout(this[internalSymbol]['timer']);

        initCallback.call(this);

        return this;
    }
}

/**
 * @private
 */
function initCallback() {

    const self = this;

    self[internalSymbol]['timer'] = setTimeout(() => {
        self[internalSymbol]['isAlreadyRun'] = true;
        self[internalSymbol]['callback']();
    }, self[internalSymbol]['delay'])
}

/**
 * @private
 * @param {integer} delay
 * @param {function} callback
 */
function init(delay, callback) {
    const self = this;

    self[internalSymbol] = {
        callback,
        delay,
        isAlreadyRun: false,
        timer: undefined
    };

    initCallback.call(self);

}