import EventEmitter from "../tools/event-emitter";
import WebsocketService from "./websocket-service";

function WebRTCService() {
  const TYPE_SIGNALING = "signaling";
  const STATE_CONNECTED = "connected";
  const STATE_DISCONNECTED = "disconnected";
  const STATE_CLOSED = "closed";
  const STATE_FAILED = "failed";
  const ICE_CONNECTION_STATE_CHANGE_EVENT = "iceconnectionstatechange";
  const DATA_CHANNEL_OPEN_EVENT = "datachannelopen";

  let videoElement;
  let peerConnection;
  let peerId;
  let dataChannel;

  let eventEmitter = new EventEmitter();

  WebsocketService.onMessage((data) => {
    let dataObject = JSON.parse(data);
    let sender = dataObject.sender;
    if (sender !== peerId) return;
    let message = dataObject.message;
    handleSignalingMessage(peerId, message);
  });

  async function connect(targetId) {
    peerId = targetId;
    let configuration = getConfiguration();
    peerConnection = new RTCPeerConnection(configuration);
    console.log("ICE connection state:" + peerConnection.iceConnectionState);
    peerConnection.addEventListener("iceconnectionstatechange", (event) => {
      let state = peerConnection.iceConnectionState;
      console.log("ICE connection state changed to:", state);
      eventEmitter.dispatchEvent(ICE_CONNECTION_STATE_CHANGE_EVENT, state);
      if (state === STATE_CONNECTED) {
        videoElement.play();
      }
    });
    peerConnection.addEventListener("signalingstatechange", (event) => {
      console.log("Signaling state changed to:", event.target.signalingState);
    });
    peerConnection.onicecandidate = (event) => {
      if (event.candidate) {
        sendIceCandidate(targetId, event.candidate);
      }
    };
    peerConnection.ontrack = (event) => {
      console.log("Received track:", event.track);
      if (videoElement) {
        videoElement.srcObject.addTrack(event.track);
      }
    };
    peerConnection.onicecandidateerror = (event) => {
      console.error("ICE candidate error:", event);
    };
    peerConnection.onnegotiationneeded = () => {
      console.log("Negotiation needed");
      negotiate(targetId);
    };
    dataChannel = peerConnection.createDataChannel("data");
    dataChannel.addEventListener("open", () => {
      eventEmitter.dispatchEvent(DATA_CHANNEL_OPEN_EVENT);
    });
    negotiate(targetId);
  }

  function disconnect() {
    peerConnection.close();
    eventEmitter.dispatchEvent(
      ICE_CONNECTION_STATE_CHANGE_EVENT,
      peerConnection.iceConnectionState
    );
  }

  async function negotiate(targetId) {
    try {
      let offer = await peerConnection.createOffer({
        offerToReceiveAudio: true,
        offerToReceiveVideo: true,
      });
      console.log("Created offer:", offer);
      await peerConnection.setLocalDescription(offer);
      sendOffer(targetId, offer);
    } catch (error) {
      console.error("Error creating offer:", error);
    }
  }

  function sendOffer(integrationId, offer) {
    WebsocketService.sendJson({
      type: TYPE_SIGNALING,
      target: integrationId,
      message: offer,
    });
  }

  function sendIceCandidate(integrationId, candidate) {
    console.log("Sending ICE candidate:", candidate);
    WebsocketService.sendJson({
      type: TYPE_SIGNALING,
      target: integrationId,
      message: {
        type: "ice_candidate",
        candidate: candidate,
      },
    });
  }

  function handleSignalingMessage(targetId, message) {
    //console.log("Received message:", message);
    switch (message.type) {
      case "answer":
        handleAnswer(message);
        break;
      case "ice_candidate":
        handleIceCandidate(message);
        break;
      default:
        return;
    }
  }

  function handleAnswer(answer) {
    console.log("Remote answer:", answer);
    if (peerConnection.signalingState === "stable") return;
    peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
  }

  function handleIceCandidate(message) {
    let iceCandidate = new RTCIceCandidate(message.candidate);
    console.log("Received ICE candidate:", iceCandidate);
    peerConnection.addIceCandidate(iceCandidate);
  }

  function sendDataString(data) {
    if (!dataChannel) return;
    if (dataChannel.readyState !== "open") return;
    dataChannel.send(data);
  }

  function sendDataJson(data) {
    let dataJson = JSON.stringify(data);
    sendDataString(dataJson);
  }

  function onStateChanged(callback) {
    eventEmitter.on(ICE_CONNECTION_STATE_CHANGE_EVENT, () => {
      callback(peerConnection.iceConnectionState);
    });
  }

  function onDataChannelOpen(callback) {
    eventEmitter.on(DATA_CHANNEL_OPEN_EVENT, callback);
  }

  function getConfiguration() {
    return {
      iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
    };
  }

  function setVideoElement(element) {
    videoElement = element;
    videoElement.srcObject = new MediaStream();
  }

  function getVideoElement() {
    return videoElement;
  }

  return {
    STATE_CONNECTED,
    STATE_DISCONNECTED,
    STATE_CLOSED,
    STATE_FAILED,
    connect,
    disconnect,
    setVideoElement,
    getVideoElement,
    sendDataString,
    sendDataJson,
    onStateChanged,
    onDataChannelOpen,
  };
}

WebRTCService = new WebRTCService();

export default WebRTCService;
