// ============================================================================
// move.gl | Virtual Keyboard
// ============================================================================
// Copyright 2025 Scape Agency BV
// Licensed under MIT License
// ============================================================================

/**
 * Keyboard layout configuration
 */
export interface KeyboardLayout {
    [mode: string]: string[][];
}

/**
 * Virtual Keyboard Configuration Options
 */
export interface VirtualKeyboardOptions {
    /** Custom keyboard layout */
    layout?: KeyboardLayout;
    /** Callback when a key is pressed */
    onKeyPress?: (key: string) => void;
}

/**
 * Virtual Keyboard
 *
 * Manages the rendering and interaction of a virtual keyboard on the web.
 * Supports multiple layouts (default, shift, special) and handles both
 * mouse and keyboard inputs, including touch support.
 *
 * @example
 * ```typescript
 * const keyboard = new VirtualKeyboard('textInput', 'keyboard');
 * keyboard.switchMode('special');
 * ```
 */
export class VirtualKeyboard {

    private keys: { [mode: string]: string[][] } = {
        "default": [
            ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
            ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
            ["a", "s", "d", "f", "g", "h", "j", "k", "l"],
            ["z", "x", "c", "v", "b", "n", "m", "Backspace"]
        ],
        "shift": [
            ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
            ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
            ["A", "S", "D", "F", "G", "H", "J", "K", "L"],
            ["Z", "X", "C", "V", "B", "N", "M", "Backspace"]
        ],
        "special": [
            ["[", "]", "{", "}", "#", "%", "^", "*", "+", "="],
            ["_", "\\", "|", "~", "<", ">", "€", "£", "¥"],
            [".", ",", "?", "!", "'", '"', ":", ";", "Backspace"]
        ]
    };
    private currentMode = "default";
    private inputElement: HTMLInputElement;
    private keyboardElement: HTMLElement;

    /**
     * @notice Initializes the virtual keyboard with specific input and
     * keyboard element IDs.
     * @param inputId The ID of the HTML input element to which the keyboard
     * will be linked.
     * @param keyboardId The ID of the container element where the keyboard
     * will be rendered.
     */
    constructor(inputId: string, keyboardId: string) {
        this.inputElement = document.getElementById(
            inputId
        ) as HTMLInputElement;
        this.keyboardElement = document.getElementById(
            keyboardId
        ) as HTMLElement;
        this.renderKeyboard();
        this.attachEventListeners();
    }

    /**
     * @notice Renders the keyboard based on the current mode (default, shift,
     * or special).
     * @dev Dynamically creates HTML for keyboard keys and appends them to the
     * keyboardElement.
     */
    private renderKeyboard() {
        // Clear existing keys
        this.keyboardElement.innerHTML = "";
        this.keys[this.currentMode].forEach(row => {
            const rowElement = document.createElement("div");
            rowElement.className = "keyboard__row";
            row.forEach(key => {
                const keyElement = document.createElement("div");
                keyElement.textContent = key;
                // Assign a class for easier CSS styling
                keyElement.className = "key";
                keyElement.addEventListener(
                    "click", () => this.handleKeyPress(key)
                );
                rowElement.appendChild(keyElement);
            });
            this.keyboardElement.appendChild(rowElement);
        });
    }

    /**
     * @notice Handles key presses on the virtual keyboard.
     * @param key The key character or function (like "Backspace") that was
     * pressed.
     */
    private handleKeyPress(key: string) {
        if (key === "Backspace") {
            this.inputElement.value = this.inputElement.value.slice(0, -1);
        } else if (key === "Shift" || key === "CapsLock") {
            this.toggleShift();
        } else {
            this.inputElement.value += key;
        }
    }

    /**
     * @notice Toggles the keyboard between "default" and "shift" modes.
     * @dev This method is called when the "Shift" or "CapsLock" key is pressed.
     */
    private toggleShift() {
        this.currentMode = this.currentMode === "default" ? "shift" : "default";
        this.renderKeyboard();
    }

    /**
     * @notice Attaches necessary event listeners to handle both physical
     * keyboard and touch inputs.
     */
    private attachEventListeners() {
        document.addEventListener("keydown", this.handlePhysicalKeyPress);
        this.keyboardElement.addEventListener(
            "touchstart", this.handleTouchStart, false
        );
    }

    /**
     * @notice Handles physical keyboard events and maps them to virtual key
     * presses.
     * @param event The keyboard event captured from the user"s physical
     * keyboard.
     */
    private handlePhysicalKeyPress = (event: KeyboardEvent) => {
        const key = event.key;
        if (key === "Shift" || key === "CapsLock") {
            this.toggleShift();
            event.preventDefault();
        } else if (key === "Enter" || key === "Tab") {
            // Optional: Implement behavior for Enter and Tab if needed
        } else {
            this.handleKeyPress(key);
        }
    };

    /**
     * @notice Handles touch events on the keyboard element.
     * @param event The touch event on the virtual keyboard.
     */
    private handleTouchStart = (event: TouchEvent) => {
        event.preventDefault(); // Prevents emulating mouse events
        const keyElement = event.target as HTMLElement;
        if (keyElement.classList.contains("key")) {
            this.handleKeyPress(keyElement.textContent || "");
        }
    };

    /**
     * @notice Switches the keyboard layout to a specified mode.
     * @param mode The mode to which the keyboard layout should switch
     * ("default", "shift", or "special").
     */
    public switchMode(mode: string) {
        if (this.keys[mode]) {
            this.currentMode = mode;
            this.renderKeyboard();
        }
    }

    /**
     * Removes all event listeners and cleans up.
     */
    public destroy(): void {
        document.removeEventListener('keydown', this.handlePhysicalKeyPress);
        this.keyboardElement.removeEventListener('touchstart', this.handleTouchStart);
        this.keyboardElement.innerHTML = '';
    }
}

export default VirtualKeyboard;
