"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = VideoNative;
exports.NativeVideo = NativeVideo;
exports.NativeWHEP = NativeWHEP;
exports.NativeIngestPlayer = NativeIngestPlayer;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const expo_video_1 = require("expo-video");
const lucide_react_native_1 = require("lucide-react-native");
const react_1 = require("react");
const react_native_1 = require("react-native");
const react_native_webrtc_1 = require("react-native-webrtc");
const __1 = require("../..");
const atoms_1 = require("../../lib/theme/atoms");
const shared_1 = require("./shared");
const use_webrtc_1 = tslib_1.__importStar(require("./use-webrtc"));
const webrtc_primitives_native_1 = require("./webrtc-primitives.native");
// Add NativeIngestPlayer to the switch below!
function VideoNative(props) {
    const inProto = (0, __1.usePlayerStore)((x) => x.protocol);
    const selectedRendition = (0, __1.usePlayerStore)((x) => x.selectedRendition);
    const src = (0, __1.usePlayerStore)((x) => x.src);
    const ingest = (0, __1.usePlayerStore)((x) => x.ingestConnectionState) != null;
    const { protocol } = (0, shared_1.srcToUrl)({ src, selectedRendition }, inProto);
    return ((0, jsx_runtime_1.jsx)(__1.View, { children: ingest ? ((0, jsx_runtime_1.jsx)(NativeIngestPlayer, { objectFit: props?.objectFit })) : protocol === __1.PlayerProtocol.WEBRTC ? ((0, jsx_runtime_1.jsx)(NativeWHEP, { objectFit: props?.objectFit, pictureInPictureEnabled: props?.pictureInPictureEnabled })) : ((0, jsx_runtime_1.jsx)(NativeVideo, { objectFit: props?.objectFit, pictureInPictureEnabled: props?.pictureInPictureEnabled })) }));
}
function NativeVideo(props) {
    const videoRef = (0, react_1.useRef)(null);
    const protocol = (0, __1.usePlayerStore)((x) => x.protocol);
    const mode = (0, __1.usePlayerStore)((x) => x.mode);
    const selectedRendition = (0, __1.usePlayerStore)((x) => x.selectedRendition);
    const src = (0, __1.usePlayerStore)((x) => x.src);
    const { url } = (0, shared_1.srcToUrl)({ src: src, selectedRendition }, protocol);
    const setStatus = (0, __1.usePlayerStore)((x) => x.setStatus);
    const muted = (0, __1.useMuted)();
    const volume = (0, __1.useEffectiveVolume)();
    const setFullscreen = (0, __1.usePlayerStore)((x) => x.setFullscreen);
    const playerEvent = (0, __1.usePlayerStore)((x) => x.playerEvent);
    const spurl = (0, __1.useStreamplaceStore)((x) => x.url);
    const setPlayerWidth = (0, __1.usePlayerStore)((x) => x.setPlayerWidth);
    const setPlayerHeight = (0, __1.usePlayerStore)((x) => x.setPlayerHeight);
    const setPlayTime = (0, __1.usePlayerStore)((x) => x.setPlayTime);
    const setDuration = (0, __1.usePlayerStore)((x) => x.setDuration);
    const setBufferedEnd = (0, __1.usePlayerStore)((x) => x.setBufferedEnd);
    const playTime = (0, __1.usePlayerStore)((x) => x.playTime);
    const setTogglePlayPause = (0, __1.usePlayerStore)((x) => x.setTogglePlayPause);
    const status = (0, __1.usePlayerStore)((x) => x.status);
    const statusRef = (0, react_1.useRef)(status);
    const lastReportedTimeRef = (0, react_1.useRef)(0);
    // State for live dimensions
    const [dimensions, setDimensions] = (0, react_1.useState)({ width: 0, height: 0 });
    const handleLayout = (0, react_1.useCallback)((event) => {
        const { width, height } = event.nativeEvent.layout;
        if (dimensions.width === width && dimensions.height === height) {
            return;
        }
        setDimensions({ width, height });
        setPlayerWidth(width);
        setPlayerHeight(height);
    }, []);
    (0, react_1.useEffect)(() => {
        return () => {
            setStatus(__1.PlayerStatus.START);
        };
    }, [setStatus]);
    const player = (0, expo_video_1.useVideoPlayer)({ uri: url, contentType: "hls" }, (player) => {
        player.addListener("playingChange", ({ isPlaying }) => {
            if (isPlaying) {
                setStatus(__1.PlayerStatus.PLAYING);
            }
            else {
                setStatus(__1.PlayerStatus.WAITING);
            }
        });
        player.loop = mode !== "vod";
        player.muted = muted;
        player.play();
    });
    (0, react_1.useEffect)(() => {
        player.muted = muted;
    }, [muted, player]);
    (0, react_1.useEffect)(() => {
        player.volume = volume;
    }, [volume, player]);
    (0, react_1.useEffect)(() => {
        statusRef.current = status;
    }, [status]);
    (0, react_1.useEffect)(() => {
        player.loop = mode !== "vod";
    }, [player, mode]);
    (0, react_1.useEffect)(() => {
        setTogglePlayPause(() => {
            if (statusRef.current === __1.PlayerStatus.PLAYING) {
                player.pause();
            }
            else {
                player.play();
            }
        });
        return () => setTogglePlayPause(() => { });
    }, [player, setTogglePlayPause]);
    (0, react_1.useEffect)(() => {
        if (mode !== "vod")
            return;
        const interval = setInterval(() => {
            const currentTime = player.currentTime;
            const duration = player.duration;
            lastReportedTimeRef.current = currentTime;
            setPlayTime(currentTime);
            if (isFinite(duration) && duration > 0) {
                setDuration(duration);
            }
            const buffered = player.bufferedPosition;
            if (typeof buffered === "number" && buffered > 0) {
                setBufferedEnd(buffered);
            }
        }, 250);
        return () => clearInterval(interval);
    }, [mode, player, setPlayTime, setDuration, setBufferedEnd]);
    (0, react_1.useEffect)(() => {
        if (mode !== "vod")
            return;
        const diff = Math.abs(playTime - lastReportedTimeRef.current);
        if (diff > 1.0) {
            player.seekBy(playTime - player.currentTime);
        }
    }, [playTime, mode, player]);
    (0, react_1.useEffect)(() => {
        const subs = [
            "playToEnd",
            "playbackRateChange",
            "playingChange",
            "sourceChange",
            "statusChange",
            "volumeChange",
        ].map((evType) => {
            return player.addListener(evType, (...args) => {
                const now = new Date();
                console.log("video native event", evType);
                if (evType === "statusChange") {
                    console.log("player.status", ...args, { url });
                }
                playerEvent(spurl, now.toISOString(), evType, { args: args });
            });
        });
        subs.push(player.addListener("playingChange", ({ isPlaying }) => {
            if (isPlaying) {
                setStatus(__1.PlayerStatus.PLAYING);
            }
            else {
                setStatus(__1.PlayerStatus.WAITING);
            }
        }));
        return () => {
            for (const sub of subs) {
                sub.remove();
            }
        };
    }, [player, playerEvent, setStatus, spurl]);
    return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(expo_video_1.VideoView, { ref: videoRef, player: player, 
            // Always off — we render our own controls (MobileUi / DesktopUi). Even
            // briefly enabling them flashes expo-video's native control overlay.
            nativeControls: false, onFullscreenEnter: () => {
                setFullscreen(true);
            }, onFullscreenExit: () => {
                setFullscreen(false);
            }, allowsPictureInPicture: props?.pictureInPictureEnabled !== false, onLayout: handleLayout, style: {
                minWidth: "100%",
                minHeight: "100%",
                flex: 1,
            } }) }));
}
function NativeWHEP(props) {
    const selectedRendition = (0, __1.usePlayerStore)((x) => x.selectedRendition);
    const src = (0, __1.usePlayerStore)((x) => x.src);
    const [stream, stuck] = (0, use_webrtc_1.default)(src);
    const status = (0, __1.usePlayerStore)((x) => x.status);
    const setPlayerWidth = (0, __1.usePlayerStore)((x) => x.setPlayerWidth);
    const setPlayerHeight = (0, __1.usePlayerStore)((x) => x.setPlayerHeight);
    // PiP support: wire up videoRef (no direct ref for RTCView)
    const setVideoRef = (0, __1.usePlayerStore)((x) => x.setVideoRef);
    // State for live dimensions
    const [dimensions, setDimensions] = (0, react_1.useState)({ width: 0, height: 0 });
    const handleLayout = (0, react_1.useCallback)((event) => {
        const { width, height } = event.nativeEvent.layout;
        setDimensions({ width, height });
        setPlayerWidth(width);
        setPlayerHeight(height);
    }, []);
    const setStatus = (0, __1.usePlayerStore)((x) => x.setStatus);
    const muted = (0, __1.useMuted)();
    const volume = (0, __1.useEffectiveVolume)();
    (0, react_1.useEffect)(() => {
        if (stuck && status === __1.PlayerStatus.PLAYING) {
            console.log("setting status to stalled", status);
            setStatus(__1.PlayerStatus.STALLED);
        }
        if (!stuck && status === __1.PlayerStatus.STALLED) {
            console.log("setting status to playing", status);
            setStatus(__1.PlayerStatus.PLAYING);
        }
    }, [stuck, status]);
    const mediaStream = stream;
    // useEffect(() => {
    //   if (!mediaStream) {
    //     setStatus(PlayerStatus.WAITING);
    //     return;
    //   }
    //   setStatus(PlayerStatus.PLAYING);
    // }, [mediaStream, setStatus]);
    (0, react_1.useEffect)(() => {
        if (!mediaStream) {
            return;
        }
        mediaStream.getTracks().forEach((track) => {
            if (track.kind === "audio") {
                track._setVolume(muted ? 0 : volume);
            }
        });
    }, [mediaStream, muted, volume]);
    // Keep the playerStore videoRef in sync for PiP (if possible)
    (0, react_1.useEffect)(() => {
        if (typeof setVideoRef === "function") {
            setVideoRef(null); // No direct ref for RTCView, but keep API consistent
        }
    }, [setVideoRef]);
    if (!mediaStream) {
        return (0, jsx_runtime_1.jsx)(__1.View, {});
    }
    return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(react_native_webrtc_1.RTCView, { mirror: false, objectFit: props?.objectFit || "contain", streamURL: mediaStream.toURL(), onLayout: handleLayout, pictureInPictureEnabled: props?.pictureInPictureEnabled !== false, autoStartPictureInPicture: true, pictureInPicturePreferredSize: {
                width: 160,
                height: 90,
            }, style: {
                minWidth: "100%",
                minHeight: "100%",
                flex: 1,
            } }) }));
}
function NativeIngestPlayer(props) {
    const ingestMediaSource = (0, __1.usePlayerStore)((x) => x.ingestMediaSource);
    const ingestAutoStart = (0, __1.usePlayerStore)((x) => x.ingestAutoStart);
    const setIngestLive = (0, __1.usePlayerStore)((x) => x.setIngestLive);
    const setStatus = (0, __1.usePlayerStore)((x) => x.setStatus);
    const setVideoRef = (0, __1.usePlayerStore)((x) => x.setVideoRef);
    const [error, setError] = (0, react_1.useState)(null);
    const ingestCamera = (0, __1.usePlayerStore)((x) => x.ingestCamera);
    (0, react_1.useEffect)(() => {
        setStatus(__1.PlayerStatus.PLAYING);
    }, [setStatus]);
    (0, react_1.useEffect)(() => {
        if (typeof setVideoRef === "function") {
            setVideoRef(null);
        }
    }, [setVideoRef]);
    const url = (0, __1.useStreamplaceStore)((x) => x.url);
    const [lms, setLocalMediaStream] = (0, react_1.useState)(null);
    const [, setRemoteMediaStream] = (0, use_webrtc_1.useWebRTCIngest)({
        endpoint: `${url}/api/ingest/webrtc`,
    });
    // Use lms directly as localMediaStream
    const localMediaStream = lms;
    (0, react_1.useEffect)(() => {
        let acquiredStream = null;
        if (ingestMediaSource === __1.IngestMediaSource.DISPLAY) {
            webrtc_primitives_native_1.mediaDevices
                .getDisplayMedia()
                .then((stream) => {
                acquiredStream = stream;
                console.log("display media", stream);
                setLocalMediaStream(stream);
            })
                .catch((e) => {
                console.log("error getting display media", e);
                console.error("error getting display media", e);
            });
        }
        else {
            webrtc_primitives_native_1.mediaDevices
                .getUserMedia({
                audio: {
                // deviceId: "audio-1",
                // echoCancellation: true,
                // autoGainControl: true,
                // noiseSuppression: true,
                // latency: false,
                // channelCount: false,
                },
                video: {
                    facingMode: ingestCamera,
                    width: { min: 200, ideal: 1080, max: 2160 },
                    height: { min: 200, ideal: 1920, max: 3840 },
                },
            })
                .then((stream) => {
                acquiredStream = stream;
                setLocalMediaStream(stream);
                let errs = [];
                if (stream.getAudioTracks().length === 0) {
                    console.warn("No audio tracks found in user media stream");
                    errs.push("microphone");
                }
                if (stream.getVideoTracks().length === 0) {
                    console.warn("No video tracks found in user media stream");
                    errs.push("camera");
                }
                if (errs.length > 0) {
                    setError(new Error(`We could not access your ${errs.join(" and ")}. To stream, you need to give us permission to access these.`));
                }
                else {
                    setError(null);
                }
            })
                .catch((e) => {
                console.error("error getting user media", e);
                setError(new Error("We could not access your camera or microphone. To stream, you need to give us permission to access these."));
            });
        }
        return () => {
            if (acquiredStream) {
                acquiredStream.getTracks().forEach((track) => track.stop());
            }
            setLocalMediaStream(null);
        };
    }, [ingestMediaSource, ingestCamera]);
    (0, react_1.useEffect)(() => {
        if (localMediaStream) {
            setIngestLive(true);
        }
    }, [localMediaStream]);
    (0, react_1.useEffect)(() => {
        if (!localMediaStream) {
            return;
        }
        console.log("setting remote media stream", localMediaStream);
        // @ts-expect-error: WebRTCMediaStream may not have all MediaStream properties, but is compatible for our use
        setRemoteMediaStream(localMediaStream);
    }, [localMediaStream, ingestAutoStart, setRemoteMediaStream]);
    if (!localMediaStream) {
        return null;
    }
    if (error) {
        return ((0, jsx_runtime_1.jsxs)(__1.View, { backgroundColor: atoms_1.colors.destructive[900], style: [atoms_1.p[4], atoms_1.m[4], atoms_1.gap.all[2], { borderRadius: atoms_1.borderRadius.md }], children: [(0, jsx_runtime_1.jsx)(__1.View, { children: (0, jsx_runtime_1.jsx)(__1.Text, { style: [atoms_1.fontWeight.semibold], size: "2xl", children: "Error encountered!" }) }), (0, jsx_runtime_1.jsx)(__1.Text, { children: error.message }), error.message.includes("To stream, you need to give us permission to access these.") && ((0, jsx_runtime_1.jsx)(__1.Button, { onPress: react_native_1.Linking.openSettings, style: [atoms_1.h[10]], variant: "secondary", children: (0, jsx_runtime_1.jsxs)(__1.View, { style: [atoms_1.layout.flex.row, atoms_1.gap.all[1]], children: [(0, jsx_runtime_1.jsx)(__1.Text, { children: "Open Settings" }), (0, jsx_runtime_1.jsx)(lucide_react_native_1.ArrowRight, { color: "white", size: "18" })] }) }))] }));
    }
    return ((0, jsx_runtime_1.jsx)(react_native_webrtc_1.RTCView, { mirror: ingestCamera !== "environment", objectFit: props?.objectFit || "contain", streamURL: localMediaStream.toURL(), zOrder: 0, style: {
            minWidth: "100%",
            minHeight: "100%",
            flex: 1,
        } }));
}
//# sourceMappingURL=video-async.native.js.map