"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const Validate_1 = __importDefault(require("../Validate/Validate"));
/**
 * The `sos.input` API groups together all input-related functionality, such as remote control key events. With this API, you can
 * listen to key events from the remote control and handle them in your application.
 */
class Input {
    window;
    messagePrefix;
    static MESSAGE_PREFIX = 'input';
    eventEmitter = new events_1.EventEmitter();
    hasWindowEventListener = false;
    alive = true;
    /** @internal */
    constructor(window, messagePrefix) {
        this.window = window;
        this.messagePrefix = messagePrefix;
        if (!this.isTopLevelFrame() && this.canAccessParentOrigin()) {
            this.hasWindowEventListener = true;
            this.window.addEventListener('keyup', this.dispatchEventToParent);
        }
    }
    /** @internal */
    destroy() {
        if (this.alive) {
            this.alive = false;
            this.removeEventListeners();
            if (this.hasWindowEventListener) {
                this.window.removeEventListener('keyup', this.dispatchEventToParent);
            }
        }
    }
    /**
     * The `onKeyUp` method sets up a listeners, which is called on every keystroke of the remote controller. For the specific logic of an
     * application (games for example), binding the remote control inputs can be helpful. Only a subset of all remote control keys is
     * supported on the specific device. Only numerical digits are guaranteed to be supported.
     *
     * :::note
     * This feature must be tested by users on real devices.
     * :::
     *
     * @param listener The listener function that will be called on every key up event.
     * @returns {void} Resolves when the listener is successfully set up.
     * @throws {Error} If listener is not valid function.
     * @throws {Error} If listener is unregistered.
     * @since 1.3.0
     */
    onKeyUp(listener) {
        this.checkAlive();
        (0, Validate_1.default)({ listener }).required().function();
        this.eventEmitter.addListener('keyup', listener);
    }
    /** @internal */
    handleMessageData(data) {
        switch (data.type) {
            case this.getMessage('keyup'): {
                const keyupEvent = {
                    type: 'keyup',
                    keyCode: data.keyCode,
                    keyName: data.keyName,
                };
                this.emitKeyUpEvent(keyupEvent);
                break;
            }
            default:
        }
    }
    /**
     * The `removeEventListeners()` method removes all event listeners bind on `sos.input` object.
     */
    removeEventListeners() {
        this.eventEmitter.removeAllListeners();
    }
    /**
     * The `removeEventListener()` method removes all event listeners for a specific event (only `keyup` is supported).
     *
     * @param event The name of the event to remove the listener for. Currently, only `keyup` is supported.
     * @param listener The listener function to remove.
     * @returns {void} Resolves when the listener is successfully removed.
     */
    removeEventListener(event, listener) {
        this.eventEmitter.removeListener(event, listener);
    }
    /** @internal */
    emitKeyUpEvent(event) {
        this.checkAlive();
        this.eventEmitter.emit('keyup', event);
    }
    isTopLevelFrame() {
        return this.window === this.window.parent;
    }
    canAccessParentOrigin() {
        try {
            const hasParent = !!this.window?.parent?.document;
            if (!hasParent) {
                console.warn('Parent window or document is not defined');
            }
            return hasParent;
        }
        catch (e) {
            if (e instanceof DOMException) {
                // Blocked a frame with origin "..." from accessing a cross-origin frame.
                // This will happen on Android when applets have https://appassets.android.signageos.io origin
                // and top-level frame has file:// origin. Android doesn't benefit from this hack anyway.
                console.warn('Parent document is not accessible');
                return false;
            }
            else {
                throw e;
            }
        }
    }
    dispatchEventToParent = (event) => {
        try {
            this.window.parent.document.dispatchEvent(new KeyboardEvent('keyup', event));
        }
        catch (e) {
            if (e instanceof ReferenceError) {
                // KeyboardEvent constructor isn't available during tests.
                // TODO Mock KeyboardEvent constructor during tests.
                this.window.parent.document.dispatchEvent(event);
            }
            else {
                throw e;
            }
        }
    };
    getMessage(name) {
        return this.messagePrefix + '.' + Input.MESSAGE_PREFIX + '.' + name;
    }
    checkAlive() {
        if (!this.alive) {
            throw new Error('destroyed');
        }
    }
}
exports.default = Input;
//# sourceMappingURL=Input.js.map