// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { concatStyleSets, mergeStyles, Stack } from '@fluentui/react';
import React, { useCallback, useMemo, useRef } from 'react';
import { Announcer } from './Announcer';
import { useEffect } from 'react';
import { useLocale } from '../localization';
import { useTheme } from '../theming';
import { _RemoteVideoTile } from './RemoteVideoTile';
import { isNarrowWidth, _useContainerHeight, _useContainerWidth } from './utils/responsive';
import { LocalScreenShare } from './VideoGallery/LocalScreenShare';
import { RemoteScreenShare } from './VideoGallery/RemoteScreenShare';
import { _isIdentityMicrosoftTeamsUser } from "../../../acs-ui-common/src";
import { _LocalVideoTile } from './LocalVideoTile';
import { DefaultLayout } from './VideoGallery/DefaultLayout';
import { FloatingLocalVideoLayout } from './VideoGallery/FloatingLocalVideoLayout';
import { useIdentifiers } from '../identifiers';
import { localVideoTileContainerStyles, videoGalleryOuterDivStyle } from './styles/VideoGallery.styles';
import { floatingLocalVideoTileStyle } from './VideoGallery/styles/FloatingLocalVideo.styles';
import { useId } from '@fluentui/react-hooks';
import { SpeakerVideoLayout } from './VideoGallery/SpeakerVideoLayout';
import { FocusedContentLayout } from './VideoGallery/FocusContentLayout';
import { TogetherModeLayout } from './VideoGallery/TogetherModeLayout';
import { TogetherModeStream } from './VideoGallery/TogetherModeStream';
/**
 * @private
 * Currently the Calling JS SDK supports up to 4 remote video streams
 */
export const DEFAULT_MAX_REMOTE_VIDEO_STREAMS = 4;
/**
 * @private
 * Styles to disable the selectivity of a text in video gallery
 */
export const unselectable = {
    '-webkit-user-select': 'none',
    '-webkit-touch-callout': 'none',
    '-moz-user-select': 'none',
    '-ms-user-select': 'none',
    'user-select': 'none'
};
/**
 * @private
 * Set aside only 6 dominant speakers for remaining audio participants
 */
export const MAX_AUDIO_DOMINANT_SPEAKERS = 6;
/**
 * @private
 * Default remote video tile menu options
 */
export const DEFAULT_REMOTE_VIDEO_TILE_MENU_OPTIONS = {
    kind: 'contextual'
};
/**
 * @private
 * Maximum number of remote video tiles that can be pinned
 */
export const MAX_PINNED_REMOTE_VIDEO_TILES = 4;
/**
 * VideoGallery represents a layout of video tiles for a specific call.
 * It displays a {@link VideoTile} for the local user as well as for each remote participant who has joined the call.
 *
 * @public
 */
