class RubiksCubeScrambler extends HTMLElement {
    static #RandomMoveHysteresisMaxLength = 2;

    #shadowRoot;
    #movesListElement;

    #numberOfMovesToGenerate = 25;

    constructor() {
        super();
        this.#shadowRoot = this.attachShadow({ mode: "open" });
    }

    scramble() {
        console.log("Randomizing Rubik's cube...");

        const movesList = this.#movesListElement;

        while (movesList.childElementCount > this.#numberOfMovesToGenerate) {
            movesList.removeChild(movesList.lastChild);
        }

        let randomMoveHysteresis = [];

        for (let i = 0; i < this.#numberOfMovesToGenerate; i++) {
            const randomMove = this.#randomMove(randomMoveHysteresis);

            let moveItem;
            if (i < movesList.childElementCount) {
                moveItem = movesList.children[i];
            } else {
                moveItem = document.createElement("li");
                movesList.appendChild(moveItem);
            }

            moveItem.classList.add("scrambler__move");
            moveItem.classList.remove("scrambler__move--start", "scrambler__move--end");

            if (randomMove.includes("2")) {
                moveItem.classList.add("scrambler__move--start");
            } else if (randomMove.includes("'")) {
                moveItem.classList.add("scrambler__move--end");
            }

            moveItem.innerText = randomMove;
        }
    }

    #randomMove(hysteresis) {
        const faces = "FBLRUD";

        let move;
        do {
            move = faces.charAt(Math.floor(Math.random() * faces.length));
        } while (hysteresis && hysteresis.includes(move));

        if (hysteresis) {
            hysteresis.unshift(move);
            while (hysteresis.length > RubiksCubeScrambler.#RandomMoveHysteresisMaxLength) {
                hysteresis.pop();
            }
        }
        
        const modifierFactor = Math.random();
        if (modifierFactor < 0.33333) {
            move = "2" + move;
        } else if (modifierFactor < 0.666666) {
            move = move + "'";
        }

        return move;
    }

    #removeAllMoves() {
        const element = this.#movesListElement;
        while (element.hasChildNodes()) {
            element.removeChild(element.lastChild);
        }
    }

    // MARK: Custom Element

    connectedCallback() {
        let template = document.getElementById("rubiks-cube-scrambler-template");
        console.assert(template, "Couldn't find RubiksCubeScrambler component template in the document");

        const shadowRoot = this.#shadowRoot;
        shadowRoot.appendChild(template.content.cloneNode(true));

        this.#movesListElement = shadowRoot.querySelector(".scrambler__move-list");

        shadowRoot
            .querySelector("button[name='scramble']")
            .addEventListener("click", () => this.scramble());

        const patternLengthInputElement = shadowRoot.querySelector(".scrambler__pattern-length > input");
        patternLengthInputElement.value = this.#numberOfMovesToGenerate;
        patternLengthInputElement.addEventListener("input", event => {
            try {
                const integerValue = parseInt(event.target.value);
                this.#numberOfMovesToGenerate = integerValue;
            } catch (e) {
                console.error("Non-integer value of pattern length field", e);
            }
        });

        this.scramble();
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.debug("RubiksCubeScrambler attribute changed", name, oldValue, newValue);
        if (name === "count") {
            try {
                let newIntValue = parseInt(newValue);
                this.#numberOfMovesToGenerate = newIntValue;
            } catch (e) {
                console.error("`count` attribute should have an integer value.", e);
            }
        }
    }
}

window.customElements.define("rubiks-cube-scrambler", RubiksCubeScrambler);
