// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { ErrorBar, useTheme } from "../../../../react-components/src";
import { NotificationStack } from "../../../../react-components/src";
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { BaseProvider } from '../common/BaseComposite';
import { useLocale } from '../localization';
import { CallAdapterProvider, useAdapter } from './adapter/CallAdapterProvider';
import { CallPage } from './pages/CallPage';
import { ConfigurationPage } from './pages/ConfigurationPage';
import { NoticePage } from './pages/NoticePage';
import { useSelector } from './hooks/useSelector';
import { getAlternateCallerId, getEndedCall, getPage, getRole, getTargetCallees } from './selectors/baseSelectors';
import { LobbyPage } from './pages/LobbyPage';
import { TransferPage } from './pages/TransferPage';
import { leavingPageStyle, mainScreenContainerStyleDesktop, mainScreenContainerStyleMobile } from './styles/CallComposite.styles';
import { LayerHost, mergeStyles } from '@fluentui/react';
import { modalLayerHostStyle } from '../common/styles/ModalLocalAndRemotePIP.styles';
import { useId } from '@fluentui/react-hooks';
import { HoldPage } from './pages/HoldPage';
import { SidePaneProvider } from './components/SidePane/SidePaneProvider';
import { filterLatestNotifications, getEndedCallPageProps, trackNotificationAsDismissed, updateTrackedNotificationsWithActiveNotifications } from './utils';
import { computeComplianceNotification } from './utils';
import { usePropsFor } from './hooks/usePropsFor';
import { deviceCountSelector } from './selectors/deviceCountSelector';
import { capabilitiesChangedInfoAndRoleSelector } from './selectors/capabilitiesChangedInfoAndRoleSelector';
import { useTrackedCapabilityChangedNotifications } from './utils/TrackCapabilityChangedNotifications';
import { useEndedCallConsoleErrors } from './utils/useConsoleErrors';
import { SurveyPage } from './pages/SurveyPage';
import { useAudio } from '../common/AudioProvider';
import { complianceBannerSelector } from './selectors/complianceBannerSelector';
import { devicePermissionSelector } from './selectors/devicePermissionSelector';
const isShowing = (overrideSidePane) => {
    return !!(overrideSidePane === null || overrideSidePane === void 0 ? void 0 : overrideSidePane.isActive);
};
const MainScreen = (props) => {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
    const adapter = useAdapter();
    const { camerasCount, microphonesCount } = useSelector(deviceCountSelector);
    const hasCameras = camerasCount > 0;
    const hasMicrophones = microphonesCount > 0;
    const role = useSelector(getRole);
    const { video: cameraHasPermission, audio: micHasPermission } = useSelector(devicePermissionSelector);
    useEffect(() => {
        (() => __awaiter(void 0, void 0, void 0, function* () {
            const constrain = getQueryOptions({
                role
            });
            const permissionsResult = yield adapter.askDevicePermission(constrain);
            if (permissionsResult === null || permissionsResult === void 0 ? void 0 : permissionsResult.audio) {
                adapter.queryMicrophones();
            }
            if (permissionsResult === null || permissionsResult === void 0 ? void 0 : permissionsResult.video) {
                adapter.queryCameras();
            }
            adapter.querySpeakers();
        }))();
    }, [adapter, role,
        // Ensure we re-ask for permissions if the number of devices goes from 0 -> n during a call
        // as we cannot request permissions when there are no devices.
        hasCameras, hasMicrophones,
        // Ensure we re-query for devices when permission for the device is granted.
        cameraHasPermission, micHasPermission]);
    const { callInvitationUrl, onFetchAvatarPersonaData, onFetchParticipantMenuItems } = props;
    const page = useSelector(getPage);
    const endedCall = useSelector(getEndedCall);
    const [sidePaneRenderer, setSidePaneRenderer] = React.useState();
    const [injectedSidePaneProps, setInjectedSidePaneProps] = React.useState();
    const [userSetGalleryLayout, setUserSetGalleryLayout] = useState((_c = (_b = (_a = props.options) === null || _a === void 0 ? void 0 : _a.galleryOptions) === null || _b === void 0 ? void 0 : _b.layout) !== null && _c !== void 0 ? _c : 'floatingLocalVideo');
    const [userSetOverflowGalleryPosition, setUserSetOverflowGalleryPosition] = useState('Responsive');
    const overridePropsRef = useRef(props.overrideSidePane);
    useEffect(() => {
        setInjectedSidePaneProps(props.overrideSidePane);
        // When the injected side pane is opened, clear the previous side pane active state.
        // this ensures when the injected side pane is "closed", the previous side pane is not "re-opened".
        if (!isShowing(overridePropsRef.current) && isShowing(props.overrideSidePane)) {
            setSidePaneRenderer(undefined);
        }
        overridePropsRef.current = props.overrideSidePane;
    }, [props.overrideSidePane]);
    const onSidePaneIdChange = props.onSidePaneIdChange;
    useEffect(() => {
        onSidePaneIdChange === null || onSidePaneIdChange === void 0 ? void 0 : onSidePaneIdChange(sidePaneRenderer === null || sidePaneRenderer === void 0 ? void 0 : sidePaneRenderer.id);
    }, [sidePaneRenderer === null || sidePaneRenderer === void 0 ? void 0 : sidePaneRenderer.id, onSidePaneIdChange]);
    // When the call ends ensure the side pane is set to closed to prevent the side pane being open if the call is re-joined.
    useEffect(() => {
        const closeSidePane = () => {
            setSidePaneRenderer(undefined);
        };
        const resetUserGalleryLayout = () => {
            if (userSetGalleryLayout === 'togetherMode') {
                setUserSetGalleryLayout('floatingLocalVideo');
            }
        };
        const handleCallEnded = () => {
            closeSidePane();
            resetUserGalleryLayout();
        };
        adapter.on('callEnded', handleCallEnded);
        return () => {
            adapter.off('callEnded', handleCallEnded);
        };
    }, [adapter, userSetGalleryLayout]);
    const compositeAudioContext = useAudio();
    const capabilitiesChangedInfoAndRole = useSelector(capabilitiesChangedInfoAndRoleSelector);
    const capabilitiesChangedNotificationBarProps = useTrackedCapabilityChangedNotifications(capabilitiesChangedInfoAndRole);
    // Track the last dismissed errors of any error kind to prevent errors from re-appearing on subsequent page navigation
    // This works by tracking the most recent timestamp of any active error type.
    // And then tracking when that error type was last dismissed.
    const activeErrors = usePropsFor(ErrorBar).activeErrorMessages;
    const activeInCallErrors = usePropsFor(NotificationStack).activeErrorMessages;
    const activeNotifications = usePropsFor(NotificationStack).activeNotifications;
    const complianceProps = useSelector(complianceBannerSelector);
    const cachedProps = useRef({
        latestBooleanState: {
            callTranscribeState: false,
            callRecordState: false
        },
        latestStringState: {
            callTranscribeState: 'off',
            callRecordState: 'off'
        },
        lastUpdated: Date.now()
    });
    const complianceNotification = useMemo(() => {
        return computeComplianceNotification(complianceProps, cachedProps);
    }, [complianceProps, cachedProps]);
    useEffect(() => {
        if (complianceNotification) {
            for (let i = activeNotifications.length - 1; i >= 0; i--) {
                const notification = activeNotifications[i];
                if (notification && ['recordingStarted', 'transcriptionStarted', 'recordingStopped', 'transcriptionStopped', 'recordingAndTranscriptionStarted', 'recordingAndTranscriptionStopped', 'recordingStoppedStillTranscribing', 'transcriptionStoppedStillRecording'].includes(notification.type)) {
                    activeNotifications.splice(i, 1);
                }
            }
            activeNotifications.push(complianceNotification);
        }
        setTrackedNotifications(prev => updateTrackedNotificationsWithActiveNotifications(prev, activeNotifications));
    }, [complianceNotification, activeNotifications]);
    const [trackedErrors, setTrackedErrors] = useState({});
    const [trackedInCallErrors, setTrackedInCallErrors] = useState({});
    const [trackedNotifications, setTrackedNotifications] = useState({});
    useEffect(() => {
        setTrackedErrors(prev => updateTrackedNotificationsWithActiveNotifications(prev, activeErrors));
        setTrackedInCallErrors(prev => updateTrackedNotificationsWithActiveNotifications(prev, activeInCallErrors));
    }, [activeErrors, activeInCallErrors]);
    const onDismissError = useCallback((error) => {
        setTrackedErrors(prev => trackNotificationAsDismissed(error.type, prev));
        setTrackedInCallErrors(prev => trackNotificationAsDismissed(error.type, prev));
    }, []);
    const onDismissNotification = useCallback((notification) => {
        setTrackedNotifications(prev => trackNotificationAsDismissed(notification.type, prev));
    }, []);
    const latestErrors = useMemo(() => filterLatestNotifications(activeErrors, trackedErrors), [activeErrors, trackedErrors]);
    const latestInCallErrors = useMemo(() => filterLatestNotifications(activeInCallErrors, trackedInCallErrors), [activeInCallErrors, trackedInCallErrors]);
    const latestNotifications = useMemo(() => {
        const result = filterLatestNotifications(activeNotifications, trackedNotifications);
        // sort notifications by timestamp from earliest to latest
        result.sort((a, b) => { var _a, _b; return ((_a = a.timestamp) !== null && _a !== void 0 ? _a : new Date(Date.now())).getTime() - ((_b = b.timestamp) !== null && _b !== void 0 ? _b : new Date(Date.now())).getTime(); });
        return result;
    }, [activeNotifications, trackedNotifications]);
    const callees = useSelector(getTargetCallees);
    const locale = useLocale();
    const palette = useTheme().palette;
    const alternateCallerId = useSelector(getAlternateCallerId);
    const leavePageStyle = useMemo(() => leavingPageStyle(palette), [palette]);
    let pageElement;
    const [pinnedParticipants, setPinnedParticipants] = useState([]);
    switch (page) {
        case 'configuration':
            pageElement = React.createElement(ConfigurationPage, { joinCallOptions: (_d = props.options) === null || _d === void 0 ? void 0 : _d.joinCallOptions, mobileView: props.mobileView, startCallHandler: () => {
                    if (callees) {
                        adapter.startCall(callees, alternateCallerId ? {
                            alternateCallerId: {
                                phoneNumber: alternateCallerId
                            }
                        } : {});
                    }
                    else {
                        adapter.joinCall({
                            microphoneOn: 'keep',
                            cameraOn: 'keep'
                        });
                    }
                }, updateSidePaneRenderer: setSidePaneRenderer, latestErrors: latestErrors, onDismissError: onDismissError, modalLayerHostId: props.modalLayerHostId, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps, logo: (_f = (_e = props.options) === null || _e === void 0 ? void 0 : _e.branding) === null || _f === void 0 ? void 0 : _f.logo, backgroundImage: (_h = (_g = props.options) === null || _g === void 0 ? void 0 : _g.branding) === null || _h === void 0 ? void 0 : _h.backgroundImage });
            break;
        case 'accessDeniedTeamsMeeting':
            pageElement = React.createElement(NoticePage, { iconName: "NoticePageAccessDeniedTeamsMeeting", title: locale.strings.call.failedToJoinTeamsMeetingReasonAccessDeniedTitle, moreDetails: locale.strings.call.failedToJoinTeamsMeetingReasonAccessDeniedMoreDetails, dataUiId: 'access-denied-teams-meeting-page' });
            break;
        case 'removedFromCall':
            pageElement = React.createElement(NoticePage, { iconName: "NoticePageRemovedFromCall", title: locale.strings.call.removedFromCallTitle, moreDetails: locale.strings.call.removedFromCallMoreDetails, dataUiId: 'removed-from-call-page' });
            break;
        case 'joinCallFailedDueToNoNetwork':
            pageElement = React.createElement(NoticePage, { iconName: "NoticePageJoinCallFailedDueToNoNetwork", title: locale.strings.call.failedToJoinCallDueToNoNetworkTitle, moreDetails: locale.strings.call.failedToJoinCallDueToNoNetworkMoreDetails, dataUiId: 'join-call-failed-due-to-no-network-page' });
            break;
        case 'leaving':
            pageElement = React.createElement(NoticePage, { title: (_j = locale.strings.call.leavingCallTitle) !== null && _j !== void 0 ? _j : 'Leaving...', dataUiId: 'leaving-page', pageStyle: leavePageStyle, disableStartCallButton: true });
            break;
        case 'badRequest':
            {
                const { title, moreDetails, disableStartCallButton, iconName } = getEndedCallPageProps(locale, endedCall);
                pageElement = React.createElement(NoticePage, { iconName: iconName, title: title, moreDetails: callees ? '' : moreDetails, dataUiId: 'left-call-page', disableStartCallButton: disableStartCallButton });
                break;
            }
        case 'leftCall':
            {
                const { title, moreDetails, disableStartCallButton, iconName } = getEndedCallPageProps(locale, endedCall);
                if (!((_l = (_k = props.options) === null || _k === void 0 ? void 0 : _k.surveyOptions) === null || _l === void 0 ? void 0 : _l.disableSurvey)) {
                    pageElement = React.createElement(SurveyPage, { dataUiId: 'left-call-page', surveyOptions: (_m = props.options) === null || _m === void 0 ? void 0 : _m.surveyOptions, iconName: iconName, title: title, moreDetails: moreDetails, disableStartCallButton: disableStartCallButton, mobileView: props.mobileView });
                    break;
                }
                pageElement = React.createElement(NoticePage, { iconName: iconName, title: title, moreDetails: callees ? '' : moreDetails, dataUiId: 'left-call-page', disableStartCallButton: disableStartCallButton });
                break;
            }
        case 'lobby':
            pageElement = React.createElement(LobbyPage, { mobileView: props.mobileView, modalLayerHostId: props.modalLayerHostId, options: props.options, updateSidePaneRenderer: setSidePaneRenderer, mobileChatTabHeader: props.mobileChatTabHeader, latestErrors: latestInCallErrors, latestNotifications: latestNotifications, onDismissError: onDismissError, onDismissNotification: onDismissNotification, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps });
            break;
        case 'transferring':
            pageElement = React.createElement(TransferPage, { mobileView: props.mobileView, modalLayerHostId: props.modalLayerHostId, options: props.options, updateSidePaneRenderer: setSidePaneRenderer, mobileChatTabHeader: props.mobileChatTabHeader, onFetchAvatarPersonaData: onFetchAvatarPersonaData, latestErrors: latestInCallErrors, latestNotifications: latestNotifications, onDismissError: onDismissError, onDismissNotification: onDismissNotification, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps });
            break;
        case 'call':
            pageElement = React.createElement(CallPage, { callInvitationURL: callInvitationUrl, onFetchAvatarPersonaData: onFetchAvatarPersonaData, onFetchParticipantMenuItems: onFetchParticipantMenuItems, mobileView: props.mobileView, modalLayerHostId: props.modalLayerHostId, options: props.options, updateSidePaneRenderer: setSidePaneRenderer, mobileChatTabHeader: props.mobileChatTabHeader, onCloseChatPane: props.onCloseChatPane, latestErrors: latestInCallErrors, latestNotifications: latestNotifications, onDismissError: onDismissError, onDismissNotification: onDismissNotification, galleryLayout: userSetGalleryLayout, onUserSetGalleryLayoutChange: setUserSetGalleryLayout, onSetUserSetOverflowGalleryPosition: setUserSetOverflowGalleryPosition, userSetOverflowGalleryPosition: userSetOverflowGalleryPosition, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps, pinnedParticipants: pinnedParticipants, setPinnedParticipants: setPinnedParticipants, compositeAudioContext: compositeAudioContext, disableAutoShowDtmfDialer: (_o = props.options) === null || _o === void 0 ? void 0 : _o.disableAutoShowDtmfDialer, notificationOptions: (_p = props.options) === null || _p === void 0 ? void 0 : _p.notificationOptions });
            break;
        case 'hold':
            pageElement = React.createElement(React.Fragment, null, React.createElement(HoldPage, { mobileView: props.mobileView, modalLayerHostId: props.modalLayerHostId, options: props.options, updateSidePaneRenderer: setSidePaneRenderer, mobileChatTabHeader: props.mobileChatTabHeader, latestErrors: latestInCallErrors, latestNotifications: latestNotifications, onDismissError: onDismissError, onDismissNotification: onDismissNotification, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps }));
            break;
    }
    if (page === 'returningFromBreakoutRoom') {
        pageElement = React.createElement(CallPage, { callInvitationURL: callInvitationUrl, onFetchAvatarPersonaData: onFetchAvatarPersonaData, onFetchParticipantMenuItems: onFetchParticipantMenuItems, mobileView: props.mobileView, modalLayerHostId: props.modalLayerHostId, options: props.options, updateSidePaneRenderer: setSidePaneRenderer, mobileChatTabHeader: props.mobileChatTabHeader, onCloseChatPane: props.onCloseChatPane, latestErrors: latestInCallErrors, latestNotifications: latestNotifications, onDismissError: onDismissError, onDismissNotification: onDismissNotification, galleryLayout: userSetGalleryLayout, onUserSetGalleryLayoutChange: setUserSetGalleryLayout, onSetUserSetOverflowGalleryPosition: setUserSetOverflowGalleryPosition, userSetOverflowGalleryPosition: userSetOverflowGalleryPosition, capabilitiesChangedNotificationBarProps: capabilitiesChangedNotificationBarProps, pinnedParticipants: pinnedParticipants, setPinnedParticipants: setPinnedParticipants, compositeAudioContext: compositeAudioContext, disableAutoShowDtmfDialer: (_q = props.options) === null || _q === void 0 ? void 0 : _q.disableAutoShowDtmfDialer, notificationOptions: (_r = props.options) === null || _r === void 0 ? void 0 : _r.notificationOptions });
    }
    useEndedCallConsoleErrors(endedCall);
    if (!pageElement) {
        throw new Error('Invalid call composite page');
    }
    return React.createElement(SidePaneProvider, { sidePaneRenderer: sidePaneRenderer, overrideSidePane: injectedSidePaneProps }, pageElement);
};
/**
 * A customizable UI composite for calling experience.
 *
 * @remarks Call composite min width/height are as follow:
 * - mobile: 17.5rem x 21rem (280px x 336px, with default rem at 16px)
 * - desktop: 30rem x 22rem (480px x 352px, with default rem at 16px)
 *
 * @public
 */
