import React, { useEffect, useMemo, useState } from 'react';
import { getChildNodeFrom } from './get-child-node-from';
export const useHoverTransforms = (ref, disabled) => {
    const [state, setState] = React.useState({
        progress: 0,
        isActive: false,
    });
    const eventTarget = useMemo(() => new EventTarget(), []);
    useEffect(() => {
        if (disabled) {
            eventTarget.dispatchEvent(new Event('disabled'));
        }
        else {
            eventTarget.dispatchEvent(new Event('enabled'));
        }
    }, [disabled, eventTarget]);
    React.useEffect(() => {
        const element = ref.current;
        if (!element)
            return;
        let animationFrame;
        let start = null;
        let isHovered = false;
        let internalDisabled = disabled;
        const getIsActive = () => {
            return isHovered && !internalDisabled;
        };
        const animate = (timestamp) => {
            if (start === null)
                start = timestamp;
            const progress = Math.min((timestamp - start) / 150, 1);
            setState(() => ({
                isActive: getIsActive(),
                progress: getIsActive() ? progress : 1 - progress,
            }));
            if (progress < 1) {
                animationFrame = requestAnimationFrame(animate);
            }
        };
        const animateIn = () => {
            start = null;
            cancelAnimationFrame(animationFrame);
            animationFrame = requestAnimationFrame(animate);
        };
        const handleMouseEnter = () => {
            const prevIsActive = getIsActive();
            isHovered = true;
            if (prevIsActive === getIsActive()) {
                return;
            }
            animateIn();
        };
        const handleMouseLeave = () => {
            const prevIsActive = getIsActive();
            isHovered = false;
            if (prevIsActive === getIsActive()) {
                return;
            }
            animateIn();
        };
        const handleDisabled = () => {
            const prevIsActive = getIsActive();
            internalDisabled = true;
            if (prevIsActive === getIsActive()) {
                return;
            }
            animateIn();
        };
        const handleEnabled = () => {
            const prevIsActive = getIsActive();
            internalDisabled = false;
            if (prevIsActive === getIsActive()) {
                return;
            }
            animateIn();
        };
        element.addEventListener('mouseenter', handleMouseEnter);
        element.addEventListener('mouseleave', handleMouseLeave);
        eventTarget.addEventListener('disabled', handleDisabled);
        eventTarget.addEventListener('enabled', handleEnabled);
        return () => {
            element.removeEventListener('mouseenter', handleMouseEnter);
            element.removeEventListener('mouseleave', handleMouseLeave);
            eventTarget.removeEventListener('disabled', handleDisabled);
            eventTarget.removeEventListener('enabled', handleEnabled);
            cancelAnimationFrame(animationFrame);
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [ref, eventTarget]);
    return state;
};
export const useClickTransforms = (ref) => {
    const [hoverProgress, setHoverProgress] = React.useState(0);
    React.useEffect(() => {
        const element = getChildNodeFrom(ref.current);
        if (!element) {
            return;
        }
        let animationFrame;
        let start = null;
        let isHovered = false;
        const animate = (timestamp) => {
            if (start === null)
                start = timestamp;
            const progress = Math.min((timestamp - start) / 150, 1);
            setHoverProgress(isHovered ? progress : 1 - progress);
            if (progress < 1) {
                animationFrame = requestAnimationFrame(animate);
            }
        };
        const handleMouseEnter = () => {
            isHovered = true;
            start = null;
            cancelAnimationFrame(animationFrame);
            animationFrame = requestAnimationFrame(animate);
        };
        const handleMouseLeave = () => {
            isHovered = false;
            start = null;
            cancelAnimationFrame(animationFrame);
            animationFrame = requestAnimationFrame(animate);
        };
        element.addEventListener('pointerdown', handleMouseEnter);
        element.addEventListener('pointerup', handleMouseLeave);
        return () => {
            element.removeEventListener('pointerdown', handleMouseEnter);
            element.removeEventListener('pointerup', handleMouseLeave);
            cancelAnimationFrame(animationFrame);
        };
    }, [ref]);
    return hoverProgress;
};
const getAngle = (ref, coordinates) => {
    const element = getChildNodeFrom(ref);
    if (!element) {
        return 0;
    }
    if (coordinates === null) {
        return 0;
    }
    const clientRect = element.getClientRects();
    if (!clientRect) {
        return 0;
    }
    if (clientRect.length === 0) {
        return 0;
    }
    const center = {
        x: clientRect[0].x + clientRect[0].width / 2,
        y: clientRect[0].y + clientRect[0].height / 2,
    };
    const angle = Math.atan2(coordinates.y - center.y, coordinates.x - center.x);
    return angle;
};
let lastCoordinates = null;
export const useMousePosition = (ref) => {
    const [angle, setAngle] = useState(getAngle(ref.current, lastCoordinates));
    useEffect(() => {
        const element = ref.current;
        if (!element) {
            return;
        }
        const onMouseMove = (e) => {
            const clientRect = element.getClientRects();
            if (!clientRect) {
                return;
            }
            lastCoordinates = { y: e.clientY, x: e.clientX };
            setAngle(getAngle(element, { y: e.clientY, x: e.clientX }));
        };
        window.addEventListener('mousemove', onMouseMove);
        return () => {
            window.removeEventListener('mousemove', onMouseMove);
        };
    }, [ref]);
    return angle;
};
