import throttle from "lodash/throttle";
import { useEffect, useRef, useState } from "react";
import { flattenMatrix, generateMatrixFromOperations, IDENTITY_MATRIX, invertMatrix, } from "../../components/Parallax/math/helpers";
const MAX_TILT = {
    x: 100,
    y: 100,
};
export function useTiltUpdates(active, relativeToEl, onTilt, onInverseMatrix, sensorScale = 1, tiltLimit = MAX_TILT, options) {
    useEffect(() => {
        if (active) {
            const updatePositions = (relativeToX, relativeToY) => {
                const el = relativeToEl.current;
                const rect = el.getBoundingClientRect();
                const { width: [x0, xW], height: [y0, yH], } = {
                    width: [rect.left, el.clientWidth],
                    height: [rect.top, el.clientHeight],
                };
                const distanceOffRight = calculateDistanceRelativeToBounds(relativeToX, x0, xW, options?.round ?? true);
                const distanceOffBottom = calculateDistanceRelativeToBounds(relativeToY, y0, yH, options?.round ?? true);
                const howMuchToRotateY = givenTheRelativePositionHowMuchToRotate(distanceOffRight, tiltLimit.y);
                const howMuchToRotateX = givenTheRelativePositionHowMuchToRotate(distanceOffBottom, tiltLimit.x);
                onTilt({
                    y: howMuchToRotateX * 2,
                    x: howMuchToRotateY * 2,
                });
            };
            const handler = throttle(({ x, y }) => {
                updatePositions(x, y);
            }, 1000 / 30);
            const touchHandler = throttle(({ touches }) => {
                const { pageX, pageY } = touches[0];
                updatePositions(pageX, pageY);
            }, 1000 / 30);
            window.addEventListener("mousemove", handler);
            window.addEventListener("touchmove", touchHandler);
            let _inverseMatrix;
            const _limits = {
                beta: undefined,
                gamma: undefined,
            };
            const orientationListener = ({ beta, gamma, }) => {
                if ((beta ?? 0) + (gamma ?? 0) === 0)
                    return;
                beta = beta * 0.5;
                gamma = gamma * 0.5;
                if (!_inverseMatrix) {
                    window.removeEventListener("mousemove", handler);
                    window.removeEventListener("touchmove", touchHandler);
                    const offset = 12.5;
                    _limits.beta = [beta - offset, beta + offset];
                    _limits.gamma = [gamma - offset, gamma + offset];
                }
                const _beta = Math.min(_limits.beta[1], Math.max(_limits.beta[0], beta));
                const _gamma = Math.min(_limits.gamma[1], Math.max(_limits.gamma[0], gamma));
                if (!_inverseMatrix && onInverseMatrix) {
                    onInverseMatrix(() => {
                        return (_inverseMatrix = flattenMatrix(invertMatrix(generateMatrixFromOperations({
                            type: "rotateY",
                            angle: _gamma,
                        }, {
                            type: "rotateX",
                            angle: -_beta,
                        }))));
                    });
                }
                onTilt({
                    y: -_beta * sensorScale,
                    x: _gamma * sensorScale,
                });
            };
            function removeListeners() {
                window.removeEventListener("mousemove", handler);
                window.removeEventListener("touchmove", touchHandler);
            }
            if (options?.gyroEnabled ?? true) {
                window.addEventListener("deviceorientation", orientationListener);
                return () => {
                    window.removeEventListener("deviceorientation", orientationListener);
                    removeListeners();
                };
            }
            else {
                return () => removeListeners();
            }
        }
    }, [active, onInverseMatrix, onTilt]);
}
/**
 * This hook binds to two methods for calculating tilt
 *
 * When available: The device sensors
 * When on desktop: The mouse position relative to a ref
 */
export default function useTilt(active, 
/**
 * A scale to increase the values from the sensors
 *
 * @hack You can set this value to 0 as a way to disable the tilt calc on mobile
 */
sensorScale = 1, maxTilt = MAX_TILT, options) {
    const [tilt, setTilt] = useState({
        x: 0,
        y: 0,
    });
    const [tiltResetMatrix, setTiltResetMatrix] = useState(() => flattenMatrix(IDENTITY_MATRIX()));
    const relativeTo = useRef(null);
    useTiltUpdates(active, relativeTo, setTilt, setTiltResetMatrix, sensorScale, maxTilt, options);
    return {
        /** The element to monitor mouse hover relative to */
        relativeTo,
        /**
         * The matrix that represents the original tilt, so it can be used with the tilt prop to tilt relative to the user
         * Use it on css like so transform: matrix3d(tiltResetMatrix.join(",")) rotateX(tilt.x) rotateY(tilt.y)
         */
        tiltResetMatrix,
        /**
         * How much tilt should be applied based
         */
        tilt,
    };
}
export function calculateDistanceRelativeToBounds(mousePosition, elementInitialPosition, elementSizeDimension, shouldRound) {
    const normalizeInitialPosition = mousePosition - elementInitialPosition;
    const doNotAllowGoingBeyondLowerLimit = Math.max(normalizeInitialPosition, 0);
    const doNotAllowHoingBeyondHigherLimit = Math.min(doNotAllowGoingBeyondLowerLimit, elementSizeDimension);
    const threeRule = (doNotAllowHoingBeyondHigherLimit * 100) / elementSizeDimension;
    const result = (shouldRound ? Math.round(threeRule) : threeRule) / 100;
    return result;
}
export function givenTheRelativePositionHowMuchToRotate(relativePosition, maxRotation) {
    return relativePosition * maxRotation - maxRotation / 2;
}
//# sourceMappingURL=useTilt.js.map