"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBoundingRectFromElement = exports.getElementInDirection = void 0;
var types_1 = require("./types");
/**
 * Calculates the length of a vector.
 *
 * @param vector The vector to calculate the length of
 */
function length(vector) {
    return Math.sqrt(Math.pow(vector.x, 2) + Math.pow(vector.y, 2));
}
/**
 * Normalizes the given vector.
 *
 * @param vector The vector to normalize
 */
function normalize(vector) {
    var len = length(vector);
    return {
        x: vector.x / len,
        y: vector.y / len,
    };
}
/**
 * Calculates the dot product between 2 vectors.
 *
 * @param a The first vector
 * @param b The second vector
 */
function dotProduct(a, b) {
    return a.x * b.x + a.y * b.y;
}
/**
 * Calculates the distance between the 2 points pointed to by the provided vectors.
 *
 * @param a The first vector
 * @param b The second vector
 */
function distance(a, b) {
    return length({
        x: b.x - a.x,
        y: b.y - a.y,
    });
}
/**
 * Returns a vector that corresponds to the center of the provided element.
 *
 * @param element The element to get the center of
 */
function getElementVector(element) {
    var boundingRect = getBoundingRectFromElement(element);
    return {
        x: boundingRect.x + boundingRect.width / 2,
        y: boundingRect.y + boundingRect.height / 2,
    };
}
/**
 * Returns the angle in degrees between the unit vector pointing in the given {Direction} and the unit vector that
 * points from the current element to another element.
 *
 * @param a The vector of the current element
 * @param b The vector of the other element
 * @param direction The direction to move along
 */
function calculateAngle(a, b, direction) {
    var directionVector = {
        x: (direction === types_1.Direction.LEFT ? -1 : direction === types_1.Direction.RIGHT ? 1 : 0),
        y: (direction === types_1.Direction.UP ? -1 : direction === types_1.Direction.DOWN ? 1 : 0),
    };
    var elementVector = normalize({
        x: b.x - a.x,
        y: b.y - a.y,
    });
    var angleCos = dotProduct(directionVector, elementVector) / (length(directionVector) * length(elementVector));
    return Math.acos(angleCos) * 180 / Math.PI;
}
/**
 * Returns the closest element to the current element when trying to navigate in the provided direction. Returns
 * undefined, if there is not element in the given direction.
 *
 * @param activeElement The currently selected element
 * @param elements The list of all elements that can be navigated to
 * @param direction The direction in which to navigate
 */
function getElementInDirection(activeElement, elements, direction) {
    var _a;
    if (!activeElement)
        return undefined;
    var cutoffAngle = 45;
    var activeElemVector = getElementVector(activeElement);
    return (_a = elements
        // don't take the current element into account
        .filter(function (elem) { return elem !== activeElement; })
        // get the angle between, and distance to any other element from the current element
        .map(function (element) {
        var elementVector = getElementVector(element);
        var dist = distance(activeElemVector, elementVector);
        var angle = calculateAngle(activeElemVector, elementVector, direction);
        return { angle: angle, dist: dist, element: element };
    })
        // filter out any elements that don't align with the direction we're trying to move in
        .filter(function (_a) {
        var angle = _a.angle;
        return angle <= cutoffAngle;
    })
        // sort the resulting elements based on their distance to the current element in ascending order
        .sort(function (_a, _b) {
        var angleA = _a.angle, distA = _a.dist;
        var angleB = _b.angle, distB = _b.dist;
        return (angleA - angleB) + (distA - distB);
    })
        // return the element closest to the current element
        .shift()) === null || _a === void 0 ? void 0 : _a.element;
}
exports.getElementInDirection = getElementInDirection;
/**
 * Returns DOMRect like object containing horizontal X and vertical Y coordinates from and HTMLElement.
 * Handles use-cases for getBoundingClientRect when the return type can be either
 * a ClientRect or DOMRect object type.
 *
 * @param element The currently selected element
 */
function getBoundingRectFromElement(element) {
    var boundingRect = element.getBoundingClientRect();
    if (typeof boundingRect.x !== 'number' && typeof boundingRect.y !== 'number') {
        boundingRect.x = boundingRect.left;
        boundingRect.y = boundingRect.top;
    }
    return boundingRect;
}
exports.getBoundingRectFromElement = getBoundingRectFromElement;