export const CallComposite = (props) => React.createElement(CallCompositeInner, Object.assign({}, props));
/** @private */
export const CallCompositeInner = (props) => {
    const { adapter, callInvitationUrl, onFetchAvatarPersonaData, onFetchParticipantMenuItems, options, formFactor = 'desktop' } = props;
    const mobileView = formFactor === 'mobile';
    const modalLayerHostId = useId('modalLayerhost');
    const mainScreenContainerClassName = useMemo(() => {
        return mobileView ? mainScreenContainerStyleMobile : mainScreenContainerStyleDesktop;
    }, [mobileView]);
    return React.createElement("div", { className: mainScreenContainerClassName },
        React.createElement(BaseProvider, Object.assign({}, props),
            React.createElement(CallAdapterProvider, { adapter: adapter },
                React.createElement(MainScreen, { callInvitationUrl: callInvitationUrl, onFetchAvatarPersonaData: onFetchAvatarPersonaData, onFetchParticipantMenuItems: onFetchParticipantMenuItems, mobileView: mobileView, modalLayerHostId: modalLayerHostId, options: options, onSidePaneIdChange: props.onSidePaneIdChange, overrideSidePane: props.overrideSidePane, mobileChatTabHeader: props.mobileChatTabHeader, onCloseChatPane: props.onCloseChatPane }),
                // This layer host is for ModalLocalAndRemotePIP in SidePane. This LayerHost cannot be inside the SidePane
                // because when the SidePane is hidden, ie. style property display is 'none', it takes up no space. This causes problems when dragging
                // the Modal because the draggable bounds thinks it has no space and will always return to its initial position after dragging.
                // Additionally, this layer host cannot be in the Call Arrangement as it needs to be rendered before useMinMaxDragPosition() in
                // common/utils useRef is called.
                // Warning: this is fragile and works because the call arrangement page is only rendered after the call has connected and thus this
                // LayerHost will be guaranteed to have rendered (and subsequently mounted in the DOM). This ensures the DOM element will be available
                // before the call to `document.getElementById(modalLayerHostId)` is made.
                React.createElement(LayerHost, { id: modalLayerHostId, className: mergeStyles(modalLayerHostStyle) }))));
};
const getQueryOptions = (options) => {
    if (options.role === 'Consumer') {
        return {
            video: false,
            audio: true
        };
    }
    return {
        video: true,
        audio: true
    };
};
//# sourceMappingURL=CallComposite.js.map