// 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 { LocalVideoStream } from '@azure/communication-calling';
import { toFlatCommunicationIdentifier } from "../../../acs-ui-common/src";
import { _toCommunicationIdentifier } from "../../../acs-ui-common/src";
import memoizeOne from 'memoize-one';
import { disposeAllLocalPreviewViews, _isInCall, _isInLobbyOrConnecting, _isPreviewOn, getCallStateIfExist } from '../utils/callUtils';
import { Features } from '@azure/communication-calling';
/**
 * @private
 */
export const areStreamsEqual = (prevStream, newStream) => {
    return !!prevStream && !!newStream && prevStream.source.id === newStream.source.id;
};
/**
 * Create the common implementation of {@link CallingHandlers} for all types of Call
 *
 * @private
 */
export const createDefaultCommonCallingHandlers = memoizeOne((callClient, deviceManager, call, options) => {
    const onStartLocalVideo = () => __awaiter(void 0, void 0, void 0, function* () {
        // Before the call object creates a stream, dispose of any local preview streams.
        // @TODO: is there any way to parent the unparented view to the call object instead
        // of disposing and creating a new stream?
        yield disposeAllLocalPreviewViews(callClient);
        const callId = call === null || call === void 0 ? void 0 : call.id;
        let videoDeviceInfo = callClient.getState().deviceManager.selectedCamera;
        if (!videoDeviceInfo) {
            const cameras = yield (deviceManager === null || deviceManager === void 0 ? void 0 : deviceManager.getCameras());
            videoDeviceInfo = cameras && cameras.length > 0 ? cameras[0] : undefined;
            if (videoDeviceInfo) {
                deviceManager === null || deviceManager === void 0 ? void 0 : deviceManager.selectCamera(videoDeviceInfo);
            }
        }
        if (!callId || !videoDeviceInfo) {
            return;
        }
        const stream = new LocalVideoStream(videoDeviceInfo);
        if (call && !call.localVideoStreams.find(s => areStreamsEqual(s, stream))) {
            yield call.startVideo(stream);
        }
    });
    const onStopLocalVideo = (stream) => __awaiter(void 0, void 0, void 0, function* () {
        const callId = call === null || call === void 0 ? void 0 : call.id;
        if (!callId) {
            return;
        }
        if (call && call.localVideoStreams.find(s => areStreamsEqual(s, stream))) {
            yield call.stopVideo(stream);
        }
    });
    const onToggleCamera = (options) => __awaiter(void 0, void 0, void 0, function* () {
        const previewOn = _isPreviewOn(callClient.getState().deviceManager);
        // the disposal of the unparented views is to workaround: https://skype.visualstudio.com/SPOOL/_workitems/edit/3030558.
        // The root cause of the issue is caused by never transitioning the unparented view to the
        // call object when going from configuration page (disconnected call state) to connecting.
        //
        // Currently the only time the local video stream is moved from unparented view to the call
        // object is when we transition from connecting -> call state. If the camera was on,
        // inside the MediaGallery we trigger toggleCamera. This triggers onStartLocalVideo which
        // destroys the unparentedView and creates a new stream in the call - so all looks well.
        //
        // However, if someone turns off their camera during the lobbyOrConnecting screen, the
        // call.localVideoStreams will be empty (as the stream is currently stored in the unparented
        // views and was never transitioned to the call object) and thus we incorrectly try to create
        // a new video stream for the call object, instead of only stopping the unparented view.
        //
        // The correct fix for this is to ensure that callAgent.onStartCall is called with the
        // localvideostream as a videoOption. That will mean call.onLocalVideoStreamsUpdated will
        // be triggered when the call is in connecting state, which we can then transition the
        // local video stream to the stateful call client and get into a clean state.
        if (call && (_isInCall(call.state) || _isInLobbyOrConnecting(call.state))) {
            const stream = call.localVideoStreams.find(stream => stream.mediaStreamType === 'Video');
            const unparentedViews = callClient.getState().deviceManager.unparentedViews;
            if (stream || unparentedViews.length > 0) {
                unparentedViews.forEach(view => view.mediaStreamType === 'Video' && callClient.disposeView(undefined, undefined, view));
                if (stream) {
                    yield onStopLocalVideo(stream);
                }
            }
            else {
                yield onStartLocalVideo();
            }
        }
        else {
            /**
             * This will create a unparented view to be used on the configuration page and the connecting screen
             *
             * If the device that the stream will come from is not on from permissions checks, then it will take time
             * to create the stream since device is off. If we are turn the camera on immedietly on the configuration page we see it is
             * fast but that is because the device is already primed to return a stream.
             *
             * On the connecting page the device has already turned off and the connecting window is so small we do not see the resulting
             * unparented view from the code below.
             */
            const selectedCamera = callClient.getState().deviceManager.selectedCamera;
            if (selectedCamera) {
                if (previewOn) {
                    yield onDisposeLocalStreamView();
                }
                else {
                    yield callClient.createView(undefined, undefined, {
                        source: selectedCamera,
                        mediaStreamType: 'Video'
                    }, options);
                }
            }
        }
    });
    const onSelectMicrophone = (device) => __awaiter(void 0, void 0, void 0, function* () {
        if (!deviceManager) {
            return;
        }
        return deviceManager.selectMicrophone(device);
    });
    const onSelectSpeaker = (device) => __awaiter(void 0, void 0, void 0, function* () {
        if (!deviceManager) {
            return;
        }
        return deviceManager.selectSpeaker(device);
    });
    const onSelectCamera = (device, options) => __awaiter(void 0, void 0, void 0, function* () {
        if (!deviceManager) {
            return;
        }
        if (call && _isInCall(call.state)) {
            deviceManager.selectCamera(device);
            const stream = call.localVideoStreams.find(stream => stream.mediaStreamType === 'Video');
            yield (stream === null || stream === void 0 ? void 0 : stream.switchSource(device));
            yield Promise.all([
                /// TODO: TEMPORARY SOLUTION
                /// The Calling SDK needs to wait until the stream is ready before resolving the switchSource promise.
                /// This is a temporary solution to wait for the stream to be ready before resolving the promise.
                /// This allows the onSelectCamera to be throttled to prevent the streams from getting in to a frozen state
                /// if the user switches cameras too rapidly.
                /// This is to be removed once the Calling SDK has issued a fix.
                stream === null || stream === void 0 ? void 0 : stream.getMediaStream(),
                // An extra wait here is also necessary to prevent the remote stream freezing issue.
                // If this is removed, please test switching cameras rapidly won't cause stream to freeze for remote users.
                // When this mitigation was introduced, the repro interval time that caused the issue was:
                // - iPhone 11, safari, v8.3.1: 750ms
                // - Pixel 6, chrome, Android 15: 400ms
                // - Windows 11, edge: 100ms
                new Promise(resolve => setTimeout(resolve, 1000))
            ]);
        }
        else {
            const previewOn = _isPreviewOn(callClient.getState().deviceManager);
            if (!previewOn) {
                deviceManager.selectCamera(device);
                return;
            }
            yield onDisposeLocalStreamView();
            deviceManager.selectCamera(device);
            yield callClient.createView(undefined, undefined, {
                source: device,
                mediaStreamType: 'Video'
            }, options);
        }
    });
    const onRaiseHand = () => __awaiter(void 0, void 0, void 0, function* () { var _a; return yield ((_a = call === null || call === void 0 ? void 0 : call.feature(Features.RaiseHand)) === null || _a === void 0 ? void 0 : _a.raiseHand()); });
    const onLowerHand = () => __awaiter(void 0, void 0, void 0, function* () { var _b; return yield ((_b = call === null || call === void 0 ? void 0 : call.feature(Features.RaiseHand)) === null || _b === void 0 ? void 0 : _b.lowerHand()); });
    const onToggleRaiseHand = () => __awaiter(void 0, void 0, void 0, function* () {
        const raiseHandFeature = call === null || call === void 0 ? void 0 : call.feature(Features.RaiseHand);
        const localUserId = callClient.getState().userId;
        const isLocalRaisedHand = raiseHandFeature === null || raiseHandFeature === void 0 ? void 0 : raiseHandFeature.getRaisedHands().find(publishedState => toFlatCommunicationIdentifier(publishedState.identifier) === toFlatCommunicationIdentifier(localUserId));
        if (isLocalRaisedHand) {
            yield (raiseHandFeature === null || raiseHandFeature === void 0 ? void 0 : raiseHandFeature.lowerHand());
        }
        else {
            yield (raiseHandFeature === null || raiseHandFeature === void 0 ? void 0 : raiseHandFeature.raiseHand());
        }
    });
    const onReactionClick = (reaction) => __awaiter(void 0, void 0, void 0, function* () {
        var _c;
        if (reaction === 'like' || reaction === 'applause' || reaction === 'heart' || reaction === 'laugh' || reaction === 'surprised') {
            yield ((_c = call === null || call === void 0 ? void 0 : call.feature(Features.Reaction)) === null || _c === void 0 ? void 0 : _c.sendReaction({
                reactionType: reaction
            }));
        }
        else {
            console.warn(`Can not recognize ${reaction} as meeting reaction`);
        }
        return;
    });
    const onToggleMicrophone = () => __awaiter(void 0, void 0, void 0, function* () {
        if (!call || !(_isInCall(call.state) || _isInLobbyOrConnecting(call.state))) {
            throw new Error(`Please invoke onToggleMicrophone after call is started`);
        }
        return call.isMuted ? yield call.unmute() : yield call.mute();
    });
    const onStartScreenShare = () => __awaiter(void 0, void 0, void 0, function* () { return yield (call === null || call === void 0 ? void 0 : call.startScreenSharing()); });
    const onStopScreenShare = () => __awaiter(void 0, void 0, void 0, function* () { return yield (call === null || call === void 0 ? void 0 : call.stopScreenSharing()); });
    const onToggleScreenShare = () => __awaiter(void 0, void 0, void 0, function* () { return (call === null || call === void 0 ? void 0 : call.isScreenSharingOn) ? yield onStopScreenShare() : yield onStartScreenShare(); });
    const onHangUp = (forEveryone) => __awaiter(void 0, void 0, void 0, function* () {
        return yield (call === null || call === void 0 ? void 0 : call.hangUp({
            forEveryone: forEveryone === true ? true : false
        }));
    });
    const onToggleHold = () => __awaiter(void 0, void 0, void 0, function* () { return (call === null || call === void 0 ? void 0 : call.state) === 'LocalHold' ? yield (call === null || call === void 0 ? void 0 : call.resume()) : yield (call === null || call === void 0 ? void 0 : call.hold()); });
    const onCreateLocalStreamView = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {
        scalingMode: 'Crop',
        isMirrored: true
    }) {
        if (!call || call.localVideoStreams.length === 0) {
            return;
        }
        const callState = callClient.getState().calls[call.id];
        if (!callState) {
            return;
        }
        const localStream = callState.localVideoStreams.find(item => item.mediaStreamType === 'Video');
        const localScreenSharingStream = callState.localVideoStreams.find(item => item.mediaStreamType === 'ScreenSharing');
        let createViewResult = undefined;
        if (localStream && !localStream.view) {
            createViewResult = yield callClient.createView(call.id, undefined, localStream, options);
        }
        if (localScreenSharingStream && !localScreenSharingStream.view && call.isScreenSharingOn) {
            // Hardcoded `scalingMode` since it is highly unlikely that CONTOSO would ever want to use a different scaling mode for screenshare.
            // Using `Crop` would crop the contents of screenshare and `Stretch` would warp it.
            // `Fit` is the only mode that maintains the integrity of the screen being shared.
            createViewResult = yield callClient.createView(call.id, undefined, localScreenSharingStream, {
                scalingMode: 'Fit'
            });
        }
        return (createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view) ? {
            view: createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view
        } : undefined;
    });
    const onCreateRemoteStreamView = (userId_1, ...args_2) => __awaiter(void 0, [userId_1, ...args_2], void 0, function* (userId, options = {
        scalingMode: 'Crop'
    }) {
        if (!call) {
            return;
        }
        const callState = getCallStateIfExist(callClient.getState(), call.id);
        if (!callState) {
            return;
        }
        const participant = Object.values(callState.remoteParticipants).find(participant => toFlatCommunicationIdentifier(participant.identifier) === userId);
        if (!participant || !participant.videoStreams) {
            return;
        }
        /**
         * There is a bug from the calling sdk where if a user leaves and rejoins immediately
         * it adds 2 more potential streams this remote participant can use. The old 2 streams
         * still show as available and that is how we got a frozen stream in this case. The stopgap
         * until streams accurately reflect their availability is to always prioritize the latest streams of a certain type
         * e.g findLast instead of find
         */
        // Find the first available stream, if there is none, then get the first stream
        const remoteVideoStream = Object.values(participant.videoStreams).findLast(i => i.mediaStreamType === 'Video' && i.isAvailable) || Object.values(participant.videoStreams).findLast(i => i.mediaStreamType === 'Video');
        const screenShareStream = Object.values(participant.videoStreams).findLast(i => i.mediaStreamType === 'ScreenSharing' && i.isAvailable) || Object.values(participant.videoStreams).findLast(i => i.mediaStreamType === 'ScreenSharing');
        let createViewResult = undefined;
        if (remoteVideoStream && remoteVideoStream.isAvailable && !remoteVideoStream.view) {
            createViewResult = yield callClient.createView(call.id, participant.identifier, remoteVideoStream, options);
        }
        if (screenShareStream && screenShareStream.isAvailable && !screenShareStream.view) {
            // Hardcoded `scalingMode` since it is highly unlikely that CONTOSO would ever want to use a different scaling mode for screenshare.
            // Using `Crop` would crop the contents of screenshare and `Stretch` would warp it.
            // `Fit` is the only mode that maintains the integrity of the screen being shared.
            createViewResult = yield callClient.createView(call.id, participant.identifier, screenShareStream, {
                scalingMode: 'Fit'
            });
        }
        return (createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view) ? {
            view: createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view
        } : undefined;
    });
    const onDisposeRemoteStreamView = (userId) => __awaiter(void 0, void 0, void 0, function* () {
        if (!call) {
            return;
        }
        const callState = getCallStateIfExist(callClient.getState(), call.id);
        if (!callState) {
            return;
        }
        const participant = Object.values(callState.remoteParticipants).find(participant => toFlatCommunicationIdentifier(participant.identifier) === userId);
        if (!participant || !participant.videoStreams) {
            return;
        }
        const remoteVideoStream = Object.values(participant.videoStreams).find(i => i.mediaStreamType === 'Video');
        const screenShareStream = Object.values(participant.videoStreams).find(i => i.mediaStreamType === 'ScreenSharing');
        if (remoteVideoStream && remoteVideoStream.view) {
            callClient.disposeView(call.id, participant.identifier, remoteVideoStream);
        }
        if (screenShareStream && screenShareStream.view) {
            callClient.disposeView(call.id, participant.identifier, screenShareStream);
        }
    });
    const onDisposeRemoteVideoStreamView = (userId) => __awaiter(void 0, void 0, void 0, function* () {
        if (!call) {
            return;
        }
        const callState = getCallStateIfExist(callClient.getState(), call.id);
        if (!callState) {
            return;
        }
        const participant = Object.values(callState.remoteParticipants).find(participant => toFlatCommunicationIdentifier(participant.identifier) === userId);
        if (!participant || !participant.videoStreams) {
            return;
        }
        const remoteVideoStream = Object.values(participant.videoStreams).filter(i => i.mediaStreamType === 'Video');
        for (const stream of remoteVideoStream) {
            if (stream.view) {
                callClient.disposeView(call.id, participant.identifier, stream);
            }
        }
    });
    const onDisposeRemoteScreenShareStreamView = (userId) => __awaiter(void 0, void 0, void 0, function* () {
        if (!call) {
            return;
        }
        const callState = getCallStateIfExist(callClient.getState(), call.id);
        if (!callState) {
            return;
        }
        const participant = Object.values(callState.remoteParticipants).find(participant => toFlatCommunicationIdentifier(participant.identifier) === userId);
        if (!participant || !participant.videoStreams) {
            return;
        }
        const screenShareStreams = Object.values(participant.videoStreams).filter(i => i.mediaStreamType === 'ScreenSharing');
        for (const stream of screenShareStreams) {
            if (stream.view) {
                callClient.disposeView(call.id, participant.identifier, stream);
            }
        }
    });
    const onDisposeLocalScreenShareStreamView = () => __awaiter(void 0, void 0, void 0, function* () {
        if (!call) {
            return;
        }
        const callState = getCallStateIfExist(callClient.getState(), call.id);
        if (!callState) {
            return;
        }
        const screenShareStream = callState === null || callState === void 0 ? void 0 : callState.localVideoStreams.find(item => item.mediaStreamType === 'ScreenSharing');
        if (screenShareStream && screenShareStream.view) {
            callClient.disposeView(call.id, undefined, screenShareStream);
        }
    });
    const onDisposeLocalStreamView = () => __awaiter(void 0, void 0, void 0, function* () {
        // If the user is currently in a call, dispose of the local stream view attached to that call.
        const callState = call && callClient.getState().calls[call.id];
        const localStream = callState === null || callState === void 0 ? void 0 : callState.localVideoStreams.find(item => item.mediaStreamType === 'Video');
        if (call && callState && localStream) {
            callClient.disposeView(call.id, undefined, localStream);
        }
        // If the user is not in a call we currently assume any unparented view is a LocalPreview and stop all
        // since those are only used for LocalPreview currently.
        // TODO: we need to remember which LocalVideoStream was used for LocalPreview and dispose that one.
        yield disposeAllLocalPreviewViews(callClient);
    });
    const onSendDtmfTone = (dtmfTone) => __awaiter(void 0, void 0, void 0, function* () { return yield (call === null || call === void 0 ? void 0 : call.sendDtmf(dtmfTone)); });
    const notImplemented = () => {
        throw new Error('Not implemented, cannot call a method from an abstract object');
    };
    const onRemoveVideoBackgroundEffects = () => __awaiter(void 0, void 0, void 0, function* () {
        const stream = (call === null || call === void 0 ? void 0 : call.localVideoStreams.find(stream => stream.mediaStreamType === 'Video')) || (deviceManager === null || deviceManager === void 0 ? void 0 : deviceManager.getUnparentedVideoStreams().find(stream => stream.mediaStreamType === 'Video'));
        if (stream) {
            if (!(options === null || options === void 0 ? void 0 : options.onResolveVideoBackgroundEffectsDependency)) {
                throw new Error(`Video background effects dependency not resolved`);
            }
            else {
                return stream.feature(Features.VideoEffects).stopEffects();
            }
        }
    });
    const onBlurVideoBackground = (backgroundBlurConfig) => __awaiter(void 0, void 0, void 0, function* () {
        var _d;
        const stream = (call === null || call === void 0 ? void 0 : call.localVideoStreams.find(stream => stream.mediaStreamType === 'Video')) || (deviceManager === null || deviceManager === void 0 ? void 0 : deviceManager.getUnparentedVideoStreams().find(stream => stream.mediaStreamType === 'Video'));
        if (stream) {
            if (!(options === null || options === void 0 ? void 0 : options.onResolveVideoBackgroundEffectsDependency)) {
                throw new Error(`Video background effects dependency not resolved`);
            }
            const createEffect = (options === null || options === void 0 ? void 0 : options.onResolveVideoBackgroundEffectsDependency) && ((_d = (yield options.onResolveVideoBackgroundEffectsDependency())) === null || _d === void 0 ? void 0 : _d.createBackgroundBlurEffect);
            return createEffect && stream.feature(Features.VideoEffects).startEffects(createEffect(backgroundBlurConfig));
        }
    });
    const onReplaceVideoBackground = (backgroundReplacementConfig) => __awaiter(void 0, void 0, void 0, function* () {
        var _e;
        const stream = (call === null || call === void 0 ? void 0 : call.localVideoStreams.find(stream => stream.mediaStreamType === 'Video')) || (deviceManager === null || deviceManager === void 0 ? void 0 : deviceManager.getUnparentedVideoStreams().find(stream => stream.mediaStreamType === 'Video'));
        if (stream) {
            if (!(options === null || options === void 0 ? void 0 : options.onResolveVideoBackgroundEffectsDependency)) {
                throw new Error(`Video background effects dependency not resolved`);
            }
            const createEffect = (options === null || options === void 0 ? void 0 : options.onResolveVideoBackgroundEffectsDependency) && ((_e = (yield options.onResolveVideoBackgroundEffectsDependency())) === null || _e === void 0 ? void 0 : _e.createBackgroundReplacementEffect);
            return createEffect && stream.feature(Features.VideoEffects).startEffects(createEffect(backgroundReplacementConfig));
        }
    });
    const onStartNoiseSuppressionEffect = () => __awaiter(void 0, void 0, void 0, function* () {
        var _f;
        const audioEffects = (options === null || options === void 0 ? void 0 : options.onResolveDeepNoiseSuppressionDependency) && ((_f = (yield options.onResolveDeepNoiseSuppressionDependency())) === null || _f === void 0 ? void 0 : _f.deepNoiseSuppressionEffect);
        const stream = call === null || call === void 0 ? void 0 : call.localAudioStreams.find(stream => stream.mediaStreamType === 'Audio');
        if (stream && audioEffects && audioEffects.noiseSuppression) {
            const audioEffectsFeature = stream.feature(Features.AudioEffects);
            const isNoiseSuppressionSupported = yield audioEffectsFeature.isSupported(audioEffects.noiseSuppression);
            if (isNoiseSuppressionSupported) {
                return yield audioEffectsFeature.startEffects(audioEffects);
            }
            else {
                console.warn('Deep Noise Suppression is not supported on this platform.');
            }
        }
    });
    const onStopNoiseSuppressionEffect = () => __awaiter(void 0, void 0, void 0, function* () {
        const stream = call === null || call === void 0 ? void 0 : call.localAudioStreams.find(stream => stream.mediaStreamType === 'Audio');
        if (stream && (options === null || options === void 0 ? void 0 : options.onResolveDeepNoiseSuppressionDependency)) {
            const audioEffects = {
                noiseSuppression: true
            };
            return yield stream.feature(Features.AudioEffects).stopEffects(audioEffects);
        }
    });
    const onStartCaptions = (options) => __awaiter(void 0, void 0, void 0, function* () {
        const captionsFeature = call === null || call === void 0 ? void 0 : call.feature(Features.Captions).captions;
        yield (captionsFeature === null || captionsFeature === void 0 ? void 0 : captionsFeature.startCaptions(options));
    });
    const onStopCaptions = () => __awaiter(void 0, void 0, void 0, function* () {
        const captionsFeature = call === null || call === void 0 ? void 0 : call.feature(Features.Captions).captions;
        yield (captionsFeature === null || captionsFeature === void 0 ? void 0 : captionsFeature.stopCaptions());
    });
    const onSetSpokenLanguage = (language) => __awaiter(void 0, void 0, void 0, function* () {
        const captionsFeature = call === null || call === void 0 ? void 0 : call.feature(Features.Captions).captions;
        yield (captionsFeature === null || captionsFeature === void 0 ? void 0 : captionsFeature.setSpokenLanguage(language));
    });
    const onSetCaptionLanguage = (language) => __awaiter(void 0, void 0, void 0, function* () {
        const captionsFeature = call === null || call === void 0 ? void 0 : call.feature(Features.Captions).captions;
        yield captionsFeature.setCaptionLanguage(language);
    });
    const onSendRealTimeText = (text, isFinalized) => __awaiter(void 0, void 0, void 0, function* () {
        const realTimeTextFeature = call === null || call === void 0 ? void 0 : call.feature(Features.RealTimeText);
        yield (realTimeTextFeature === null || realTimeTextFeature === void 0 ? void 0 : realTimeTextFeature.sendRealTimeText(text, isFinalized));
    });
    const onSubmitSurvey = (survey) => __awaiter(void 0, void 0, void 0, function* () { return yield (call === null || call === void 0 ? void 0 : call.feature(Features.CallSurvey).submitSurvey(survey)); });
    const onStartSpotlight = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).startSpotlight(participants));
    });
    const onStopSpotlight = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).stopSpotlight(participants));
    });
    const onStopAllSpotlight = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).stopAllSpotlight());
    });
    const onMuteParticipant = (userId) => __awaiter(void 0, void 0, void 0, function* () {
        if (call === null || call === void 0 ? void 0 : call.remoteParticipants) {
            call === null || call === void 0 ? void 0 : call.remoteParticipants.forEach((participant) => __awaiter(void 0, void 0, void 0, function* () {
                // Using toFlatCommunicationIdentifier to convert the CommunicationIdentifier to string
                // as _toCommunicationIdentifier(userId) comparison to participant.identifier did not work for this case
                if (toFlatCommunicationIdentifier(participant.identifier) === userId) {
                    yield participant.mute();
                }
            }));
        }
    });
    const onMuteAllRemoteParticipants = () => __awaiter(void 0, void 0, void 0, function* () {
        call === null || call === void 0 ? void 0 : call.muteAllRemoteParticipants();
    });
    const canStartSpotlight = call === null || call === void 0 ? void 0 : call.feature(Features.Capabilities).capabilities.spotlightParticipant.isPresent;
    const canRemoveSpotlight = call === null || call === void 0 ? void 0 : call.feature(Features.Capabilities).capabilities.removeParticipantsSpotlight.isPresent;
    const onStartLocalSpotlight = canStartSpotlight ? () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).startSpotlight());
    }) : undefined;
    const onStopLocalSpotlight = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).stopSpotlight());
    });
    const onStartRemoteSpotlight = canStartSpotlight ? (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).startSpotlight(participants));
    }) : undefined;
    const onStopRemoteSpotlight = canRemoveSpotlight ? (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.Spotlight).stopSpotlight(participants));
    }) : undefined;
    const onCreateTogetherModeStreamView = (...args_3) => __awaiter(void 0, [...args_3], void 0, function* (options = {
        scalingMode: 'Fit',
        isMirrored: false,
        viewKind: 'main'
    }) {
        if (!call) {
            return;
        }
        const callState = callClient.getState().calls[call.id];
        if (!callState) {
            return;
        }
        const togetherModeStreams = callState.togetherMode.streams;
        const togetherModeCreateViewResult = {};
        const mainVideoStream = togetherModeStreams.mainVideoStream;
        if (mainVideoStream && mainVideoStream.isAvailable && !mainVideoStream.view) {
            const createViewResult = yield callClient.createView(call.id, undefined, mainVideoStream, options);
            // SDK currently only supports 1 Video media stream type
            togetherModeCreateViewResult.mainVideoView = (createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view) ? {
                view: createViewResult === null || createViewResult === void 0 ? void 0 : createViewResult.view
            } : undefined;
        }
        return togetherModeCreateViewResult;
    });
    const onDisposeTogetherModeStreamView = () => __awaiter(void 0, void 0, void 0, function* () {
        if (!call) {
            return;
        }
        const callState = callClient.getState().calls[call.id];
        if (!callState) {
            throw new Error(`Call Not Found: ${call.id}`);
        }
        const togetherModeStreams = callState.togetherMode.streams;
        if (!togetherModeStreams.mainVideoStream) {
            return;
        }
        if (togetherModeStreams.mainVideoStream.view) {
            callClient.disposeView(call.id, undefined, togetherModeStreams.mainVideoStream);
        }
    });
    const onSetTogetherModeSceneSize = (width, height) => {
        const togetherModeFeature = call === null || call === void 0 ? void 0 : call.feature(Features.TogetherMode);
        if (togetherModeFeature) {
            togetherModeFeature.sceneSize = {
                width,
                height
            };
        }
    };
    const onForbidAudio = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).forbidAudio(participants));
    });
    const onPermitAudio = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).permitAudio(participants));
    });
    const onForbidOthersAudio = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).forbidOthersAudio());
    });
    const onPermitOthersAudio = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).permitOthersAudio());
    });
    const onForbidVideo = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).forbidVideo(participants));
    });
    const onPermitVideo = (userIds) => __awaiter(void 0, void 0, void 0, function* () {
        const participants = userIds === null || userIds === void 0 ? void 0 : userIds.map(userId => _toCommunicationIdentifier(userId));
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).permitVideo(participants));
    });
    const onForbidOthersVideo = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).forbidOthersVideo());
    });
    const onPermitOthersVideo = () => __awaiter(void 0, void 0, void 0, function* () {
        yield (call === null || call === void 0 ? void 0 : call.feature(Features.MediaAccess).permitOthersVideo());
    });
    return {
        onHangUp,
        onToggleHold,
        onSelectCamera,
        onSelectMicrophone,
        onSelectSpeaker,
        onStartScreenShare,
        onStopScreenShare,
        onToggleCamera,
        onToggleMicrophone,
        onToggleScreenShare,
        onCreateLocalStreamView,
        onCreateRemoteStreamView,
        onStartLocalVideo,
        onDisposeRemoteStreamView,
        onDisposeLocalStreamView,
        onDisposeRemoteScreenShareStreamView,
        onDisposeLocalScreenShareStreamView,
        onDisposeRemoteVideoStreamView,
        onRaiseHand,
        onLowerHand,
        onToggleRaiseHand,
        onReactionClick,
        onAddParticipant: notImplemented,
        onRemoveParticipant: notImplemented,
        onStartCall: notImplemented,
        onSendDtmfTone,
        onRemoveVideoBackgroundEffects,
        onBlurVideoBackground,
        onReplaceVideoBackground,
        onStartNoiseSuppressionEffect,
        onStopNoiseSuppressionEffect,
        onStartCaptions,
        onStopCaptions,
        onSetCaptionLanguage,
        onSetSpokenLanguage,
        onSubmitSurvey,
        onStartSpotlight,
        onStopSpotlight,
        onStopAllSpotlight,
        onStartLocalSpotlight,
        onStopLocalSpotlight,
        onStartRemoteSpotlight,
        onStopRemoteSpotlight,
        onMuteParticipant,
        onMuteAllRemoteParticipants,
        onAcceptCall: notImplemented,
        onRejectCall: notImplemented,
        onCreateTogetherModeStreamView,
        onStartTogetherMode: notImplemented,
        onSetTogetherModeSceneSize,
        onDisposeTogetherModeStreamView,
        onForbidAudio,
        onPermitAudio,
        onForbidOthersAudio,
        onPermitOthersAudio,
        onForbidVideo,
        onPermitVideo,
        onForbidOthersVideo,
        onPermitOthersVideo,
        onSendRealTimeText
    };
});
//# sourceMappingURL=createCommonHandlers.js.map