import React, { createContext, useState, useEffect, useRef, useContext, } from 'react';
import { batch, useDispatch, useSelector } from 'react-redux';
import { MediaStates, } from '../store/roomSlice';
import { AudioDevicesEmitter, getAllVideoDevices, getAudioTrack, getScreenShareConstraints, getVideoTrack, getWebcamConstraints, setOnDeviceChange, } from '../utils/media-utils';
import { PresentingState, RequestToJoinType, setAudioMuted, setAudioProduction, setAudioTrackSelection, setCameraFacing, setVideoOff, setVideoProduction, setVideoTrackSelection, } from '../store/participantSlice';
import { ConfigServiceContext } from '../utils/ConfigProvider';
import InCallManager from '../consolidatedLibraries/RNInCallManager';
import { DyteViewTypes } from '../utils/dyte-utils/src';
import { NativeModules, NativeEventEmitter } from 'react-native';
const masterOut = NativeModules.InCallManager;
const masterOutEmitter = new NativeEventEmitter(masterOut);
export const UserTrackContext = createContext(null);
export default function UserTrackProvider(props) {
    const [audioTrack, setAudioTrack] = useState();
    const [audioInputDevices, setAudioInputDevices] = useState([]);
    const [webcamTrack, setWebcamTrack] = useState();
    const [videoInputDevices, setVideoInputDevices] = useState([]);
    const [screenShareVideoTrack, setScreenShareVideoTrack] = useState();
    const [screenShareAudioTrack, setScreenShareAudioTrack] = useState();
    const webcamTrackSave = useRef(null);
    const screenShareVideoTrackSave = useRef(null);
    const screenShareAudioTrackSave = useRef(null);
    const audioTrackSave = useRef(null);
    const { meeting, sessionConfig } = useContext(ConfigServiceContext);
    const { audio: audioAllowed } = sessionConfig.permissions.produce;
    const videoConfig = sessionConfig.permissions.produce.video;
    const screenshareConfig = sessionConfig.permissions.produce.video;
    const videoAllowed = videoConfig.allow;
    let webcamConstraints = getWebcamConstraints(videoConfig.quality, videoConfig.frame_rate);
    const screenshareConstraints = getScreenShareConstraints(screenshareConfig.quality, screenshareConfig.frame_rate);
    const { children } = props;
    const audioMuted = useSelector((state) => state.participant.audioMuted);
    const videoOff = useSelector((state) => state.participant.videoOff);
    const producersState = useSelector((state) => state.room.producersState);
    const switched = useSelector((state) => state.participant.switched);
    const canPresent = useSelector((state) => state.participant.canPresent);
    const presentingState = useSelector((state) => state.participant.presentingState);
    const audioTrackSelection = useSelector((state) => state.participant.audioTrackSelection);
    const videoTrackSelection = useSelector((state) => state.participant.videoTrackSelection);
    const videoProduction = useSelector((state) => state.participant.videoProduction);
    const audioProduction = useSelector((state) => state.participant.audioProduction);
    const webinarPresentState = useSelector((state) => state.participant.presentingState);
    const videoButtonsEnabled = (producersState === MediaStates.NOT_APPLICABLE
        || videoProduction === MediaStates.LOADED
        || videoProduction === MediaStates.NOT_STARTED
        || (videoProduction === MediaStates.STOPPING
            && webinarPresentState !== PresentingState.NOT_PRESENTING)) && videoAllowed;
    const audioButtonsEnabled = (producersState === MediaStates.NOT_APPLICABLE
        || audioProduction === MediaStates.LOADED
        || audioProduction === MediaStates.NOT_STARTED
        || (audioProduction === MediaStates.STOPPING
            && webinarPresentState !== PresentingState.NOT_PRESENTING)) && audioAllowed;
    const dispatch = useDispatch();
    const [audioDevices, setAudioDevices] = React.useState([]);
    const getAllAudioDevices = (devices) => {
        setAudioDevices(devices);
    };
    const roomViewType = useSelector((state) => state.room.roomViewType);
    const requestToJoinType = useSelector((state) => state.participant.requestToJoinType);
    const routerState = useSelector((state) => state.room.routerState);
    if (roomViewType === DyteViewTypes.webinar && requestToJoinType === RequestToJoinType.ask) {
        webcamConstraints = getWebcamConstraints('QQVGA', sessionConfig.permissions?.produce?.video?.frame_rate);
    }
    useEffect(() => {
        webcamTrackSave.current = webcamTrack;
        screenShareVideoTrackSave.current = screenShareVideoTrack;
        screenShareAudioTrackSave.current = screenShareAudioTrack;
        audioTrackSave.current = audioTrack;
    }, [webcamTrack, screenShareVideoTrack, screenShareAudioTrack, audioTrack]);
    useEffect(() => {
        if (audioTrack) {
            audioTrack.enabled = !audioMuted;
        }
        if (roomViewType !== DyteViewTypes.webinar
            || presentingState === PresentingState.PRESENTING) {
            dispatch(setAudioProduction(MediaStates.CONNECTING));
        }
    }, [audioMuted, presentingState]);
    const initialAudioStreamFetch = async () => {
        try {
            let audTrack = null;
            try {
                audTrack = await getAudioTrack();
            }
            catch (e) {
                if (e.name === 'NotAllowedError') {
                    throw new Error('No audio permission');
                }
            }
            if (!audTrack) {
                throw new Error('No audio track');
            }
            // AudioTrack
            setAudioTrack(audTrack);
        }
        catch (err) {
            dispatch(setAudioProduction(MediaStates.NOT_APPLICABLE));
            dispatch(setAudioMuted(true));
        }
    };
    const setAudioDevice = (newDevice) => {
        if (audioTrack) {
            audioTrack.stop();
        }
        if (!audioAllowed) {
            dispatch(setAudioMuted(true));
            return;
        }
        const deviceId = newDevice?.deviceId || audioTrackSelection?.selectedDeviceId;
        batch(async () => {
            await initialAudioStreamFetch();
            dispatch(setAudioProduction(MediaStates.CONNECTING));
            if (audioTrackSelection?.selectedDeviceId !== deviceId) {
                dispatch(setAudioTrackSelection({
                    selectedDeviceId: deviceId,
                }));
            }
        });
    };
    const initialVideoStreamFetch = async () => {
        try {
            let videoDevices = await getAllVideoDevices();
            const frontCamera = videoDevices.filter((el) => el.facing === 'front');
            const currentVideoDevice = frontCamera.length > 0 ? frontCamera[0] : videoDevices[0];
            let videoTrack = null;
            try {
                videoTrack = await getVideoTrack(currentVideoDevice.deviceId || '', webcamConstraints);
            }
            catch (e) {
                if (e.name === 'NotAllowedError') {
                    throw new Error('No video permission');
                }
            }
            if (!videoTrack) {
                throw new Error('No video track');
            }
            setWebcamTrack(videoTrack);
            if (currentVideoDevice.label === '') {
                videoDevices = await getAllVideoDevices();
            }
            setVideoInputDevices(videoDevices);
            dispatch(setVideoTrackSelection({
                selectedDeviceId: currentVideoDevice.deviceId,
            }));
        }
        catch (err) {
            console.log('Error', err);
            dispatch(setVideoProduction(MediaStates.NOT_APPLICABLE));
            dispatch(setVideoOff(true));
        }
    };
    const setVideoDevice = async (newDevice) => {
        const deviceId = newDevice?.deviceId || videoTrackSelection?.selectedDeviceId;
        if (!videoAllowed) {
            dispatch(setVideoOff(true));
            return;
        }
        batch(() => {
            dispatch(setVideoOff(false));
            dispatch(setVideoProduction(MediaStates.CONNECTING));
            if (videoTrackSelection?.selectedDeviceId !== deviceId) {
                dispatch(setVideoTrackSelection({
                    selectedDeviceId: deviceId,
                }));
            }
        });
        setTimeout(async () => {
            if (webcamTrack) {
                webcamTrack.stop();
            }
            const newVideoTrack = await getVideoTrack(deviceId, webcamConstraints);
            setWebcamTrack(newVideoTrack);
        });
    };
    useEffect(() => {
        if (roomViewType === DyteViewTypes.webinar
            && routerState === MediaStates.LOADED
            && webinarPresentState === PresentingState.NOT_PRESENTING) {
            batch(() => {
                dispatch(setVideoProduction(MediaStates.STOPPING));
                dispatch(setAudioProduction(MediaStates.STOPPING));
                dispatch(setVideoOff(true));
                dispatch(setAudioMuted(true));
                setTimeout(() => {
                    if (webcamTrack) {
                        webcamTrack.stop();
                    }
                    if (audioTrack) {
                        audioTrack.stop();
                    }
                    setAudioTrack(undefined);
                    setWebcamTrack(undefined);
                });
            });
        }
        else if (webinarPresentState === PresentingState.INITIATED) {
            initialVideoStreamFetch().then(setVideoDevice);
            setAudioDevice();
        }
    }, [routerState, webinarPresentState]);
    useEffect(() => {
        InCallManager.start({
            media: 'video',
        });
        const audioDeviceEmitter = new AudioDevicesEmitter(getAllAudioDevices);
        const x = async () => {
            if (webcamTrackSave.current) {
                webcamTrackSave.current.stop();
            }
            if (audioTrackSave.current) {
                audioTrackSave.current.stop();
            }
            if (screenShareVideoTrackSave.current) {
                screenShareVideoTrackSave.current.stop();
            }
            if (screenShareAudioTrackSave.current) {
                screenShareAudioTrackSave.current.stop();
            }
        };
        x();
        return () => {
            audioDeviceEmitter.removeListener();
        };
    }, []);
    React.useEffect(() => {
        if (!videoAllowed) {
            dispatch(setVideoOff(true));
            setTimeout(() => {
                if (webcamTrack) {
                    webcamTrack.stop();
                }
                setWebcamTrack(undefined);
            });
        }
    }, [videoAllowed]);
    useEffect(() => {
        if (videoProduction === MediaStates.NOT_STARTED) {
            setTimeout(() => {
                if (webcamTrack) {
                    webcamTrack.stop();
                    setWebcamTrack(undefined);
                }
            });
        }
    }, [videoProduction]);
    const toggleAudioState = async () => {
        if (!audioButtonsEnabled) {
            return;
        }
        if (!audioAllowed) {
            if (!audioMuted) {
                dispatch(setAudioMuted(true));
            }
        }
        else if (audioMuted) {
            if (!audioTrack || audioTrack?.readyState === 'ended') {
                setAudioDevice(undefined);
            }
            dispatch(setAudioMuted(false));
        }
        else {
            dispatch(setAudioMuted(true));
        }
    };
    const disableVideoStream = () => {
        batch(() => {
            dispatch(setVideoProduction(MediaStates.CONNECTING));
            dispatch(setVideoOff(true));
            setTimeout(() => {
                if (webcamTrack) {
                    webcamTrack.stop();
                }
                setWebcamTrack(undefined);
            });
        });
    };
    useEffect(() => {
        if (switched === 'meeting') {
            if (!videoOff)
                disableVideoStream();
        }
        else if (switched === 'webinar') {
            if (!canPresent) {
                if (!audioMuted)
                    dispatch(setAudioMuted(true));
                if (!videoOff)
                    disableVideoStream();
            }
        }
    }, [switched]);
    const toggleVideoState = async (stop) => {
        if (!videoButtonsEnabled) {
            return;
        }
        if (videoOff) {
            setVideoDevice();
        }
        else {
            disableVideoStream();
        }
    };
    const setupStreams = async () => {
        await initialVideoStreamFetch();
        await initialAudioStreamFetch();
    };
    const changeAudioTrack = async (selectedDevice = null) => {
        if (audioDevices.length > 0) {
            const devicesKey = {};
            let audioTrackToChange = {
                deviceId: null,
                InCallManagerId: null,
                device: null,
            };
            if (!selectedDevice) {
                audioDevices.forEach((device) => {
                    const { deviceId } = device;
                    devicesKey[deviceId] = device;
                });
            }
            else {
                const selectedDeviceId = selectedDevice.deviceId;
                devicesKey[selectedDeviceId] = selectedDevice;
            }
            if (devicesKey.bluetooth) {
                audioTrackToChange = {
                    deviceId: 'bluetooth',
                    InCallManagerId: 'BLUETOOTH',
                    device: devicesKey.bluetooth,
                };
            }
            else if (devicesKey.wired) {
                audioTrackToChange = {
                    deviceId: 'wired',
                    InCallManagerId: 'WIRED_HEADSET',
                    device: devicesKey.wired,
                };
            }
            else if (devicesKey.speaker) {
                audioTrackToChange = {
                    deviceId: 'speaker',
                    InCallManagerId: 'SPEAKER_PHONE',
                    device: devicesKey.speaker,
                };
            }
            await InCallManager.chooseAudioRoute(audioTrackToChange.InCallManagerId);
            setAudioDevice(audioTrackToChange.device);
        }
    };
    const onAudioInputChange = async (value) => {
        const newDevice = audioInputDevices
            .find((dev) => dev.deviceId === value);
        changeAudioTrack(newDevice);
    };
    const onVideoInputChange = (value) => {
        const newDevice = videoInputDevices
            .find((dev) => dev.deviceId === value);
        dispatch(setCameraFacing(newDevice.facing));
        setVideoDevice(newDevice);
    };
    // TOUNDERSTAND: When is on OnDeviceChange called
    setOnDeviceChange(async () => {
        const videoDevices = await getAllVideoDevices();
        // TOUNDERSTAND What does below code do?
        const newAudioDevice = [...audioDevices]
            .find((device) => !audioInputDevices.map((d) => d.deviceId).includes(device.deviceId));
        setAudioInputDevices(audioDevices);
        const newVideoDevice = videoDevices
            .find((device) => !videoInputDevices.map((d) => d.deviceId).includes(device.deviceId));
        setVideoInputDevices(videoDevices);
        if (!audioMuted && newAudioDevice) {
            setAudioDevice(newAudioDevice);
            return;
        }
        if (!videoOff && newVideoDevice) {
            setVideoDevice(newVideoDevice);
            return;
        }
        const isCurrentAudioDeviceUnplugged = !audioDevices
            .map((d) => d.deviceId).includes(audioTrackSelection?.selectedDeviceId);
        const isCurrentVideoDeviceUnplugged = !videoDevices
            .map((d) => d.deviceId).includes(videoTrackSelection?.selectedDeviceId);
        if (!audioMuted && (audioTrack === undefined || isCurrentAudioDeviceUnplugged)) {
            const currentDevice = audioDevices[0];
            setAudioDevice(currentDevice);
        }
        if (!videoOff && (webcamTrack === undefined || isCurrentVideoDeviceUnplugged)) {
            const currentDevice = videoDevices[0];
            setVideoDevice(currentDevice);
        }
    });
    useEffect(() => {
        changeAudioTrack(undefined);
        setAudioInputDevices(audioDevices);
    }, [audioDevices.length]);
    const context = {
        mediaTracks: {
            webcamTrack,
            setWebcamTrack,
            audioTrack,
            setAudioTrack,
            screenShareVideoTrack,
            setScreenShareVideoTrack,
            screenShareAudioTrack,
            setScreenShareAudioTrack,
        },
        inputDevices: {
            audioInputDevices,
            videoInputDevices,
            setAudioInputDevices,
            setVideoInputDevices,
            setAudioDevice,
            setVideoDevice,
            toggleAudioState,
            toggleVideoState,
            onAudioInputChange,
            onVideoInputChange,
        },
        setupStreams,
        videoButtonsEnabled,
        audioButtonsEnabled,
    };
    return (<UserTrackContext.Provider value={context}>
			{children}
		</UserTrackContext.Provider>);
}
//# sourceMappingURL=UserTrackProvider.js.map