export const VideoGallery = (props) => {
    var _a, _b, _c, _d;
    const { localParticipant, remoteParticipants = [], localVideoViewOptions, remoteVideoViewOptions, dominantSpeakers, onRenderLocalVideoTile, onRenderRemoteVideoTile, onCreateLocalStreamView, onDisposeLocalStreamView, onCreateRemoteStreamView, onDisposeRemoteScreenShareStreamView, onDisposeLocalScreenShareStreamView, onDisposeRemoteVideoStreamView, styles, layout, onRenderAvatar, showMuteIndicator, maxRemoteVideoStreams = DEFAULT_MAX_REMOTE_VIDEO_STREAMS, showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps, onPinParticipant: onPinParticipantHandler, onUnpinParticipant: onUnpinParticipantHandler, remoteVideoTileMenu = DEFAULT_REMOTE_VIDEO_TILE_MENU_OPTIONS, overflowGalleryPosition = 'horizontalBottom', localVideoTileSize = 'followDeviceOrientation', spotlightedParticipants, onStartLocalSpotlight, onStartRemoteSpotlight, onStopLocalSpotlight, onStopRemoteSpotlight, maxParticipantsToSpotlight, reactionResources, videoTilesOptions, onMuteParticipant, startTogetherModeEnabled, isTogetherModeActive, onCreateTogetherModeStreamView, onStartTogetherMode, onSetTogetherModeSceneSize, togetherModeStreams, togetherModeSeatingCoordinates, onDisposeTogetherModeStreamView, onForbidAudio, onPermitAudio, onForbidVideo, onPermitVideo, localScreenShareView } = props;
    const ids = useIdentifiers();
    const theme = useTheme();
    const localeStrings = useLocale().strings.videoGallery;
    const strings = useMemo(() => (Object.assign(Object.assign({}, localeStrings), props.strings)), [localeStrings, props.strings]);
    const drawerMenuHostIdFromProp = remoteVideoTileMenu && remoteVideoTileMenu.kind === 'drawer' ? remoteVideoTileMenu.hostId : undefined;
    const drawerMenuHostId = useId('drawerMenuHost', drawerMenuHostIdFromProp);
    const localTileNotInGrid = (layout === 'floatingLocalVideo' || layout === 'speaker') && remoteParticipants.length > 0;
    const containerRef = useRef(null);
    const containerWidth = _useContainerWidth(containerRef);
    const containerHeight = _useContainerHeight(containerRef);
    const isNarrow = containerWidth ? isNarrowWidth(containerWidth) : false;
    const [pinnedParticipantsState, setPinnedParticipantsState] = React.useState([]);
    const [selectedScalingModeState, setselectedScalingModeState] = React.useState({});
    const onUpdateScalingMode = useCallback((remoteUserId, scalingMode) => {
        setselectedScalingModeState(current => (Object.assign(Object.assign({}, current), { [remoteUserId]: {
                scalingMode,
                isMirrored: remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.isMirrored
            } })));
    }, [remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.isMirrored]);
    useEffect(() => {
        var _a;
        (_a = props.pinnedParticipants) === null || _a === void 0 ? void 0 : _a.forEach(pinParticipant => {
            var _a;
            if (!((_a = props.remoteParticipants) === null || _a === void 0 ? void 0 : _a.find(t => t.userId === pinParticipant))) {
                // warning will be logged in the console when invalid participant id is passed in pinned participants
                console.warn('Invalid pinned participant UserId :' + pinParticipant);
            }
        });
    }, [props.pinnedParticipants, props.remoteParticipants]);
    // Use pinnedParticipants from props but if it is not defined use the maintained state of pinned participants
    const pinnedParticipants = useMemo(() => { var _a; return (_a = props.pinnedParticipants) !== null && _a !== void 0 ? _a : pinnedParticipantsState.filter(pinnedParticipantId => remoteParticipants.find(remoteParticipant => remoteParticipant.userId === pinnedParticipantId)); }, [props.pinnedParticipants, pinnedParticipantsState, remoteParticipants]);
    const showLocalVideoTileLabel = !(localTileNotInGrid && isNarrow && localVideoTileSize !== '16:9' || localVideoTileSize === '9:16') || layout === 'default';
    /**
     * Utility function for memoized rendering of LocalParticipant.
     */
    const localVideoTile = useMemo(() => {
        var _a, _b;
        if (localVideoTileSize === 'hidden') {
            return undefined;
        }
        if (onRenderLocalVideoTile) {
            return onRenderLocalVideoTile(localParticipant);
        }
        const isSpotlighted = !!localParticipant.spotlight;
        const localVideoTileStyles = concatStyleSets(localTileNotInGrid ? floatingLocalVideoTileStyle : {}, {
            root: {
                borderRadius: theme.effects.roundedCorner4
            }
        }, styles === null || styles === void 0 ? void 0 : styles.localVideo);
        const initialsName = !localParticipant.displayName ? '' : localParticipant.displayName;
        const showDisplayNameTrampoline = () => {
            return layout === 'default' ? strings.localVideoLabel : isNarrow && localVideoTileSize !== '16:9' ? '' : strings.localVideoLabel;
        };
        return React.createElement(Stack, { styles: localVideoTileContainerStyles, key: "local-video-tile-key" },
            React.createElement(_LocalVideoTile, { alwaysShowLabelBackground: videoTilesOptions === null || videoTilesOptions === void 0 ? void 0 : videoTilesOptions.alwaysShowLabelBackground, userId: localParticipant.userId, onCreateLocalStreamView: onCreateLocalStreamView, onDisposeLocalStreamView: onDisposeLocalStreamView, isAvailable: (_a = localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable, isMuted: localParticipant.isMuted, renderElement: (_b = localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.videoStream) === null || _b === void 0 ? void 0 : _b.renderElement, displayName: showDisplayNameTrampoline(), initialsName: initialsName, localVideoViewOptions: localVideoViewOptions, onRenderAvatar: onRenderAvatar, showLabel: showLocalVideoTileLabel, showMuteIndicator: showMuteIndicator, showCameraSwitcherInLocalPreview: showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps: localVideoCameraCycleButtonProps, localVideoCameraSwitcherLabel: strings.localVideoCameraSwitcherLabel, localVideoSelectedDescription: strings.localVideoSelectedDescription, styles: localVideoTileStyles, raisedHand: localParticipant.raisedHand, reaction: localParticipant.reaction, spotlightedParticipantUserIds: spotlightedParticipants, isSpotlighted: isSpotlighted, onStartSpotlight: onStartLocalSpotlight, onStopSpotlight: onStopLocalSpotlight, maxParticipantsToSpotlight: maxParticipantsToSpotlight, menuKind: remoteVideoTileMenu ? remoteVideoTileMenu.kind === 'drawer' ? 'drawer' : 'contextual' : undefined, drawerMenuHostId: drawerMenuHostId, strings: strings, reactionResources: reactionResources, participantsCount: remoteParticipants.length + 1, isScreenSharingOn: localParticipant.isScreenSharingOn, mediaAccess: localParticipant.mediaAccess }));
    }, [isNarrow, localParticipant, localVideoCameraCycleButtonProps, localVideoViewOptions, onCreateLocalStreamView, onDisposeLocalStreamView, onRenderAvatar, onRenderLocalVideoTile, localTileNotInGrid, showCameraSwitcherInLocalPreview, showMuteIndicator, styles === null || styles === void 0 ? void 0 : styles.localVideo, theme.effects.roundedCorner4, localVideoTileSize, layout, showLocalVideoTileLabel, spotlightedParticipants, onStartLocalSpotlight, onStopLocalSpotlight, maxParticipantsToSpotlight, remoteVideoTileMenu, strings, drawerMenuHostId, reactionResources, videoTilesOptions, remoteParticipants.length]);
    const onPinParticipant = useCallback((userId) => {
        if (pinnedParticipants.length >= MAX_PINNED_REMOTE_VIDEO_TILES) {
            return;
        }
        if (!pinnedParticipants.includes(userId)) {
            setPinnedParticipantsState(pinnedParticipants.concat(userId));
        }
        onPinParticipantHandler === null || onPinParticipantHandler === void 0 ? void 0 : onPinParticipantHandler(userId);
    }, [pinnedParticipants, setPinnedParticipantsState, onPinParticipantHandler]);
    const onUnpinParticipant = useCallback((userId) => {
        setPinnedParticipantsState(pinnedParticipantsState.filter(p => p !== userId));
        onUnpinParticipantHandler === null || onUnpinParticipantHandler === void 0 ? void 0 : onUnpinParticipantHandler(userId);
    }, [pinnedParticipantsState, setPinnedParticipantsState, onUnpinParticipantHandler]);
    const [announcementString, setAnnouncementString] = React.useState('');
    /**
     * sets the announcement string for VideoGallery actions so that the screenreader will trigger
     */
    const toggleAnnouncerString = useCallback((announcement) => {
        setAnnouncementString(announcement);
        /**
         * Clears the announcer string after VideoGallery action allowing it to be re-announced.
         */
        setTimeout(() => {
            setAnnouncementString('');
        }, 3000);
    }, [setAnnouncementString]);
    const defaultOnRenderVideoTile = useCallback((participant, isVideoParticipant) => {
        const remoteVideoStream = participant.videoStream;
        const selectedScalingMode = remoteVideoStream ? selectedScalingModeState[participant.userId] : undefined;
        let isPinned = pinnedParticipants === null || pinnedParticipants === void 0 ? void 0 : pinnedParticipants.includes(participant.userId);
        const isSpotlighted = !!participant.spotlight;
        isPinned = isSpotlighted ? false : isPinned;
        const createViewOptions = () => {
            var _a, _b;
            if (selectedScalingMode) {
                return selectedScalingMode;
            }
            return (remoteVideoStream === null || remoteVideoStream === void 0 ? void 0 : remoteVideoStream.streamSize) && ((_a = remoteVideoStream.streamSize) === null || _a === void 0 ? void 0 : _a.height) > ((_b = remoteVideoStream.streamSize) === null || _b === void 0 ? void 0 : _b.width) ? {
                scalingMode: 'Fit',
                isMirrored: remoteVideoViewOptions === null || remoteVideoViewOptions === void 0 ? void 0 : remoteVideoViewOptions.isMirrored
            } : remoteVideoViewOptions;
        };
        return React.createElement(_RemoteVideoTile, { alwaysShowLabelBackground: videoTilesOptions === null || videoTilesOptions === void 0 ? void 0 : videoTilesOptions.alwaysShowLabelBackground, streamId: remoteVideoStream === null || remoteVideoStream === void 0 ? void 0 : remoteVideoStream.id, key: participant.userId, userId: participant.userId, remoteParticipant: participant, onCreateRemoteStreamView: isVideoParticipant ? onCreateRemoteStreamView : undefined, onDisposeRemoteStreamView: isVideoParticipant ? onDisposeRemoteVideoStreamView : undefined, isAvailable: isVideoParticipant ? remoteVideoStream === null || remoteVideoStream === void 0 ? void 0 : remoteVideoStream.isAvailable : false, isReceiving: isVideoParticipant ? remoteVideoStream === null || remoteVideoStream === void 0 ? void 0 : remoteVideoStream.isReceiving : false, renderElement: isVideoParticipant ? remoteVideoStream === null || remoteVideoStream === void 0 ? void 0 : remoteVideoStream.renderElement : undefined, remoteVideoViewOptions: createViewOptions(), onRenderAvatar: onRenderAvatar, showMuteIndicator: showMuteIndicator, strings: strings, participantState: participant.state, menuKind: participant.userId === localParticipant.userId ? undefined : remoteVideoTileMenu ? remoteVideoTileMenu.kind === 'drawer' ? 'drawer' : 'contextual' : undefined, drawerMenuHostId: drawerMenuHostId, onPinParticipant: onPinParticipant, onUnpinParticipant: onUnpinParticipant, onUpdateScalingMode: onUpdateScalingMode, isPinned: isPinned, disablePinMenuItem: pinnedParticipants.length >= MAX_PINNED_REMOTE_VIDEO_TILES, toggleAnnouncerString: toggleAnnouncerString, spotlightedParticipantUserIds: spotlightedParticipants, isSpotlighted: isSpotlighted, onStartSpotlight: onStartRemoteSpotlight, onStopSpotlight: onStopRemoteSpotlight, maxParticipantsToSpotlight: maxParticipantsToSpotlight, reactionResources: reactionResources, onMuteParticipant: onMuteParticipant, onForbidAudio: onForbidAudio, onPermitAudio: onPermitAudio, onForbidVideo: onForbidVideo, onPermitVideo: onPermitVideo });
    }, [selectedScalingModeState, pinnedParticipants, videoTilesOptions === null || videoTilesOptions === void 0 ? void 0 : videoTilesOptions.alwaysShowLabelBackground, onCreateRemoteStreamView, onDisposeRemoteVideoStreamView, onRenderAvatar, showMuteIndicator, strings, localParticipant.userId, remoteVideoTileMenu, drawerMenuHostId, onPinParticipant, onUnpinParticipant, onUpdateScalingMode, toggleAnnouncerString, spotlightedParticipants, onStartRemoteSpotlight, onStopRemoteSpotlight, maxParticipantsToSpotlight, reactionResources, onMuteParticipant, onForbidAudio, onPermitAudio, onForbidVideo, onPermitVideo, remoteVideoViewOptions]);
    const screenShareParticipant = remoteParticipants.find(participant => { var _a; return (_a = participant.screenShareStream) === null || _a === void 0 ? void 0 : _a.isAvailable; });
    const localScreenShareStreamComponent = React.createElement(LocalScreenShare, { localParticipant: localParticipant, renderElement: (_a = localParticipant.screenShareStream) === null || _a === void 0 ? void 0 : _a.renderElement, isAvailable: (_b = localParticipant.screenShareStream) === null || _b === void 0 ? void 0 : _b.isAvailable, onCreateLocalStreamView: onCreateLocalStreamView, onDisposeLocalScreenShareStreamView: onDisposeLocalScreenShareStreamView, localScreenShareView: localScreenShareView });
    const remoteScreenShareComponent = screenShareParticipant && React.createElement(RemoteScreenShare, Object.assign({}, screenShareParticipant, { renderElement: (_c = screenShareParticipant.screenShareStream) === null || _c === void 0 ? void 0 : _c.renderElement, onCreateRemoteStreamView: onCreateRemoteStreamView, onDisposeRemoteStreamView: onDisposeRemoteScreenShareStreamView, isReceiving: (_d = screenShareParticipant.screenShareStream) === null || _d === void 0 ? void 0 : _d.isReceiving, participantVideoScalingMode: selectedScalingModeState[screenShareParticipant.userId], localParticipant: localParticipant, remoteParticipants: remoteParticipants, reactionResources: reactionResources }));
    const screenShareComponent = remoteScreenShareComponent ? remoteScreenShareComponent : localParticipant.isScreenSharingOn ? localScreenShareStreamComponent : undefined;
    // Current implementation of capabilities is only based on user role.
    // This logic checks for the user role and if the user is a Teams user.
    const canSwitchToTogetherModeLayout = isTogetherModeActive || _isIdentityMicrosoftTeamsUser(localParticipant.userId) && startTogetherModeEnabled;
    const togetherModeStreamComponent = useMemo(() => 
    // Avoids unnecessary rendering of TogetherModeStream component when it is not needed
    !screenShareComponent && canSwitchToTogetherModeLayout && layout === 'togetherMode' ? React.createElement(TogetherModeStream, { startTogetherModeEnabled: startTogetherModeEnabled, isTogetherModeActive: isTogetherModeActive, onCreateTogetherModeStreamView: onCreateTogetherModeStreamView, onStartTogetherMode: onStartTogetherMode, onDisposeTogetherModeStreamView: onDisposeTogetherModeStreamView, onSetTogetherModeSceneSize: onSetTogetherModeSceneSize, togetherModeStreams: togetherModeStreams, seatingCoordinates: togetherModeSeatingCoordinates, localParticipant: localParticipant, remoteParticipants: remoteParticipants, reactionResources: reactionResources, containerWidth: containerWidth, containerHeight: containerHeight }) : undefined, [layout, screenShareComponent, canSwitchToTogetherModeLayout, startTogetherModeEnabled, isTogetherModeActive, onCreateTogetherModeStreamView, onStartTogetherMode, onDisposeTogetherModeStreamView, onSetTogetherModeSceneSize, togetherModeStreams, togetherModeSeatingCoordinates, localParticipant, remoteParticipants, reactionResources, containerWidth, containerHeight]);
    const layoutProps = useMemo(() => ({
        remoteParticipants,
        localParticipant,
        screenShareComponent,
        showCameraSwitcherInLocalPreview,
        maxRemoteVideoStreams,
        dominantSpeakers,
        styles,
        onRenderRemoteParticipant: onRenderRemoteVideoTile !== null && onRenderRemoteVideoTile !== void 0 ? onRenderRemoteVideoTile : defaultOnRenderVideoTile,
        localVideoComponent: localVideoTile,
        parentWidth: containerWidth,
        parentHeight: containerHeight,
        pinnedParticipantUserIds: pinnedParticipants,
        overflowGalleryPosition,
        localVideoTileSize,
        spotlightedParticipantUserIds: spotlightedParticipants
    }), [remoteParticipants, localParticipant, screenShareComponent, showCameraSwitcherInLocalPreview, maxRemoteVideoStreams, dominantSpeakers, styles, localVideoTile, containerWidth, containerHeight, onRenderRemoteVideoTile, defaultOnRenderVideoTile, pinnedParticipants, overflowGalleryPosition, localVideoTileSize, spotlightedParticipants]);
    const videoGalleryLayout = useMemo(() => {
        if (screenShareParticipant && layout === 'focusedContent') {
            return React.createElement(FocusedContentLayout, Object.assign({}, layoutProps));
        }
        if (layout === 'floatingLocalVideo') {
            return React.createElement(FloatingLocalVideoLayout, Object.assign({}, layoutProps));
        }
        if (layout === 'speaker') {
            return React.createElement(SpeakerVideoLayout, Object.assign({}, layoutProps));
        }
        // Teams users can switch to Together mode layout only if they have the capability,
        // while ACS users can do so only if Together mode is enabled.
        if (togetherModeStreamComponent && layout === 'togetherMode') {
            return React.createElement(TogetherModeLayout, { togetherModeStreamComponent: togetherModeStreamComponent });
        }
        return React.createElement(DefaultLayout, Object.assign({}, layoutProps));
    }, [layout, layoutProps, screenShareParticipant, togetherModeStreamComponent]);
    return React.createElement("div", { 
        // We don't assign an drawer menu host id to the VideoGallery when a drawerMenuHostId is assigned from props
        id: drawerMenuHostIdFromProp ? undefined : drawerMenuHostId, "data-ui-id": ids.videoGallery, ref: containerRef, className: mergeStyles(videoGalleryOuterDivStyle, styles === null || styles === void 0 ? void 0 : styles.root, unselectable) },
        videoGalleryLayout,
        React.createElement(Announcer, { announcementString: announcementString, ariaLive: "polite" }));
};
//# sourceMappingURL=VideoGallery.js.map