"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReactPlayerBlock = void 0;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const React = tslib_1.__importStar(require("react"));
const icons_1 = require("@gravity-ui/icons");
const uikit_1 = require("@gravity-ui/uikit");
const debounce_1 = tslib_1.__importDefault(require("lodash/debounce.js"));
const react_player_1 = tslib_1.__importDefault(require("react-player"));
const mobileContext_1 = require("../../gravity-blocks/context/mobileContext/index.js");
const videoContext_1 = require("../../gravity-blocks/context/videoContext/index.js");
const hooks_1 = require("../../gravity-blocks/hooks/index.js");
const models_1 = require("../../models/index.js");
const utils_1 = require("../../utils/index.js");
const CustomBarControls_1 = tslib_1.__importDefault(require("./CustomBarControls.js"));
const i18n_1 = require("./i18n/index.js");
const utils_2 = require("./utils.js");
const youtube_1 = require("./utils/youtube.js");
const b = (0, utils_1.block)('ReactPlayer');
const FPS = 60;
const ReactPlayer = 'default' in react_player_1.default && react_player_1.default.default
    ? react_player_1.default.default
    : react_player_1.default;
// eslint-disable-next-line react/display-name
exports.ReactPlayerBlock = React.forwardRef((props, originRef) => {
    const isMobile = React.useContext(mobileContext_1.MobileContext);
    const { src, previewImgUrl, loop = false, controls = models_1.MediaVideoControlsType.Default, customControlsOptions = {}, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, onIntrinsicSizeChange, disableAutoSizing, analyticsEvents, height, ariaLabel, ratio, autoRatio, contain = true, } = props;
    const { type = models_1.PlayButtonType.Default, theme = models_1.PlayButtonThemes.Blue, text, className: buttonClassName, } = playButton || {};
    const { type: customControlsType = models_1.CustomControlsType.WithMuteButton, muteButtonShown, positioning = models_1.CustomControlsButtonPositioning.Center, } = customControlsOptions;
    const autoPlay = Boolean(!isMobile && !previewImgUrl && props.autoplay);
    const mute = initiallyMuted || autoPlay;
    const { playingVideoRef, setProps } = React.useContext(videoContext_1.VideoContext);
    const ref = React.useRef(null);
    const buttonRef = React.useRef(null);
    const [playerRef, setPlayerRef] = React.useState();
    const [isPlaying, setIsPlaying] = React.useState(autoPlay);
    const [playedPercent, setPlayedPercent] = React.useState(0);
    const [currentHeight, setCurrentHeight] = React.useState(height);
    const [duration, setDuration] = React.useState(null);
    const [width, setWidth] = React.useState(0);
    const [actualRatio, setActualRatio] = React.useState();
    const [muted, setMuted] = React.useState(mute);
    const [started, setStarted] = React.useState(autoPlay);
    const [ended, setEnded] = React.useState(false);
    const [isMounted, setIsMounted] = React.useState(false);
    const [hovered, setHovered] = React.useState(isMobile);
    (0, hooks_1.useMount)(() => setIsMounted(true));
    const videoSrc = React.useMemo(() => (0, utils_2.checkYoutubeVideos)(src), [src]);
    const { imageRef: previewImgRef, onLoad: onPreviewImgLoad } = (0, hooks_1.useImageSize)({
        onIntrinsicSizeChange,
    });
    const eventsArray = React.useMemo(() => {
        if (analyticsEvents) {
            return Array.isArray(analyticsEvents) ? analyticsEvents : [analyticsEvents];
        }
        return [];
    }, [analyticsEvents]);
    const handleAnalytics = (0, hooks_1.useAnalytics)(models_1.DefaultEventNames.ReactPlayerControls);
    React.useImperativeHandle(originRef, () => {
        if (!playerRef) {
            return;
        }
        let play, pause, addEventListener;
        const videoInstance = playerRef.getInternalPlayer();
        if ((0, youtube_1.isYoutubePlayerInstance)(videoInstance)) {
            ({ pauseVideo: pause, playVideo: play, addEventListener } = videoInstance);
        }
        else {
            // it is assumed that `videoInstance` is HTMLVideoElement by default
            ({ play, pause, addEventListener } = videoInstance);
        }
        // eslint-disable-next-line consistent-return
        return {
            play: play.bind(videoInstance),
            pause: pause.bind(videoInstance),
            addEventListener: addEventListener.bind(videoInstance),
        };
    }, [playerRef]);
    React.useEffect(() => {
        if (ref.current && !playingVideoRef?.contains(ref.current)) {
            setMuted(true);
        }
    }, [playingVideoRef]);
    React.useEffect(() => {
        if (showPreview) {
            playerRef?.showPreview();
        }
    }, [showPreview, playerRef]);
    React.useEffect(() => {
        if (playerRef && !started) {
            setIsPlaying(autoPlay);
        }
    }, [autoPlay, playerRef, started]);
    React.useEffect(() => setMuted(mute), [mute]);
    React.useEffect(() => {
        if (!started && isPlaying) {
            setStarted(true);
        }
    }, [isPlaying, started]);
    React.useEffect(() => {
        if (started && !Number.isNaN(Number(elapsedTime))) {
            playerRef?.seekTo(elapsedTime ?? 0, 'seconds');
        }
    }, [elapsedTime, playerRef, started]);
    React.useEffect(() => {
        if (disableAutoSizing) {
            return;
        }
        const updateSize = (0, debounce_1.default)(() => {
            if (ref.current) {
                // We need to get parent's width does not equal 0
                const parentElement = getParentElement(ref.current);
                const { paddingLeft, paddingRight } = getComputedStyle(parentElement);
                const newWidth = parentElement.offsetWidth -
                    parseFloat(paddingLeft) -
                    parseFloat(paddingRight);
                setWidth(newWidth);
                setCurrentHeight(Math.floor(getHeight(newWidth, ratio ?? (autoRatio ? actualRatio : undefined))));
            }
        }, 200);
        updateSize();
        window.addEventListener('resize', updateSize, { passive: true });
        // eslint-disable-next-line consistent-return
        return () => {
            window.removeEventListener('resize', updateSize);
        };
    }, [actualRatio, autoRatio, disableAutoSizing, playerRef, ratio]);
    const playEvents = React.useMemo(() => eventsArray?.filter((e) => e.type === models_1.PredefinedEventTypes.Play), [eventsArray]);
    const stopEvents = React.useMemo(() => eventsArray?.filter((e) => e.type === models_1.PredefinedEventTypes.Stop), [eventsArray]);
    const playIcon = React.useMemo(() => {
        let playButtonContent;
        switch (type) {
            case models_1.PlayButtonType.Text:
                playButtonContent = text;
                break;
            case models_1.PlayButtonType.Default:
            default:
                playButtonContent = (0, jsx_runtime_1.jsx)(uikit_1.Icon, { className: b('icon'), data: icons_1.PlayFill, size: 24 });
                break;
        }
        return ((0, jsx_runtime_1.jsx)("button", { className: b('button', { theme, text: Boolean(text) }, buttonClassName), "aria-label": (0, i18n_1.i18n)('play'), ref: buttonRef, children: playButtonContent }));
    }, [type, theme, text, buttonClassName]);
    const changeMute = React.useCallback((isMuted) => {
        if (isMuted &&
            playerRef &&
            controls === models_1.MediaVideoControlsType.Custom &&
            customControlsType === models_1.CustomControlsType.WithMuteButton) {
            playerRef.seekTo(0);
            setPlayedPercent(0);
        }
        const events = isMuted ? playEvents : stopEvents;
        handleAnalytics(events);
        if (isMuted) {
            setProps({ playingVideoRef: ref.current });
        }
        // In order to the progress bar to update (equals 0) before displaying
        setTimeout(() => setMuted(!isMuted), 0);
    }, [
        playerRef,
        customControlsType,
        playEvents,
        stopEvents,
        handleAnalytics,
        setProps,
        controls,
    ]);
    const handleClickPreview = React.useCallback(() => {
        setIsPlaying(true);
        onClickPreview?.();
        handleAnalytics(playEvents);
    }, [onClickPreview, handleAnalytics, playEvents]);
    const onPause = React.useCallback(() => {
        // For support correct state for youtube
        if (controls !== models_1.MediaVideoControlsType.Custom ||
            customControlsType !== models_1.CustomControlsType.WithMuteButton) {
            setIsPlaying(false);
        }
    }, [controls, customControlsType]);
    const onStart = React.useCallback(() => {
        if (!autoPlay && !initiallyMuted) {
            setMuted(false);
        }
    }, [autoPlay, initiallyMuted]);
    const onPlay = React.useCallback(() => {
        setIsPlaying(true);
        if (controls === models_1.MediaVideoControlsType.Custom &&
            customControlsType === models_1.CustomControlsType.WithMuteButton) {
            if (ended) {
                changeMute(false);
            }
            else if (!isPlaying) {
                changeMute(muted);
            }
            setEnded(false);
        }
    }, [changeMute, controls, customControlsType, ended, isPlaying, muted]);
    const onReady = React.useCallback((player) => {
        setPlayerRef(player);
        const videoElement = player.getInternalPlayer();
        const videoWidth = videoElement.videoWidth;
        const videoHeight = videoElement.videoHeight;
        if (videoWidth && videoHeight) {
            setActualRatio(videoHeight / videoWidth);
            onIntrinsicSizeChange?.({ width: videoWidth, height: videoHeight });
        }
    }, [onIntrinsicSizeChange]);
    const onProgress = React.useCallback(({ played, playedSeconds }) => {
        setPlayedPercent(played);
        if (loop) {
            const { start = 0, end = duration } = typeof loop === 'boolean' ? {} : loop;
            // Youtube videos not muted after finishing playing and start again.
            // 'onEnded' does not fire when 'loop' is set to true.
            // It is custom loop with muted sound after finishing playing and start again.
            if (end !== null && playedSeconds >= end) {
                setIsPlaying(true);
                playerRef?.seekTo(start);
            }
        }
        if (played === 1) {
            setMuted(true);
        }
    }, [duration, loop, playerRef]);
    const onDuration = React.useCallback((currentDuration) => {
        setDuration(currentDuration);
    }, []);
    const onEnded = React.useCallback(() => {
        setEnded(true);
    }, []);
    const onPlayClick = React.useCallback(() => {
        if (isPlaying) {
            onPause();
        }
        else {
            onPlay();
        }
    }, [isPlaying, onPlay, onPause]);
    const handleClick = React.useCallback(() => {
        buttonRef.current?.click();
        if (controls === models_1.MediaVideoControlsType.Custom) {
            if (customControlsType === models_1.CustomControlsType.WithMuteButton) {
                changeMute(muted);
            }
            else {
                onPlayClick();
            }
        }
    }, [controls, customControlsType, changeMute, muted, onPlayClick]);
    const handleKeyDown = React.useCallback((e) => {
        const key = e.key.toLowerCase();
        if (key === 'enter') {
            buttonRef.current?.click();
        }
    }, []);
    const onFocusIn = React.useCallback(() => setHovered(true), []);
    const onFocusOut = React.useCallback(() => setHovered(false), []);
    return ((0, jsx_runtime_1.jsx)("div", { className: b({
            wrapper: !currentHeight,
            controls,
            contain,
            'auto-ratio': autoRatio,
        }, className), ref: ref, onClick: handleClick, onMouseEnter: onFocusIn, onMouseLeave: onFocusOut, onFocus: onFocusIn, onBlur: onFocusOut, onKeyDown: handleKeyDown, role: "button", tabIndex: 0, children: isMounted ? ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [(0, jsx_runtime_1.jsx)(ReactPlayer, { className: b('player'), url: videoSrc, muted: muted, controls: controls === models_1.MediaVideoControlsType.Default, height: currentHeight || '100%', width: width || '100%', light: previewImgUrl && ((0, jsx_runtime_1.jsx)("img", { className: b('preview-img'), src: previewImgUrl, onLoad: onPreviewImgLoad, alt: "", "aria-hidden": true, ref: previewImgRef })), playing: isPlaying, playIcon: playIcon, progressInterval: FPS, onClickPreview: handleClickPreview, onStart: onStart, onReady: onReady, onPlay: onPlay, onPause: autoPlay && customControlsType !== models_1.CustomControlsType.WithMuteButton
                        ? undefined
                        : onPause, onProgress: onProgress, onEnded: onEnded, onDuration: onDuration, "aria-label": ariaLabel, previewTabIndex: -1, config: {
                        file: {
                            attributes: {
                                pip: isMobile ? 'false' : undefined,
                                playsinline: isMobile ? '' : undefined,
                                disablepictureinpicture: isMobile ? '' : undefined,
                            },
                        },
                    } }), controls === models_1.MediaVideoControlsType.Custom && ((0, jsx_runtime_1.jsx)(CustomBarControls_1.default, { className: customBarControlsClassName, mute: {
                        isMuted: muted,
                        changeMute: (event) => {
                            event.stopPropagation();
                            changeMute(muted);
                        },
                    }, elapsedTimePercent: playedPercent, type: customControlsType, isPaused: !isPlaying, onPlayClick: onPlayClick, muteButtonShown: muteButtonShown, shown: hovered && ((!started && !previewImgUrl) || started), positioning: positioning }))] })) : null }));
});
function getHeight(width, ratio = 9 / 16) {
    return width * ratio;
}
function getParentElement(element) {
    const parentElement = element.parentElement;
    if (!parentElement) {
        return element;
    }
    return parentElement.offsetWidth ? parentElement : getParentElement(parentElement);
}
exports.default = exports.ReactPlayerBlock;
//# sourceMappingURL=ReactPlayer.js.map
