// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { DirectionalHint, Icon, IconButton, mergeStyles, Persona, Stack, Text } from '@fluentui/react';
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useIdentifiers } from '../identifiers';
import { useLocale } from '../localization';
import { useTheme } from '../theming';
import { RaisedHandIcon } from './assets/RaisedHandIcon';
import { disabledVideoHint, displayNameStyle, iconContainerStyle, overlayContainerStyles, rootStyles, videoContainerStyles, tileInfoContainerStyle, participantStateStringStyles, videoTileHighContrastStyles, iconsGroupContainerStyle } from './styles/VideoTile.styles';
import { pinIconStyle } from './styles/VideoTile.styles';
import useLongPress from './utils/useLongPress';
import { moreButtonStyles } from './styles/VideoTile.styles';
import { raiseHandContainerStyles } from './styles/VideoTile.styles';
import { formatMoreButtonAriaDescription } from './utils';
// Coin max size is set to PersonaSize.size100
const DEFAULT_PERSONA_MAX_SIZE_PX = 100;
// Coin min size is set PersonaSize.size32
const DEFAULT_PERSONA_MIN_SIZE_PX = 32;
const DefaultPlaceholder = (props) => {
    const { text, noVideoAvailableAriaLabel, coinSize, hidePersonaDetails } = props;
    return React.createElement(Stack, { className: mergeStyles({
            position: 'absolute',
            height: '100%',
            width: '100%'
        }) },
        React.createElement(Stack, { styles: defaultPersonaStyles }, coinSize && React.createElement(Persona, { coinSize: coinSize, hidePersonaDetails: hidePersonaDetails, text: text !== null && text !== void 0 ? text : '', initialsTextColor: "white", "aria-label": noVideoAvailableAriaLabel !== null && noVideoAvailableAriaLabel !== void 0 ? noVideoAvailableAriaLabel : '', showOverflowTooltip: false })));
};
const defaultPersonaStyles = {
    root: {
        margin: 'auto',
        maxHeight: '100%'
    }
};
const videoTileMoreMenuIconProps = {
    iconName: undefined,
    style: {
        display: 'none'
    }
};
const videoTileMoreMenuProps = {
    directionalHint: DirectionalHint.topLeftEdge,
    isBeakVisible: false,
    styles: {
        container: {
            maxWidth: '8rem'
        }
    }
};
const VideoTileMoreOptionsButton = (props) => {
    const locale = useLocale().strings.videoTile;
    const theme = useTheme();
    const { contextualMenu, canShowContextMenuButton, participantDisplayName, participantHandRaised, participantIsSpeaking, participantState, participantIsMuted, isMicDisabled, isCameraDisabled } = props;
    const [moreButtonAiraDescription, setMoreButtonAriaDescription] = useState('');
    useEffect(() => {
        setMoreButtonAriaDescription(formatMoreButtonAriaDescription(participantDisplayName, participantIsMuted, participantHandRaised, participantState, participantIsSpeaking, locale, isMicDisabled, isCameraDisabled));
    }, [participantDisplayName, participantHandRaised, participantIsMuted, participantIsSpeaking, participantState, locale, isMicDisabled, isCameraDisabled]);
    if (!contextualMenu) {
        return React.createElement(React.Fragment, null);
    }
    const optionsIcon = canShowContextMenuButton ? 'VideoTileMoreOptions' : undefined;
    return React.createElement(IconButton, { "data-ui-id": "video-tile-more-options-button", ariaLabel: moreButtonAiraDescription, styles: moreButtonStyles(theme), menuIconProps: videoTileMoreMenuIconProps, menuProps: Object.assign(Object.assign({}, videoTileMoreMenuProps), contextualMenu), iconProps: {
            iconName: optionsIcon
        } });
};
/**
 * A component to render the video stream for a single call participant.
 *
 * Use with {@link GridLayout} in a {@link VideoGallery}.
 *
 * @public
 */
