import { Device } from 'mediasoup-client';
import { PC_PROPRIETARY_CONSTRAINTS, WEBCAM_KSVC_ENCODINGS, SCREEN_SHARING_SIMULCAST_ENCODINGS, SCREEN_SHARING_SVC_ENCODINGS, ICE_SERVERS, WEBCAM_SIMULCAST_ENCODINGS_VGA, } from '../../RoomConfig';
import { getDeviceInfo, } from '../../utils/browser-utils';
import { setVideoOff, setVideoProduction, setScreenSharing, setScreenSharingProduction, setAudioMuted, setAudioProduction, } from '../../store/participantSlice';
import { setRouterFetchState, MediaStates, setRoomDisplayTitleState, } from '../../store/roomSlice';
import { addConsumer } from '../../store/consumersSlice';
import { roomJoined } from '../../store/peersSlice';
export default class MediaRoomClient {
    constructor(peerId, socketObj, dispatch, autoTune = true) {
        this.socketObj = socketObj;
        this.peerid = peerId;
        this.dispatch = dispatch;
        this.device = new Device();
        this.consumers = new Map();
        this.legacyMode = true;
        this.closedConsumers = [];
        this.handleSockets = this.handleSockets.bind(this);
    }
    handleSockets(message, callBack) {
        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;
            default:
                break;
        }
    }
    async loadRouter() {
        if (this.recvTransport || this.sendTransport) {
            this.stopAllTransports();
        }
        this.dispatch(setRouterFetchState(MediaStates.CONNECTING));
        const routerMessage = await this.socketObj?.sendMessage({ type: 'getRouterRtpCapabilities' });
        if (this.device.loaded) {
            this.device = new Device();
        }
        const routerRtpCapabilities = 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 });
    }
    async getRoomState() {
        this.dispatch(setRoomDisplayTitleState(MediaStates.CONNECTING));
        await this.socketObj?.sendMessage({ type: 'getRoomState' });
        this.dispatch(setRoomDisplayTitleState(MediaStates.LOADED));
    }
    async changeRoomDisplayTitle(displayTitle) {
        this.dispatch(setRoomDisplayTitleState(MediaStates.CONNECTING));
        await this.socketObj?.sendMessage({
            type: 'changeDisplayTitle',
            payload: {
                displayTitle,
            },
        });
        this.dispatch(setRoomDisplayTitleState(MediaStates.LOADED));
    }
    async joinRoom(displayName, audioMuted, roomTitle) {
        const response = await this.socketObj.sendMessage({
            type: 'joinRoom',
            payload: {
                device: getDeviceInfo(),
                displayName,
                rtpCapabilities: this.device.rtpCapabilities,
                isLegacy: this.legacyMode,
                audioMuted,
            },
        });
        this.legacyMode = response.payload.legacyMode || this.legacyMode;
        if (roomTitle && roomTitle !== '') {
            await this.changeRoomDisplayTitle(roomTitle);
        }
        this.dispatch(roomJoined(response.payload));
        return response;
    }
    async createRecvTransport() {
        if (this.socketObj === undefined) {
            return;
        }
        const payload = {
            forceTcp: false,
            producing: false,
            consuming: true,
        };
        const transportParams = (await this.socketObj.sendMessage({ type: 'createWebRtcTransport', payload })).payload;
        this.recvTransport = this.device.createRecvTransport({
            ...transportParams,
            iceServers: ICE_SERVERS,
            proprietaryConstraints: 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) => this.socketObj?.sendMessage({
            type: 'connectWebRtcTransport',
            payload: {
                transportId: this.recvTransport?.id,
                dtlsParameters,
            },
        }, (message) => {
            if (!message.payload.error) {
                callback();
            }
            else {
                errback();
            }
        }));
    }
    async createSendTransport() {
        const payload = {
            forceTcp: false,
            producing: true,
            consuming: false,
        };
        const transportParams = (await this.socketObj.sendMessage({ type: 'createWebRtcTransport', payload })).payload;
        this.sendTransport = this.device.createSendTransport({
            ...transportParams,
            iceServers: ICE_SERVERS,
            proprietaryConstraints: 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) => this.socketObj?.sendMessage({
                type: 'connectWebRtcTransport',
                payload: {
                    transportId: this.sendTransport?.id,
                    dtlsParameters,
                },
            }, (message) => {
                if (!message.payload.error) {
                    callback();
                }
                else {
                    errback();
                }
            }));
            this.sendTransport.on('produce', ({ kind, rtpParameters, appData }, callback, errback) => this.socketObj?.sendMessage({
                type: 'produce',
                payload: {
                    transportId: this.sendTransport?.id,
                    kind,
                    rtpParameters,
                    appData,
                },
            }, (message) => {
                if (!message.payload.error) {
                    callback({ id: message.payload.id });
                }
                else {
                    errback(message.payload.error);
                }
            }));
        }
    }
    async createConsumer(request, callback) {
        if (this.recvTransport === undefined || this.recvTransport.closed) {
            return;
        }
        const { peerId, producerId, id, kind, rtpParameters, type, appData, producerPaused, remotelyPaused, priority, } = request;
        const consumer = await this.recvTransport.consume({
            id,
            producerId,
            kind,
            rtpParameters,
            appData: { ...appData, peerId },
        });
        if (!this.closedConsumers.includes(id)) {
            this.consumers.set(consumer.id, consumer);
            this.dispatch(addConsumer({
                id,
                peerId,
                type,
                kind,
                remotelyPaused,
                producerPaused,
                priority,
                appData: consumer.appData,
                score: 10,
                producerScore: 10,
            }));
        }
        else {
            consumer.close();
        }
        if (callback) {
            callback('Done');
        }
    }
    async closeConsumer(request) {
        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);
    }
    async pauseConsumer(request) {
        const { id } = request;
        const consumer = this.consumers.get(id);
        if (!consumer)
            return;
        consumer.pause();
    }
    async resumeConsumer(request) {
        const { id } = request;
        const consumer = this.consumers.get(id);
        if (!consumer)
            return;
        consumer.resume();
    }
    async shareWebcam(videoTrack) {
        if (videoTrack === undefined) {
            return;
        }
        if (this.webCamProducer) {
            await this.removeProducer('webCamProducer', false);
        }
        let simulcast = 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 = WEBCAM_KSVC_ENCODINGS;
        }
        this.dispatch(setVideoOff(false));
        this.dispatch(setVideoProduction(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();
        };
        await this.createProducer('webCamProducer', producerOptions, onDisconnect);
        this.dispatch(setVideoProduction(MediaStates.LOADED));
    }
    async shareScreen(tracks) {
        const { video: videoTrack, audio: audioTrack } = tracks;
        if (videoTrack === undefined) {
            return;
        }
        let simulcast = SCREEN_SHARING_SIMULCAST_ENCODINGS;
        const vp9Codec = this.device.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/vp9') || null;
        if (vp9Codec !== null && !this.legacyMode) {
            simulcast = SCREEN_SHARING_SVC_ENCODINGS;
        }
        this.dispatch(setScreenSharing(true));
        this.dispatch(setScreenSharingProduction(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();
        };
        await 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
            };
            await this.createProducer('screenShareAudioProducer', audioProducerOptions, onDisconnect);
        }
        this.dispatch(setScreenSharingProduction(MediaStates.LOADED));
    }
    async shareMic(audioTrack, defaultState) {
        if (audioTrack === undefined) {
            return;
        }
        if (this.micProducer) {
            await this.removeProducer('micProducer', false);
        }
        if (defaultState) {
            this.dispatch(setAudioMuted(defaultState || false));
        }
        this.dispatch(setAudioProduction(MediaStates.FETCHED));
        const producerOptions = {
            track: audioTrack,
            codecOptions: {
                opusDtx: true,
                opusStereo: false,
                opusFec: true,
            },
            stopTracks: false,
        };
        const onDisconnect = () => {
            this.disableMic();
        };
        await this.createProducer('micProducer', producerOptions, onDisconnect);
        this.dispatch(setAudioProduction(MediaStates.LOADED));
    }
    pauseMic() {
        this.dispatch(setAudioMuted(true));
        this.micProducer.pause();
    }
    pauseWebcam() {
        this.dispatch(setVideoOff(true));
        this.webCamProducer.pause();
    }
    resumeMic() {
        this.dispatch(setAudioMuted(false));
        this.micProducer.resume();
    }
    resumeWebcam() {
        this.dispatch(setVideoOff(false));
        this.webCamProducer.resume();
    }
    async disableWebcam(data) {
        if (!data || !data?.silent) {
            this.dispatch(setVideoOff(true));
        }
        await this.removeProducer('webCamProducer');
        this.dispatch(setVideoProduction(MediaStates.NOT_STARTED));
    }
    async disableMic(data) {
        if (!data || !data?.silent) {
            this.dispatch(setAudioMuted(true));
        }
        await this.removeProducer('micProducer');
        this.dispatch(setAudioProduction(MediaStates.NOT_STARTED));
    }
    async disableScreenShare() {
        this.dispatch(setScreenSharingProduction(MediaStates.NOT_STARTED));
        this.dispatch(setScreenSharing(false));
        await this.removeProducer('screenShareVideoProducer');
        await 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');
        }
    }
    async stopAllTransports() {
        if (this.sendTransport) {
            try {
                this.sendTransport.close();
            }
            catch {
                console.log('awaitqueue error');
            }
            this.sendTransport = null;
        }
        if (this.recvTransport) {
            try {
                this.recvTransport.close();
            }
            catch {
                console.log('awaitqueue error');
            }
            this.recvTransport = null;
        }
        this.dispatch(setRouterFetchState(MediaStates.NOT_STARTED));
    }
    async resetVideoProducers(videoTrack, screenShareTrack) {
        if (videoTrack) {
            await this.removeProducer('webCamProducer', false);
            this.shareWebcam(videoTrack);
        }
        if (screenShareTrack) {
            await this.removeProducer('screenShareVideoProducer', false);
            this.shareScreen({ video: screenShareTrack });
        }
    }
    async createProducer(producer, producerOptions, onDisconnect) {
        if (this.sendTransport === undefined || this.sendTransport.closed) {
            return;
        }
        this[producer] = await 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();
        });
    }
    async removeProducer(producer, stopTrack = true) {
        if (!this[producer] || this[producer].closed) {
            return;
        }
        if (stopTrack) {
            this[producer].track.stop();
        }
        this[producer].close();
        await this.socketObj.sendMessage({ type: 'closeProducer', payload: { producerId: this[producer].id } });
        this[producer] = null;
    }
    async sendMessage(message) {
        await this.socketObj.sendMessage({ type: 'chatMessage', payload: message });
    }
    async getMesssages() {
        await this.socketObj.sendMessage({ type: 'getChatMessages' });
    }
    async getPolls() {
        await this.socketObj.sendMessage({ type: 'getPolls' });
    }
    async newPoll(poll) {
        await this.socketObj.sendMessage({ type: 'newPoll', payload: poll });
    }
    async votePoll(poll) {
        await this.socketObj.sendMessage({ type: 'votePoll', payload: poll });
    }
    async acceptWaitingRequest(peerId) {
        await this.socketObj.sendMessage({ type: 'acceptWaitingRequest', payload: { id: peerId } });
    }
    async rejectWaitingRequest(peerId) {
        await this.socketObj.sendMessage({ type: 'rejectWaitingRequest', payload: { id: peerId } });
    }
    async muteAll(allowUnMute) {
        await this.socketObj.sendMessage({ type: 'muteAllNew', payload: { allowUnMute } });
    }
    async rejectRequestToJoinStage(peerId) {
        await this.socketObj.sendMessage({ type: 'rejectRequestToJoinStage', payload: { id: peerId } });
    }
    async muteAllVideo() {
        await this.socketObj.sendMessage({ type: 'muteAllVideo' });
    }
    async pin(peerId) {
        await this.socketObj.sendMessage({ type: 'pinPeer', payload: { id: peerId } });
    }
    async disableAudio(peerId) {
        await this.socketObj.sendMessage({ type: 'disableAudioPeerNew', payload: { id: peerId } });
    }
    async disableVideo(peerId) {
        await this.socketObj.sendMessage({ type: 'disableVideoPeer', payload: { id: peerId } });
    }
    async kickAll() {
        await this.socketObj.sendMessage({ type: 'kickAll' });
    }
    async kick(peerId) {
        await this.socketObj.sendMessage({ type: 'kick', payload: { id: peerId } });
    }
    async getPage(pageNum) {
        await this.socketObj.sendMessage({ type: 'getPage', payload: { pageNum } });
    }
    // eslint-disable-next-line class-methods-use-this
    async setRecordingStatus(status) {
        // @ts-ignore
        window.triggerDyteRecording = status;
    }
    async muteSelf() {
        await this.socketObj.sendMessage({ type: 'muteSelf' });
    }
    async unmuteSelf() {
        await this.socketObj.sendMessage({ type: 'unmuteSelf' });
    }
    async requestToJoinStage(requestToJoinType) {
        await this.socketObj.sendMessage({ type: 'requestToJoinStage', payload: requestToJoinType });
    }
    async acceptAllRequestToJoinStage(peers) {
        await this.socketObj.sendMessage({ type: 'acceptAllRequestToJoinStage', payload: peers });
    }
    async removePeerFromStage(peerId, type) {
        await this.socketObj.sendMessage({ type: 'removePeerFromStage', payload: { id: peerId, type } });
    }
    async startPresenting() {
        await this.socketObj.sendMessage({ type: 'startPresenting' });
    }
    async stopPresenting() {
        await this.socketObj.sendMessage({ type: 'stopPresenting' });
    }
    async updatePreset(peerId, presetName) {
        await this.socketObj.sendMessage({ type: 'updatePeerPreset', payload: { id: peerId, presetName } });
    }
}
//# sourceMappingURL=MediaRoomClient.js.map