"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const mediasoup_client_1 = require("mediasoup-client");
const RoomConfig_1 = require("../../RoomConfig");
const browser_utils_1 = require("../../utils/browser-utils");
const participantSlice_1 = require("../../store/participantSlice");
const roomSlice_1 = require("../../store/roomSlice");
const consumersSlice_1 = require("../../store/consumersSlice");
const peersSlice_1 = require("../../store/peersSlice");
class MediaRoomClient {
    constructor(peerId, socketObj, dispatch, autoTune = true) {
        this.socketObj = socketObj;
        this.peerid = peerId;
        this.dispatch = dispatch;
        this.device = new mediasoup_client_1.Device();
        this.consumers = new Map();
        this.legacyMode = true;
        this.closedConsumers = [];
        this.handleSockets = this.handleSockets.bind(this);
    }
    handleSockets(message, callBack) {
        var _a, _b, _c;
        if (!message) {
            return;
        }
        switch (message.type) {
            case 'websocket/new-consumer':
                this.createConsumer(message.payload, callBack);
                break;
            case 'websocket/consumer-closed':
                this.closeConsumer(message.payload);
                break;
            case 'websocket/consumer-paused':
                this.pauseConsumer(message.payload);
                break;
            case 'websocket/consumer-resumed':
                this.resumeConsumer(message.payload);
                break;
            case 'websocket/recording-started':
                this.setRecordingStatus(true);
                break;
            case 'websocket/recording-stopped':
                this.setRecordingStatus(false);
                break;
            case 'websocket/room-message':
                if (((_a = message.payload) === null || _a === void 0 ? void 0 : _a.type) === 'hand-raise-rejected' && ((_c = (_b = message.payload) === null || _b === void 0 ? void 0 : _b.payload) === null || _c === void 0 ? void 0 : _c.id) === this.peerid) {
                    this.dispatch(participantSlice_1.raiseHandsRejectedAction());
                }
                break;
            default:
                break;
        }
    }
    loadRouter() {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            if (this.recvTransport || this.sendTransport) {
                this.stopAllTransports();
            }
            this.dispatch(roomSlice_1.setRouterFetchState(roomSlice_1.MediaStates.CONNECTING));
            const routerMessage = yield ((_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({
                type: 'getRouterRtpCapabilities',
            }));
            if (this.device.loaded) {
                this.device = new mediasoup_client_1.Device();
            }
            const routerRtpCapabilities = routerMessage === null || routerMessage === void 0 ? void 0 : routerMessage.payload;
            if (routerRtpCapabilities) {
                routerRtpCapabilities.headerExtensions =
                    routerRtpCapabilities.headerExtensions.filter((ext) => ext.uri !== 'urn:3gpp:video-orientation');
            }
            this.socketObj.emitter.on('socket', this.handleSockets);
            return this.device.load({ routerRtpCapabilities });
        });
    }
    getRoomState() {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            this.dispatch(roomSlice_1.setRoomDisplayTitleState(roomSlice_1.MediaStates.CONNECTING));
            yield ((_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({ type: 'getRoomState' }));
            this.dispatch(roomSlice_1.setRoomDisplayTitleState(roomSlice_1.MediaStates.LOADED));
        });
    }
    changeRoomDisplayTitle(displayTitle) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            this.dispatch(roomSlice_1.setRoomDisplayTitleState(roomSlice_1.MediaStates.CONNECTING));
            yield ((_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({
                type: 'changeDisplayTitle',
                payload: {
                    displayTitle,
                },
            }));
            this.dispatch(roomSlice_1.setRoomDisplayTitleState(roomSlice_1.MediaStates.LOADED));
        });
    }
    joinRoom(displayName, audioMuted, roomTitle) {
        return __awaiter(this, void 0, void 0, function* () {
            const response = yield this.socketObj.sendMessage({
                type: 'joinRoom',
                payload: {
                    device: browser_utils_1.getDeviceInfo(),
                    displayName,
                    rtpCapabilities: this.device.rtpCapabilities,
                    isLegacy: this.legacyMode,
                    audioMuted,
                },
            });
            this.legacyMode = response.payload.legacyMode || this.legacyMode;
            if (roomTitle && roomTitle !== '') {
                yield this.changeRoomDisplayTitle(roomTitle);
            }
            this.dispatch(peersSlice_1.roomJoined(response.payload));
            return response;
        });
    }
    createRecvTransport() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.socketObj === undefined) {
                return;
            }
            const payload = {
                forceTcp: false,
                producing: false,
                consuming: true,
            };
            const transportParams = (yield this.socketObj.sendMessage({
                type: 'createWebRtcTransport',
                payload,
            })).payload;
            this.recvTransport = this.device.createRecvTransport(Object.assign(Object.assign({}, transportParams), { iceServers: RoomConfig_1.ICE_SERVERS, proprietaryConstraints: RoomConfig_1.PC_PROPRIETARY_CONSTRAINTS }));
            // Keep reference to recvTransport in window for automated tests to extract stats
            /* if (process.env.REACT_APP_STAGE !== 'production') {
                Object.assign(window, { recvTransport: this.recvTransport });
            } */
            this.recvTransport.on('connect', ({ dtlsParameters }, callback, errback) => { var _a, _b; return (_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({
                type: 'connectWebRtcTransport',
                payload: {
                    transportId: (_b = this.recvTransport) === null || _b === void 0 ? void 0 : _b.id,
                    dtlsParameters,
                },
            }, (message) => {
                if (!message.payload.error) {
                    callback();
                }
                else {
                    errback();
                }
            }); });
        });
    }
    createSendTransport() {
        return __awaiter(this, void 0, void 0, function* () {
            const payload = {
                forceTcp: false,
                producing: true,
                consuming: false,
            };
            const transportParams = (yield this.socketObj.sendMessage({
                type: 'createWebRtcTransport',
                payload,
            })).payload;
            this.sendTransport = this.device.createSendTransport(Object.assign(Object.assign({}, transportParams), { iceServers: RoomConfig_1.ICE_SERVERS, proprietaryConstraints: RoomConfig_1.PC_PROPRIETARY_CONSTRAINTS }));
            // Keep reference to sendTransport in window for automated tests to extract stats
            /* if (process.env.REACT_APP_STAGE !== 'production') {
                Object.assign(window, { sendTransport: this.sendTransport });
            }
     */
            if (this.sendTransport !== undefined) {
                this.sendTransport.on('connect', ({ dtlsParameters }, callback, errback) => { var _a, _b; return (_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({
                    type: 'connectWebRtcTransport',
                    payload: {
                        transportId: (_b = this.sendTransport) === null || _b === void 0 ? void 0 : _b.id,
                        dtlsParameters,
                    },
                }, (message) => {
                    if (!message.payload.error) {
                        callback();
                    }
                    else {
                        errback();
                    }
                }); });
                this.sendTransport.on('produce', ({ kind, rtpParameters, appData }, callback, errback) => { var _a, _b; return (_a = this.socketObj) === null || _a === void 0 ? void 0 : _a.sendMessage({
                    type: 'produce',
                    payload: {
                        transportId: (_b = this.sendTransport) === null || _b === void 0 ? void 0 : _b.id,
                        kind,
                        rtpParameters,
                        appData,
                    },
                }, (message) => {
                    if (!message.payload.error) {
                        callback({ id: message.payload.id });
                    }
                    else {
                        errback(message.payload.error);
                    }
                }); });
            }
        });
    }
    createConsumer(request, callback) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.recvTransport === undefined || this.recvTransport.closed) {
                return;
            }
            const { peerId, producerId, id, kind, rtpParameters, type, appData, producerPaused, remotelyPaused, priority, } = request;
            const consumer = yield this.recvTransport.consume({
                id,
                producerId,
                kind,
                rtpParameters,
                appData: Object.assign(Object.assign({}, appData), { peerId }),
            });
            if (!this.closedConsumers.includes(id)) {
                this.consumers.set(consumer.id, consumer);
                this.dispatch(consumersSlice_1.addConsumer({
                    id,
                    peerId,
                    type,
                    kind,
                    remotelyPaused,
                    producerPaused,
                    priority,
                    appData: consumer.appData,
                    score: 10,
                    producerScore: 10,
                }));
            }
            else {
                consumer.close();
            }
            if (callback) {
                callback('Done');
            }
        });
    }
    closeConsumer(request) {
        return __awaiter(this, void 0, void 0, function* () {
            const { id } = request;
            const consumer = this.consumers.get(id);
            if (!consumer) {
                console.log('Consumer not found');
                this.closedConsumers.push(id);
                return;
            }
            consumer.close();
            this.consumers.delete(id);
        });
    }
    pauseConsumer(request) {
        return __awaiter(this, void 0, void 0, function* () {
            const { id } = request;
            const consumer = this.consumers.get(id);
            if (!consumer)
                return;
            consumer.pause();
        });
    }
    resumeConsumer(request) {
        return __awaiter(this, void 0, void 0, function* () {
            const { id } = request;
            const consumer = this.consumers.get(id);
            if (!consumer)
                return;
            consumer.resume();
        });
    }
    shareWebcam(videoTrack) {
        return __awaiter(this, void 0, void 0, function* () {
            if (videoTrack === undefined) {
                return;
            }
            if (this.webCamProducer) {
                yield this.removeProducer('webCamProducer', false);
            }
            let simulcast = RoomConfig_1.WEBCAM_SIMULCAST_ENCODINGS_VGA;
            const vp9Codec = this.device.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/vp9') || undefined;
            const vp8Codec = this.device.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/vp8') || undefined;
            if (vp9Codec && !this.legacyMode) {
                simulcast = RoomConfig_1.WEBCAM_KSVC_ENCODINGS;
            }
            this.dispatch(participantSlice_1.setVideoOff(false));
            this.dispatch(participantSlice_1.setVideoProduction(roomSlice_1.MediaStates.FETCHED));
            const getCodec = () => (this.legacyMode ? vp8Codec : vp9Codec);
            const producerOptions = {
                track: videoTrack,
                encodings: simulcast,
                codecOptions: {
                    videoGoogleStartBitrate: 1000,
                },
                codec: getCodec(),
                appData: {
                    screenShare: false,
                },
                stopTracks: false,
            };
            const onDisconnect = () => {
                this.disableWebcam();
            };
            yield this.createProducer('webCamProducer', producerOptions, onDisconnect);
            this.dispatch(participantSlice_1.setVideoProduction(roomSlice_1.MediaStates.LOADED));
        });
    }
    shareScreen(tracks) {
        return __awaiter(this, void 0, void 0, function* () {
            const { video: videoTrack, audio: audioTrack } = tracks;
            if (videoTrack === undefined) {
                return;
            }
            let simulcast = RoomConfig_1.SCREEN_SHARING_SIMULCAST_ENCODINGS;
            const vp9Codec = this.device.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/vp9') || null;
            if (vp9Codec !== null && !this.legacyMode) {
                simulcast = RoomConfig_1.SCREEN_SHARING_SVC_ENCODINGS;
            }
            this.dispatch(participantSlice_1.setScreenSharing(true));
            this.dispatch(participantSlice_1.setScreenSharingProduction(roomSlice_1.MediaStates.FETCHED));
            const producerOptions = {
                track: videoTrack,
                encodings: simulcast,
                codecOptions: {
                    videoGoogleStartBitrate: 1000,
                },
                codec: this.legacyMode ? undefined : vp9Codec,
                appData: {
                    screenShare: true,
                },
                stopTracks: false,
            };
            const onDisconnect = () => {
                this.disableScreenShare();
            };
            yield this.createProducer('screenShareVideoProducer', producerOptions, onDisconnect);
            if (audioTrack) {
                const audioProducerOptions = {
                    track: audioTrack,
                    codecOptions: {
                        opusDtx: true,
                        opusStereo: false,
                        opusFec: true,
                    },
                    appData: {
                        screenShare: true,
                    },
                    stopTracks: false,
                };
                const onDisconnect = () => {
                    // DO nothing as video will already stop the track
                };
                yield this.createProducer('screenShareAudioProducer', audioProducerOptions, onDisconnect);
            }
            this.dispatch(participantSlice_1.setScreenSharingProduction(roomSlice_1.MediaStates.LOADED));
        });
    }
    shareMic(audioTrack, defaultState) {
        return __awaiter(this, void 0, void 0, function* () {
            if (audioTrack === undefined) {
                return;
            }
            if (this.micProducer) {
                yield this.removeProducer('micProducer', false);
            }
            if (defaultState) {
                this.dispatch(participantSlice_1.setAudioMuted(defaultState || false));
            }
            this.dispatch(participantSlice_1.setAudioProduction(roomSlice_1.MediaStates.FETCHED));
            const producerOptions = {
                track: audioTrack,
                codecOptions: {
                    opusDtx: true,
                    opusStereo: false,
                    opusFec: true,
                },
                stopTracks: false,
            };
            const onDisconnect = () => {
                this.disableMic();
            };
            yield this.createProducer('micProducer', producerOptions, onDisconnect);
            this.dispatch(participantSlice_1.setAudioProduction(roomSlice_1.MediaStates.LOADED));
        });
    }
    pauseMic() {
        this.dispatch(participantSlice_1.setAudioMuted(true));
        this.micProducer.pause();
    }
    pauseWebcam() {
        this.dispatch(participantSlice_1.setVideoOff(true));
        this.webCamProducer.pause();
    }
    resumeMic() {
        this.dispatch(participantSlice_1.setAudioMuted(false));
        this.micProducer.resume();
    }
    resumeWebcam() {
        this.dispatch(participantSlice_1.setVideoOff(false));
        this.webCamProducer.resume();
    }
    disableWebcam(data) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!data || !(data === null || data === void 0 ? void 0 : data.silent)) {
                this.dispatch(participantSlice_1.setVideoOff(true));
            }
            yield this.removeProducer('webCamProducer');
            this.dispatch(participantSlice_1.setVideoProduction(roomSlice_1.MediaStates.NOT_STARTED));
        });
    }
    disableMic(data) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!data || !(data === null || data === void 0 ? void 0 : data.silent)) {
                this.dispatch(participantSlice_1.setAudioMuted(true));
            }
            yield this.removeProducer('micProducer');
            this.dispatch(participantSlice_1.setAudioProduction(roomSlice_1.MediaStates.NOT_STARTED));
        });
    }
    disableScreenShare() {
        return __awaiter(this, void 0, void 0, function* () {
            this.dispatch(participantSlice_1.setScreenSharingProduction(roomSlice_1.MediaStates.NOT_STARTED));
            this.dispatch(participantSlice_1.setScreenSharing(false));
            yield this.removeProducer('screenShareVideoProducer');
            yield this.removeProducer('screenShareAudioProducer');
        });
    }
    stopAllProducers() {
        if (this.webCamProducer) {
            this.removeProducer('webCamProducer');
        }
        if (this.screenShareVideoProducer) {
            this.removeProducer('screenShareVideoProducer');
        }
        if (this.screenShareAudioProducer) {
            this.removeProducer('screenShareAudioProducer');
        }
        if (this.micProducer) {
            this.removeProducer('micProducer');
        }
    }
    stopAllTransports() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.sendTransport) {
                try {
                    this.sendTransport.close();
                }
                catch (_a) {
                    console.log('awaitqueue error');
                }
                this.sendTransport = null;
            }
            if (this.recvTransport) {
                try {
                    this.recvTransport.close();
                }
                catch (_b) {
                    console.log('awaitqueue error');
                }
                this.recvTransport = null;
            }
            this.dispatch(roomSlice_1.setRouterFetchState(roomSlice_1.MediaStates.NOT_STARTED));
        });
    }
    resetVideoProducers(videoTrack, screenShareTrack) {
        return __awaiter(this, void 0, void 0, function* () {
            if (videoTrack) {
                yield this.removeProducer('webCamProducer', false);
                this.shareWebcam(videoTrack);
            }
            if (screenShareTrack) {
                yield this.removeProducer('screenShareVideoProducer', false);
                this.shareScreen({ video: screenShareTrack });
            }
        });
    }
    createProducer(producer, producerOptions, onDisconnect) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.sendTransport === undefined || this.sendTransport.closed) {
                return;
            }
            this[producer] = yield this.sendTransport.produce(producerOptions);
            this[producer].on('transportclose', () => {
                this[producer] = undefined;
            });
            this[producer].on('trackended', () => {
                this.socketObj.sendMessage({
                    type: 'closeProducer',
                    payload: { producerId: this[producer].id },
                });
                onDisconnect();
            });
        });
    }
    removeProducer(producer, stopTrack = true) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this[producer] || this[producer].closed) {
                return;
            }
            if (stopTrack) {
                this[producer].track.stop();
            }
            this[producer].close();
            yield this.socketObj.sendMessage({
                type: 'closeProducer',
                payload: { producerId: this[producer].id },
            });
            this[producer] = null;
        });
    }
    sendMessage(message) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'chatMessage',
                payload: message,
            });
        });
    }
    getMesssages() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'getChatMessages' });
        });
    }
    getPolls() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'getPolls' });
        });
    }
    newPoll(poll) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'newPoll', payload: poll });
        });
    }
    votePoll(poll) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'votePoll', payload: poll });
        });
    }
    acceptWaitingRequest(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'acceptWaitingRequest',
                payload: { id: peerId },
            });
        });
    }
    rejectWaitingRequest(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'rejectWaitingRequest',
                payload: { id: peerId },
            });
        });
    }
    muteAll(allowUnMute) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'muteAllNew',
                payload: { allowUnMute },
            });
        });
    }
    rejectRequestToJoinStage(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'rejectRequestToJoinStage',
                payload: { id: peerId },
            });
        });
    }
    muteAllVideo() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'muteAllVideo' });
        });
    }
    pin(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'pinPeer',
                payload: { id: peerId },
            });
        });
    }
    disableAudio(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'disableAudioPeerNew',
                payload: { id: peerId },
            });
        });
    }
    disableVideo(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'disableVideoPeer',
                payload: { id: peerId },
            });
        });
    }
    kickAll() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'kickAll' });
        });
    }
    kick(peerId) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'kick', payload: { id: peerId } });
        });
    }
    getPage(pageNum) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'getPage', payload: { pageNum } });
        });
    }
    // eslint-disable-next-line class-methods-use-this
    setRecordingStatus(status) {
        return __awaiter(this, void 0, void 0, function* () {
            // @ts-ignore
            window.triggerDyteRecording = status;
        });
    }
    muteSelf() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'muteSelf' });
        });
    }
    unmuteSelf() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'unmuteSelf' });
        });
    }
    requestToJoinStage(requestToJoinType) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'requestToJoinStage',
                payload: requestToJoinType,
            });
        });
    }
    acceptAllRequestToJoinStage(peers) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'acceptAllRequestToJoinStage',
                payload: peers,
            });
        });
    }
    removePeerFromStage(peerId, type) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'removePeerFromStage',
                payload: { id: peerId, type },
            });
        });
    }
    startPresenting() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'startPresenting' });
        });
    }
    stopPresenting() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({ type: 'stopPresenting' });
        });
    }
    updatePreset(peerId, presetName) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.socketObj.sendMessage({
                type: 'updatePeerPreset',
                payload: { id: peerId, presetName },
            });
        });
    }
}
exports.default = MediaRoomClient;
//# sourceMappingURL=MediaRoomClient.js.map