"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNewStreamWithTrack = exports.checkAllTracksHaveEnded = exports.checkHasTransceiverFunctionality = exports.attachAudioMedia = exports.createUniqueAudioMediaElement = exports.getOrCreateAudioMediaElement = exports._hasTransceiverFunctionality = void 0;
exports.logDeviceChange = logDeviceChange;
const GC_AUDIO_EL_CLASS = '__gc-webrtc-inbound';
/**
 * Select or create the `audio.__gc-webrtc-inbound` element
 * @deprecated use `createUniqueAudioMediaElement()` instead
 */
const getOrCreateAudioMediaElement = function (className = GC_AUDIO_EL_CLASS) {
    const existing = document.querySelector(`audio.${className}`);
    if (existing) {
        return existing;
    }
    const audio = document.createElement('audio');
    audio.classList.add(className);
    audio.style.visibility = 'hidden';
    document.body.append(audio);
    return audio;
};
exports.getOrCreateAudioMediaElement = getOrCreateAudioMediaElement;
const createUniqueAudioMediaElement = function () {
    const uuid = globalThis.crypto.randomUUID();
    const className = `${GC_AUDIO_EL_CLASS}-${uuid}`;
    return (0, exports.getOrCreateAudioMediaElement)(className);
};
exports.createUniqueAudioMediaElement = createUniqueAudioMediaElement;
/**
 * Attach an audio stream to the given audio element.
 *  If no element is provided, a new element will be
 *  created and attached to the DOM.
 *
 * @param sdk sdk instance
 * @param stream audio stream to attach
 * @param audioElement optional audio element to attach stream to
 * @param ids session and/or conversation Ids for logging
 */
const attachAudioMedia = function (sdk, stream, volume = 100, audioElement, ids) {
    if (!audioElement) {
        audioElement = (0, exports.createUniqueAudioMediaElement)();
    }
    if (audioElement.srcObject) {
        sdk.logger.warn('Attaching media to an audio element that already has a srcObject. This can result is audio issues.', ids);
    }
    // Volume must be between 0 and 1 for html elements
    audioElement.volume = volume / 100;
    audioElement.autoplay = true;
    audioElement.srcObject = stream;
    return audioElement;
};
exports.attachAudioMedia = attachAudioMedia;
/**
 * Utility method to check if the browser supports
 *  RTC transceivers.
 */
const checkHasTransceiverFunctionality = function () {
    if (typeof exports._hasTransceiverFunctionality === 'boolean') {
        return exports._hasTransceiverFunctionality;
    }
    try {
        /* make sure we are capable to use tracks */
        const dummyRtcPeerConnection = new RTCPeerConnection();
        /**
         * HACK: FF will always throw a `DOMException: RTCPeerConnection is gone (did you enter Offline mode?)`
         *  after we close the PC. The issue's root cause lies within a polyfill. The easiest (hackiest) solution
         *  is to just swallow the error thrown from this particular PC (since we don't really care about errors here).
         */
        const origGetStats = dummyRtcPeerConnection.getStats.bind(dummyRtcPeerConnection);
        /* istanbul ignore next */
        dummyRtcPeerConnection.getStats = (selector) => {
            return origGetStats(selector).catch(e => {
                if (e.name === 'InvalidStateError' && e.message === 'RTCPeerConnection is gone (did you enter Offline mode?)') {
                    return {};
                }
                throw e;
            });
        };
        /* if this function exists we should be good */
        exports._hasTransceiverFunctionality = !!dummyRtcPeerConnection.getTransceivers;
        dummyRtcPeerConnection.close();
    }
    catch (err) {
        exports._hasTransceiverFunctionality = false;
    }
    return exports._hasTransceiverFunctionality;
};
exports.checkHasTransceiverFunctionality = checkHasTransceiverFunctionality;
/**
 * Utility method to check all tracks on a given stream
 *  to determine if all tracks have ended.
 * @param stream to check tracks on
 */
const checkAllTracksHaveEnded = function (stream) {
    let allTracksHaveEnded = true;
    stream.getTracks().forEach(function (t) {
        allTracksHaveEnded = t.readyState === 'ended' && allTracksHaveEnded;
    });
    return allTracksHaveEnded;
};
exports.checkAllTracksHaveEnded = checkAllTracksHaveEnded;
/**
 * Utility method to create a new stream and add
 *  the passed in track
 * @param track media track to add
 */
