import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useEffect, useRef, } from "react";
import { throttle } from "lodash";
import Styles from "./Parallax.module.scss";
import { flattenMatrix, generateMatrixFromOperations, invertMatrix, } from "./math/helpers";
import { calculateDistanceRelativeToBounds, givenTheRelativePositionHowMuchToRotate } from "../../hooks/ui/useTilt";
const MAXIMUM_PARALLAX = {
    x: 20,
    y: 20,
};
function _Parallax({ children, className = "", active, onClick, reflection, maxParallax = MAXIMUM_PARALLAX, ...props }, ref) {
    const parallaxableRef = useRef(null);
    const reflectionRef = useRef(null);
    const multiplierRef = useRef(1);
    const initialPositionRef = useRef(undefined);
    useEffect(() => {
        if (ref)
            ref.current =
                parallaxableRef.current;
        const el = parallaxableRef.current;
        if (active && !initialPositionRef.current) {
            initialPositionRef.current = getComputedStyle(el).transform;
            if (initialPositionRef.current === "none")
                initialPositionRef.current = "";
            el.style.transform = `${initialPositionRef.current} rotateX(0deg) rotateY(0deg) translateZ(0px)`;
        }
        // eslint-disable-next-line
    }, [active]);
    useEffect(() => {
        const el = parallaxableRef.current;
        if (active) {
            const updatePositions = (relativeToX, relativeToY) => {
                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, true);
                const distanceOffBottom = calculateDistanceRelativeToBounds(relativeToY, y0, yH, true);
                const howMuchToRotateY = givenTheRelativePositionHowMuchToRotate(distanceOffRight, maxParallax.y) * multiplierRef.current;
                const howMuchToRotateX = givenTheRelativePositionHowMuchToRotate(distanceOffBottom, maxParallax.x) * multiplierRef.current;
                const howMuchToOpacify = Math.abs(Math.abs(distanceOffBottom - 0.5) / 0.5);
                const howMuchToOpacifyHorizontal = Math.max(Math.abs(Math.abs(distanceOffRight - 0.5) / 0.5), Math.abs(Math.abs(distanceOffBottom - 0.5) / 0.5));
                el.style.transform = `${initialPositionRef.current} rotateY(${howMuchToRotateY}deg) rotateX(${-howMuchToRotateX}deg) translateZ(${multiplierRef.current === 1 ? 0 : -30}px)`;
                if (reflectionRef.current) {
                    reflectionRef.current.style.left = `${(1 - distanceOffRight) * 100}%`;
                    reflectionRef.current.style.top = `${(1 - distanceOffBottom) * 100}%`;
                    reflectionRef.current.style.opacity = `${Math.min(howMuchToOpacify, howMuchToOpacifyHorizontal)}`;
                }
            };
            const handler = throttle(({ x, y }) => {
                updatePositions(x, y);
            }, 1000 / 30);
            const touchHandler = throttle(({ touches }) => {
                const { pageX, pageY } = touches[0];
                updatePositions(pageX, pageY);
            }, 1000 / 30);
            const mouseDownHandler = () => {
                multiplierRef.current = 1.5;
            };
            const mouseUpHandler = () => {
                multiplierRef.current = 1;
            };
            window.addEventListener("mousemove", handler);
            window.addEventListener("touchmove", touchHandler);
            let _inverseMatrix;
            const _limits = {
                beta: undefined,
                gamma: undefined,
            };
            const orientationListener = ({ beta, gamma, }) => {
                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) {
                    _inverseMatrix = flattenMatrix(invertMatrix(generateMatrixFromOperations({
                        type: "rotateY",
                        angle: _gamma,
                    }, {
                        type: "rotateX",
                        angle: -_beta,
                    })));
                }
                el.style.transform = `matrix3d(${_inverseMatrix.join(",")}) rotateX(${-_beta}deg) rotateY(${_gamma}deg)`;
            };
            window.addEventListener("deviceorientation", orientationListener);
            window.addEventListener("touchend", () => {
                _inverseMatrix = undefined;
            });
            el.addEventListener("touchstart", mouseDownHandler);
            el.addEventListener("touchend", mouseUpHandler);
            el.addEventListener("mousedown", mouseDownHandler);
            el.addEventListener("mouseup", mouseUpHandler);
            return () => {
                window.removeEventListener("deviceorientation", orientationListener);
                window.removeEventListener("mousemove", handler);
                window.removeEventListener("touchmove", touchHandler);
                el.removeEventListener("mousedown", mouseDownHandler);
                el.removeEventListener("mouseup", mouseUpHandler);
                el.removeEventListener("touchstart", mouseDownHandler);
                el.removeEventListener("touchend", mouseUpHandler);
                // el.style.transform = initialPositionRef.current!;
                el.style.transform = `rotateX(0deg) rotateY(0deg) translateZ(0px)`;
            };
        }
    }, [active]);
    return (_jsxs("div", { onClick: onClick, ref: parallaxableRef, className: `${Styles.parallax} ${className}`, ...props, children: [children, reflection && _jsx("div", { ref: reflectionRef, className: Styles.reflection })] }));
}
/**
 * It holds a children and animates a tilt effect relative to mouse position
 **/
/**
 * A component that creates a 3D tilt effect on its content based on where you move your mouse or touch the screen.
 * When active, the element tilts and responds to your cursor position, creating an interactive depth effect.
 **/
const Parallax = forwardRef(_Parallax);
export default Parallax;
//# sourceMappingURL=Parallax.js.map