export const VideoTile = (props) => {
    const { children, displayName, initialsName, isMirrored, isMuted, isSpotlighted, isPinned, onRenderPlaceholder, renderElement, overlay: reactionOverlay, showLabel = true, showMuteIndicator = true, styles, userId, noVideoAvailableAriaLabel, isSpeaking, raisedHand, personaMinSize = DEFAULT_PERSONA_MIN_SIZE_PX, personaMaxSize = DEFAULT_PERSONA_MAX_SIZE_PX, contextualMenu, mediaAccess } = props;
    const [isHovered, setIsHovered] = useState(false);
    const [isFocused, setIsFocused] = useState(false);
    // need to set a default otherwise the resizeObserver will get stuck in an infinite loop.
    const [personaSize, setPersonaSize] = useState(1);
    const videoTileRef = useRef(null);
    const locale = useLocale();
    const theme = useTheme();
    const callingPalette = theme.callingPalette;
    const isVideoRendered = !!renderElement;
    const observer = useRef(new ResizeObserver((entries) => {
        if (!entries[0]) {
            return;
        }
        const { width, height } = entries[0].contentRect;
        const personaCalcSize = Math.min(width, height) / 3;
        // we only want to set the persona size if it has changed
        if (personaCalcSize !== personaSize) {
            setPersonaSize(Math.max(Math.min(personaCalcSize, personaMaxSize), personaMinSize));
        }
    }));
    useLayoutEffect(() => {
        if (videoTileRef.current) {
            observer.current.observe(videoTileRef.current);
        }
        const currentObserver = observer.current;
        return () => currentObserver.disconnect();
    }, [videoTileRef]);
    // TODO: Remove after calling sdk fix the keybaord focus
    useEffect(() => {
        var _a;
        // PPTLive stream id is null
        if ((_a = videoTileRef.current) === null || _a === void 0 ? void 0 : _a.id) {
            return;
        }
        let observer;
        if (videoTileRef.current) {
            observer = new MutationObserver(mutationsList => {
                for (const mutation of mutationsList) {
                    if (mutation.type === 'childList') {
                        const iframe = document.querySelector('iframe');
                        if (iframe) {
                            if (!iframe.getAttribute('tabIndex')) {
                                iframe.setAttribute('tabIndex', '-1');
                            }
                        }
                    }
                }
            });
            observer.observe(videoTileRef.current, {
                childList: true,
                subtree: true
            });
        }
        return () => {
            observer === null || observer === void 0 ? void 0 : observer.disconnect();
        };
    }, [displayName, renderElement]);
    const useLongPressProps = useMemo(() => {
        return {
            onLongPress: () => {
                var _a;
                (_a = props.onLongTouch) === null || _a === void 0 ? void 0 : _a.call(props);
            },
            touchEventsOnly: true
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [props.onLongTouch]);
    const longPressHandlers = useLongPress(useLongPressProps);
    const hoverHandlers = useMemo(() => {
        return {
            onMouseEnter: () => setIsHovered(true),
            onMouseLeave: () => setIsHovered(false),
            onFocus: () => setIsFocused(true),
            onBlur: () => setIsFocused(false)
        };
    }, []);
    const placeholderOptions = {
        userId,
        text: initialsName !== null && initialsName !== void 0 ? initialsName : displayName,
        noVideoAvailableAriaLabel,
        coinSize: personaSize,
        styles: defaultPersonaStyles,
        hidePersonaDetails: true
    };
    const videoHintWithBorderRadius = mergeStyles(disabledVideoHint, {
        borderRadius: theme.effects.roundedCorner4,
        backgroundColor: callingPalette.videoTileLabelBackgroundLight
    });
    const tileInfoStyle = useMemo(() => mergeStyles(isVideoRendered || props.alwaysShowLabelBackground ? videoHintWithBorderRadius : disabledVideoHint, styles === null || styles === void 0 ? void 0 : styles.displayNameContainer), [isVideoRendered, videoHintWithBorderRadius, styles === null || styles === void 0 ? void 0 : styles.displayNameContainer, props.alwaysShowLabelBackground]);
    const ids = useIdentifiers();
    const canShowLabel = showLabel && (displayName || showMuteIndicator && isMuted);
    const participantStateString = getParticipantStateString(props, locale);
    const canShowContextMenuButton = isHovered || isFocused;
    let raisedHandBackgroundColor = '';
    raisedHandBackgroundColor = callingPalette.raiseHandGold;
    const participantMediaAccessIcons = useMemo(() => canShowLabel || participantStateString ? getMediaAccessIcons(showMuteIndicator, isMuted, mediaAccess) : undefined, [canShowLabel, isMuted, mediaAccess, participantStateString, showMuteIndicator]);
    const canShowParticipantIcons = participantMediaAccessIcons || isSpotlighted || isPinned;
    return React.createElement(Stack, Object.assign({ "data-ui-id": ids.videoTile, className: mergeStyles(rootStyles, {
            background: theme.palette.neutralLighter,
            borderRadius: theme.effects.roundedCorner4
        }, isSpeaking || raisedHand ? {
            '&::after': {
                content: `''`,
                position: 'absolute',
                border: `0.25rem solid ${isSpeaking ? theme.palette.themePrimary : raisedHandBackgroundColor}`,
                borderRadius: theme.effects.roundedCorner4,
                width: '100%',
                height: '100%',
                pointerEvents: 'none'
            }
        } : {}, videoTileHighContrastStyles(theme), styles === null || styles === void 0 ? void 0 : styles.root) }, longPressHandlers),
        React.createElement("div", Object.assign({ ref: videoTileRef, style: {
                width: '100%',
                height: '100%'
            } }, hoverHandlers, { "data-is-focusable": true }),
            isVideoRendered ? React.createElement(Stack, { className: mergeStyles(videoContainerStyles, isMirrored && {
                    transform: 'scaleX(-1)'
                }, styles === null || styles === void 0 ? void 0 : styles.videoContainer) }, renderElement) : React.createElement(Stack, { className: mergeStyles(videoContainerStyles, {
                    opacity: participantStateString || props.participantState === 'Idle' ? 0.4 : 1
                }) }, onRenderPlaceholder ? onRenderPlaceholder(userId !== null && userId !== void 0 ? userId : '', placeholderOptions, DefaultPlaceholder) : React.createElement(DefaultPlaceholder, Object.assign({}, placeholderOptions))),
            reactionOverlay,
            (canShowLabel || participantStateString) && React.createElement(Stack, { horizontal: true, className: tileInfoContainerStyle, tokens: tileInfoContainerTokens },
                React.createElement(Stack, { horizontal: true, className: tileInfoStyle },
                    canShowLabel && React.createElement(Text, { className: mergeStyles(displayNameStyle), title: displayName, style: {
                            color: participantStateString ? theme.palette.neutralSecondary : 'inherit'
                        }, "data-ui-id": "video-tile-display-name" }, displayName),
                    participantStateString && React.createElement(Text, { className: mergeStyles(participantStateStringStyles(theme)) }, bracketedParticipantString(participantStateString, !!canShowLabel)),
                    canShowParticipantIcons && React.createElement(Stack, { horizontal: true, className: mergeStyles(iconsGroupContainerStyle) },
                        participantMediaAccessIcons,
                        isSpotlighted && React.createElement(Stack, { className: mergeStyles(iconContainerStyle) },
                            React.createElement(Icon, { iconName: "VideoTileSpotlighted" })),
                        isPinned && React.createElement(Stack, { className: mergeStyles(iconContainerStyle) },
                            React.createElement(Icon, { iconName: "VideoTilePinned", className: mergeStyles(pinIconStyle) }))),
                    React.createElement(VideoTileMoreOptionsButton, { contextualMenu: contextualMenu, participantDisplayName: displayName, participantHandRaised: !!raisedHand, participantIsMuted: isMuted, participantState: participantStateString, participantIsSpeaking: isSpeaking, canShowContextMenuButton: canShowContextMenuButton, isMicDisabled: (mediaAccess === null || mediaAccess === void 0 ? void 0 : mediaAccess.isAudioPermitted) === false, isCameraDisabled: (mediaAccess === null || mediaAccess === void 0 ? void 0 : mediaAccess.isVideoPermitted) === false }))),
            children && React.createElement(Stack, { className: mergeStyles(overlayContainerStyles, styles === null || styles === void 0 ? void 0 : styles.overlayContainer) }, children),
            raisedHand && React.createElement(Stack, { horizontal: true, tokens: {
                    childrenGap: '0.2rem'
                }, className: raiseHandContainerStyles(theme, !canShowLabel) },
                React.createElement(Stack.Item, null,
                    React.createElement(Text, null, raisedHand.raisedHandOrderPosition)),
                React.createElement(Stack.Item, null,
                    React.createElement(RaisedHandIcon, null)))));
};
const getMediaAccessIcons = (showMuteIndicator, isMuted, mediaAccess) => {
    const cameraForbidIcon = mediaAccess && !(mediaAccess === null || mediaAccess === void 0 ? void 0 : mediaAccess.isVideoPermitted) ? React.createElement(Stack, { className: mergeStyles(iconContainerStyle) },
        React.createElement(Icon, { iconName: "ControlButtonCameraProhibitedSmall" })) : undefined;
    const micOffIcon = (mediaAccess ? mediaAccess.isAudioPermitted : true) && showMuteIndicator && isMuted ? React.createElement(Stack, { className: mergeStyles(iconContainerStyle) },
        React.createElement(Icon, { iconName: "VideoTileMicOff" })) : undefined;
    const micForbidIcon = mediaAccess && !(mediaAccess === null || mediaAccess === void 0 ? void 0 : mediaAccess.isAudioPermitted) && showMuteIndicator ? React.createElement(Stack, { className: mergeStyles(iconContainerStyle) },
        React.createElement(Icon, { iconName: "ControlButtonMicProhibitedSmall" })) : undefined;
    if (!(cameraForbidIcon || micOffIcon || micForbidIcon)) {
        return undefined;
    }
    return React.createElement(React.Fragment, null,
        cameraForbidIcon,
        micOffIcon,
        micForbidIcon);
};
const getParticipantStateString = (props, locale) => {
    const strings = Object.assign(Object.assign({}, locale.strings.videoTile), props.strings);
    return props.participantState === 'EarlyMedia' || props.participantState === 'Ringing' ? strings === null || strings === void 0 ? void 0 : strings.participantStateRinging : props.participantState === 'Hold' ? strings === null || strings === void 0 ? void 0 : strings.participantStateHold : undefined;
};
const tileInfoContainerTokens = {
    // A horizontal Stack sets the left margin to 0 for all it's children.
    // We need to allow the children to set their own margins
    childrenGap: 'none'
};
const bracketedParticipantString = (participantString, withBrackets) => {
    return withBrackets ? `(${participantString})` : participantString;
};
//# sourceMappingURL=VideoTile.js.map