// @ts-check

import { Navigator } from "./navigator.js"

/**
 * @typedef {object} CustomRef
 * @property {HTMLButtonElement} first The button to jump to the first slide
 * @property {HTMLButtonElement} prev The button to move to the previous slide
 * @property {HTMLButtonElement} next The button to advance to the next slide
 * @property {HTMLButtonElement} last The button to advance to the last slide
 * @property {HTMLSpanElement} pos The span for the positional information
 */

/**
 * Custom element that renders controls to navigate the deck
 * @extends {HTMLElement}
 */
export class Controls extends HTMLElement {

    /**
     * Create a new instance of controls
     */
    constructor() {
        super();
        /**
         * The internal reference list of controls
         * @type {CustomRef}
         */
        this._controlRef = null;
        /**
         * The related Navigator instance (deck) to control
         * @type {Navigator}
         */
        this._deck = null;
    }

    /**
     * Called when the element is inserted into the DOM. Used to fetch the template and wire into the related Navigator instance.
     */
    async connectedCallback() {
        const response = await fetch("/templates/controls.html");
        const template = await response.text();
        this.innerHTML = "";
        const host = document.createElement("div");
        host.innerHTML = template;
        this.appendChild(host);
        this._controlRef = {
            first: /** @type {HTMLButtonElement} **/(document.getElementById("ctrlFirst")),
            prev: /** @type {HTMLButtonElement} **/(document.getElementById("ctrlPrevious")),
            next: /** @type {HTMLButtonElement} **/(document.getElementById("ctrlNext")),
            last: /** @type {HTMLButtonElement} **/(document.getElementById("ctrlLast")),
            pos: /** @type {HTMLSpanElement} **/(document.getElementById("position"))
        };
        this._controlRef.first.addEventListener("click", () => this._deck.jumpTo(0));
        this._controlRef.prev.addEventListener("click", () => this._deck.previous());
        this._controlRef.next.addEventListener("click", () => this._deck.next());
        this._controlRef.last.addEventListener("click", () => this._deck.jumpTo(this._deck.totalSlides - 1));
        this.refreshState();
    }

    /**
     * Get the list of attributes to watch
     * @returns {string[]} List of observable attributes
     */
    static get observedAttributes() {
        return ["deck"];
    }

    /**
     * Called when the attribute is set
     * @param {string} attrName Name of the attribute that was set
     * @param {string} oldVal The old attribute value
     * @param {string} newVal The new attribute value
     */
    async attributeChangedCallback(attrName, oldVal, newVal) {
        if (attrName === "deck") {
            if (oldVal !== newVal) {
                this._deck = /** @type {Navigator} */(document.getElementById(newVal));
                this._deck.addEventListener("slideschanged", () => this.refreshState());
            }
        }
    }

    /**
     * Enables/disables buttons and updates position based on index in the deck
     */
    refreshState() {
        if (this._controlRef == null) {
            return;
        }
        const next = this._deck.hasNext;
        const prev = this._deck.hasPrevious;
        this._controlRef.first.disabled = !prev;
        this._controlRef.prev.disabled = !prev;
        this._controlRef.next.disabled = !next;
        this._controlRef.last.disabled = this._deck.currentIndex === (this._deck.totalSlides - 1);
        this._controlRef.pos.innerText = `${this._deck.currentIndex + 1} / ${this._deck.totalSlides}`;
    }
}

/** Register the custom slide-controls element */
export const registerControls = () => customElements.define('slide-controls', Controls);