<!DOCTYPE html>
<html>
<head>
  <title>Streamer</title>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
  <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
</head>
<body>
  <h1>Streaming as {{ username }}</h1>
  <video id="localVideo" autoplay playsinline muted style="transform: scaleX(-1);"></video>

  <script>
    const username = "{{ username }}";
    const socket = io({ transports: ["websocket"] });

    // Keep a PeerConnection for each watcherSid
    const peerConnections = {};
    let localStream = null;

    socket.on("connect", () => {
      console.log("[Streamer] Connected:", socket.id);
      // Join the 'username' room
      socket.emit("join_room", { username });
      // Start local camera
      startLocalCamera();
    });

    async function startLocalCamera() {
      try {
        // Video only; use audio: true if you want to stream mic audio too
        localStream = await navigator.mediaDevices.getUserMedia({
          video: true,
          audio: true
        });
        document.getElementById("localVideo").srcObject = localStream;
      } catch (err) {
        console.error("Error accessing camera:", err);
      }
    }

    // When a new watcher arrives, we create an offer for them
    socket.on("new_watcher", (data) => {
      const watcherSid = data.watcherSid;
      console.log("[Streamer] new_watcher event:", watcherSid);
      createOfferForWatcher(watcherSid);
    });

    async function createOfferForWatcher(watcherSid) {
      console.log("[Streamer] Creating offer for", watcherSid);
      const pc = new RTCPeerConnection({
        iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
      });
      peerConnections[watcherSid] = pc;

      // Add local tracks to this new PeerConnection
      localStream.getTracks().forEach((track) => pc.addTrack(track, localStream));

      // Send our ICE candidates to the server => watchers
      pc.onicecandidate = (event) => {
        if (event.candidate) {
          socket.emit("ice-candidate", {
            candidate: event.candidate.candidate,
            sdpMid: event.candidate.sdpMid,
            sdpMLineIndex: event.candidate.sdpMLineIndex,
            targetSid: watcherSid
          });
        }
      };

      // Create an offer, set as local description
      const offer = await pc.createOffer();
      await pc.setLocalDescription(offer);

      // Send the offer to that watcher
      socket.emit("offer", {
        username,
        offer: offer.sdp,
        offerType: offer.type,
        watcherSid
      });
    }

    // Handle "answer" from watchers
    socket.on("answer", async (data) => {
      const { answer, answerType, watcherSid } = data;
      console.log("[Streamer] Received answer from watcherSid:", watcherSid);

      const pc = peerConnections[watcherSid];
      if (!pc) {
        console.warn("[Streamer] PeerConnection not found for", watcherSid);
        return;
      }

      const remoteDesc = new RTCSessionDescription({
        type: answerType,
        sdp: answer
      });
      await pc.setRemoteDescription(remoteDesc);
    });

    // Handle ICE candidates from watchers
    socket.on("ice-candidate", (data) => {
      const { candidate, sdpMid, sdpMLineIndex, senderSid } = data;
      console.log("[Streamer] ICE candidate from", senderSid);
      const pc = peerConnections[senderSid];
      if (pc && candidate) {
        pc.addIceCandidate(new RTCIceCandidate({
          candidate,
          sdpMid,
          sdpMLineIndex
        })).catch(err => console.error("Error adding ICE candidate:", err));
      }
    });
  </script>
</body>
</html>