const createNewStreamWithTrack = function (track) {
    return new MediaStream([track]);
};
exports.createNewStreamWithTrack = createNewStreamWithTrack;
/**
 * Utility to log device changes. It will use the passed in `from` track _or_
 *  look up the currently used devices via the sender (based on labels) to see
 *  what device is currently in use. Then it will log out the device changing `to`
 *  as the new device.
 *
 * NOTE: if the system default is being used and then changes (which will force
 *  devices to be enumerated) the device will not be able to be looked up in the
 *  cached devices. So the caller will need to pass in the "old" system default(s)
 *  as `fromVideoTrack` and/or `fromAudioTrack`.
 *
 * @param sdk sdk instance
 * @param session session devices are changing for
 * @param action action taken
 * @param devicesChange devices changing to/from
 */
function logDeviceChange(sdk, session, action, devicesChange = {}) {
    let currentVideoTrack;
    let currentAudioTrack;
    const currentOutputDeviceId = session._outputAudioElement?.sinkId;
    const screenShareTrackId = session._screenShareStream?.getVideoTracks()[0]?.id;
    const pcSenders = session.peerConnection.getSenders().filter(s => s.track && s.track.id && s.track.id !== screenShareTrackId);
    /* grab the currect device being used */
    pcSenders.forEach(sender => {
        if (sender.track.kind === 'audio') {
            currentAudioTrack = sender.track;
        }
        else /* if (sender.track.kind === 'video') */ {
            currentVideoTrack = sender.track;
        }
    });
    const mediaState = sdk.media.getState();
    const details = {
        /* meta data */
        action,
        availableDevices: mediaState.devices,
        sessionId: session.id,
        conversationId: session.conversationId,
        /* the device being switched from and/or currently being used.
          if a track was passed in, we will assume the caller knows what it is doing
          and we will use that track for logging. Otherwise, we will look up the device */
        currentVideoDevice: devicesChange.fromVideoTrack
            ? { deviceId: undefined, groupId: undefined, label: devicesChange.fromVideoTrack.label }
            : sdk.media.findCachedDeviceByTrackLabelAndKind(currentVideoTrack),
        currentAudioDevice: devicesChange.fromAudioTrack
            ? { deviceId: undefined, groupId: undefined, label: devicesChange.fromAudioTrack.label }
            : sdk.media.findCachedDeviceByTrackLabelAndKind(currentAudioTrack),
        currentOutputDevice: sdk.media.findCachedOutputDeviceById(currentOutputDeviceId),
        /* current tracks */
        currentVideoTrack,
        currentAudioTrack,
        /* the device being switched to */
        newVideoDevice: sdk.media.findCachedDeviceByTrackLabelAndKind(devicesChange.toVideoTrack),
        newAudioDevice: sdk.media.findCachedDeviceByTrackLabelAndKind(devicesChange.toAudioTrack),
        newOutputDevice: sdk.media.findCachedOutputDeviceById(devicesChange.requestedOutputDeviceId),
        /* the track being switched to */
        newVideoTrack: devicesChange.toVideoTrack,
        newAudioTrack: devicesChange.toAudioTrack,
        /* potential media streamTracks we _want_ to switch to */
        requestedNewMediaStreamTracks: devicesChange.requestedNewMediaStream?.getTracks(),
        /* deviceIds requested */
        requestedOutputDeviceId: devicesChange.requestedOutputDeviceId,
        requestedVideoDeviceId: devicesChange.requestedVideoDeviceId,
        requestedAudioDeviceId: devicesChange.requestedAudioDeviceId,
        /* sdk defaults */
        sdkDefaultVideoDeviceId: sdk._config.defaults.videoDeviceId,
        sdkDefaultAudioDeviceId: sdk._config.defaults.audioDeviceId,
        sdkDefaultOutputDeviceId: sdk._config.defaults.outputDeviceId,
        /* other random stuff */
        currentAudioElementSinkId: currentOutputDeviceId,
        // TODO: these don't log in sumo as tracks...
        currentSessionSenderTracks: pcSenders.map(s => s.track),
        currentSessionReceiverTracks: session.peerConnection.getReceivers().filter(s => s.track && s.track.id).map(s => s.track),
        /* other potentially useful information to log */
        sessionVideoMute: session.videoMuted,
        sessionAudioMute: session.audioMuted,
        hasMicPermissions: mediaState.hasMicPermissions,
        hasCameraPermissions: mediaState.hasCameraPermissions,
        hasOutputDeviceSupport: mediaState.hasOutputDeviceSupport
    };
    const keysToIgnoreIfBlank = [
        'newVideoTrack',
        'newAudioTrack',
        'requestedAudioDeviceId',
        'requestedVideoDeviceId',
        'requestedOutputDeviceId',
        'requestedNewMediaStreamTracks'
    ];
    /* trim off parts of logs that aren't needed if they are blank */
    Object.keys(details)
        .filter(k => keysToIgnoreIfBlank.includes(k))
        .forEach(k => {
        if (details[k] === undefined) {
            delete details[k];
        }
    });
    sdk.logger.info('media devices changing for session', details);
}
//# sourceMappingURL=media-utils.js.map