text: import { Signaling, WebSocketSignaling } from "../../js/signaling.js";
  import Peer from "../../js/peer.js";
  import * as Logger from "../../js/logger.js";
  
  // enum type of event sending from Unity
  var UnityEventType = {
    SWITCH_VIDEO: 0,
  };
  
  function uuid4() {
    var temp_url = URL.createObjectURL(new Blob());
    var uuid = temp_url.toString();
    URL.revokeObjectURL(temp_url);
    return uuid.split(/[:/]/g).pop().toLowerCase(); // remove prefixes
  }
  
  export class VideoPlayer {
    constructor(elements) {
      const _this = this;
      this.pc = null;
      this.channel = null;
      this.connectionId = null;
  
      // main video
      this.localStream = new MediaStream();
      this.video = elements[0];
      this.video.playsInline = true;
      this.video.addEventListener(
        "loadedmetadata",
        function () {
          _this.video.play();
          _this.resizeVideo();
        },
        true
      );
  
      // secondly video
      // this.localStream2 = new MediaStream();
      // this.videoThumb = elements[1];
      // this.videoThumb.playsInline = true;
      // this.videoThumb.addEventListener('loadedmetadata', function () {
      //   _this.videoThumb.play();
      // }, true);
  
      this.videoTrackList = [];
      this.videoTrackIndex = Number.MAX_VALUE;
  
      this.ondisconnect = function () {};
    }
  
    async setupConnection(useWebSocket) {
      const _this = this;
      // close current RTCPeerConnection
      if (this.pc) {
        Logger.log("Close current PeerConnection");
        this.pc.close();
        this.pc = null;
      }
  
      if (useWebSocket) {
        this.signaling = new WebSocketSignaling();
      } else {
        this.signaling = new Signaling();
      }
  
      this.connectionId = uuid4();
  
      // Create peerConnection with proxy server and set up handlers
      this.pc = new Peer(this.connectionId, true);
      this.pc.addEventListener("disconnect", () => {
        _this.ondisconnect();
      });
      this.pc.addEventListener("trackevent", (e) => {
        const data = e.detail;
        if (data.track.kind == "video") {
          _this.videoTrackList.push(data.track);
        }
        if (data.track.kind == "audio") {
          console.log("Audio: ", data.track);
          _this.localStream.addTrack(data.track);
        }
        _this.switchVideo(_this.videoTrackIndex);
      });
      this.pc.addEventListener("sendoffer", (e) => {
        const offer = e.detail;
        _this.signaling.sendOffer(offer.connectionId, offer.sdp);
      });
      this.pc.addEventListener("sendanswer", (e) => {
        const answer = e.detail;
        _this.signaling.sendAnswer(answer.connectionId, answer.sdp);
      });
      this.pc.addEventListener("sendcandidate", (e) => {
        const candidate = e.detail;
        _this.signaling.sendCandidate(
          candidate.connectionId,
          candidate.candidate,
          candidate.sdpMid,
          candidate.sdpMLineIndex
        );
      });
  
      // Create data channel with proxy server and set up handlers
      this.channel = this.pc.createDataChannel(
        this.connectionId,
        "data_" + this.connectionId
      );
      this.channel.onopen = function () {
        Logger.log("Datachannel connected.");
      };
      this.channel.onerror = function (e) {
        Logger.log(
          "The error " +
            e.error.message +
            " occurred\n while handling data with proxy server."
        );
      };
      this.channel.onclose = function () {
        Logger.log("Datachannel disconnected.");
      };
      this.channel.onmessage = async (msg) => {
        // receive message from unity and operate message
        let data;
        // receive message data type is blob only on Firefox
        if (navigator.userAgent.indexOf("Firefox") != -1) {
          data = await msg.data.arrayBuffer();
        } else {
          data = msg.data;
        }
        const bytes = new Uint8Array(data);
        console.log(bytes);
        _this.videoTrackIndex = bytes[1];
        switch (bytes[0]) {
          case UnityEventType.SWITCH_VIDEO:
            _this.switchVideo(_this.videoTrackIndex);
            break;
        }
      };
  
      this.signaling.addEventListener("disconnect", async (e) => {
        const data = e.detail;
        if (_this.pc != null && _this.pc.connectionId == data.connectionId) {
          _this.ondisconnect();
        }
      });
      this.signaling.addEventListener("offer", async (e) => {
        const offer = e.detail;
        const desc = new RTCSessionDescription({ sdp: offer.sdp, type: "offer" });
        if (_this.pc != null) {
          await _this.pc.onGotDescription(offer.connectionId, desc);
        }
      });
      this.signaling.addEventListener("answer", async (e) => {
        const answer = e.detail;
        const desc = new RTCSessionDescription({
          sdp: answer.sdp,
          type: "answer",
        });
        if (_this.pc != null) {
          await _this.pc.onGotDescription(answer.connectionId, desc);
        }
      });
      this.signaling.addEventListener("candidate", async (e) => {
        const candidate = e.detail;
        const iceCandidate = new RTCIceCandidate({
          candidate: candidate.candidate,
          sdpMid: candidate.sdpMid,
          sdpMLineIndex: candidate.sdpMLineIndex,
        });
        if (_this.pc != null) {
          await _this.pc.onGotCandidate(candidate.connectionId, iceCandidate);
        }
      });
  
      // setup signaling
      await this.signaling.start();
    }
  
    resizeVideo() {
      const clientRect = this.video.getBoundingClientRect();
      const videoRatio = this.videoWidth / this.videoHeight;
      const clientRatio = clientRect.width / clientRect.height;
  
      this._videoScale =
        videoRatio > clientRatio
          ? clientRect.width / this.videoWidth
          : clientRect.height / this.videoHeight;
      const videoOffsetX =
        videoRatio > clientRatio
          ? 0
          : (clientRect.width - this.videoWidth * this._videoScale) * 0.5;
      const videoOffsetY =
        videoRatio > clientRatio
          ? (clientRect.height - this.videoHeight * this._videoScale) * 0.5
          : 0;
      this._videoOriginX = clientRect.left + videoOffsetX;
      this._videoOriginY = clientRect.top + videoOffsetY;
    }
  
    // switch streaming destination main video and secondly video
    switchVideo(indexVideoTrack) {
      this.video.srcObject = this.localStream;
      // this.videoThumb.srcObject = this.localStream2;
      if (this.videoTrackList.length > indexVideoTrack) {
        this.replaceTrack(this.localStream, this.videoTrackList[indexVideoTrack]);
      }
      // if (indexVideoTrack == 0) {
      //   this.replaceTrack(this.localStream, this.videoTrackList[0]);
      //   this.replaceTrack(this.localStream2, this.videoTrackList[1]);
      // }
      // else {
      //   this.replaceTrack(this.localStream, this.videoTrackList[1]);
      //   this.replaceTrack(this.localStream2, this.videoTrackList[0]);
      // }
    }
  
    // replace video track related the MediaStream
    replaceTrack(stream, newTrack) {
      const tracks = stream.getVideoTracks();
      for (const track of tracks) {
        if (track.kind == "video") {
          stream.removeTrack(track);
        }
      }
      stream.addTrack(newTrack);
    }
  
    get videoWidth() {
      return this.video.videoWidth;
    }
  
    get videoHeight() {
      return this.video.videoHeight;
    }
  
    get videoOriginX() {
      return this._videoOriginX;
    }
  
    get videoOriginY() {
      return this._videoOriginY;
    }
  
    get videoScale() {
      return this._videoScale;
    }
  
    sendMsg(msg) {
      if (this.channel == null) {
        return;
      }
      switch (this.channel.readyState) {
        case "connecting":
          Logger.log("Connection not ready");
          break;
        case "open":
          this.channel.send(msg);
          break;
        case "closing":
          Logger.log("Attempt to sendMsg message while closing");
          break;
        case "closed":
          Logger.log("Attempt to sendMsg message while connection closed.");
          break;
      }
    }
  
    async stop() {
      if (this.signaling) {
        await this.signaling.stop();
        this.signaling = null;
      }
  
      if (this.pc) {
        this.pc.close();
        this.pc = null;
      }
    }
  }
  