import jQuery from "jquery";

// Helpers

/**
 * "Types" out a DOM element, emulating the way a human might.
 */
class Typer {
    private element: JQuery;
    private text: string;
    private cursor: boolean;
    private typed: number;
    private min: number;
    private max: number;
    private blink_tick: number;
    private blink_timeout: number;
    private end?: number;

    /**
     * Create the typer.
     * @param {HTMLElement} element - The element to type.
     * @param {number} blink - The time between cursor blinks.
     * @param {number} blink_timeout - How long the cursor should keep
     *                                 blinking for after the text
     *                                 finishes typing.
     */
    constructor(element: JQuery<HTMLElement>, blink: number, blink_timeout: number) {
        // Retrieve the current content and wipe it. We also make the
        // element visible if it was hidden.
        this.element = element;
        this.text = this.element.html();
        this.element.html("");
        this.element.css("visibility", "visible");

        this.cursor = false;
        this.typed = 0;

        this.min = 20;
        this.max = 70;
        this.blink_tick = blink;
        this.blink_timeout = blink_timeout;

        this.end = null;
    }

    /**
     * Start typing.
     */
    type() {
        this._type();
        this._blink();
    }

    /**
     * Draw the current text line, i.e., anything that has been typed
     * so far, and a cursor if it is currently supposed to be on.
     * @private
     */
    _draw() {
        let text = this.text.slice(0, this.typed);

        if (this.cursor) {
            text += "\u2588";
        }

        window.requestAnimationFrame(() => this.element.html(text));
    }

    /**
     * Type the next character, and prepare to draw the next one. If
     * no new characters are to be drawn, set the end timestamp.
     * @private
     */
    _type() {
        this.typed += 1;
        this._draw();

        if (this.typed != this.text.length)
            setTimeout(this._type.bind(this), this._type_tick());
        else {
            this.end = Date.now();
        }
    }

    /**
     * Make the cursor change blink status, and prepare for the next
     * blink.
     * @private
     */
    _blink() {
        this.cursor = !this.cursor;
        this._draw();

        // As long as we are typing, keep blinking
        if (this.typed != this.text.length)
            setTimeout(this._blink.bind(this), this.blink_tick);
        // Once typing ends, keep going for a little bit
        else if (Date.now() - this.end < this.blink_timeout)
            setTimeout(this._blink.bind(this), this.blink_tick);
        // Make sure we get rid of the cursor in the end
        else {
            this.cursor = true;
            setTimeout(this._blink.bind(this), this.blink_tick);
        }
    }

    /**
     * Calculate a "human" time for the next character to type.
     * @private
     */
    _type_tick() {
        return Math.round(Math.random() * this.max) + this.min;
    }
}

jQuery(($) => {
    const typer = new Typer($(".head-line .typed").first(), 500, 3000);
    typer.type();
});
