export default class CallService {
  private callId: string;
  private inCall: boolean;
  private peerId: number;
  private subscription;
  private localStream: MediaStream;
  private remoteStream: MediaStream;
  private needToAddStream: boolean = true;
  private pc: RTCPeerConnection;
  constructor(private ws) {
    this.callId = null;
    this.inCall = false;
    this.peerId = -1;
    this.subscription = null;
    this.pc = null;
    this.localStream = null;
    this.remoteStream = null;
    this.remoteStream = new MediaStream();
  }

  async connectToCall(callId: string): Promise<boolean> {
    if (this.inCall) throw new Error('Already connected to call');
    this.subscription = this.ws.subscribe(`call:${callId}`);
    const subscription = this.subscription;
    const self = this;
    return new Promise((resolve, reject) => {
      subscription.on('error', (e) => {
        console.error(e);
        resolve(false)
      });
      subscription.on('ready', () => {
        this.callId = callId;
        this.inCall = true;
        resolve(true)
      });
      subscription.on('close', self.close.bind(this));
      subscription.on('call:start', self.onCallStart.bind(this));
      subscription.on('call:standby', self.onCallStandby.bind(this));
      subscription.on('wrtc:sdp:offer', self.onRemoteOffer.bind(this));
      subscription.on('wrtc:sdp:answer', self.onRemoteAnswer.bind(this));
      subscription.on('wrtc:ice', self.onRemoteIce.bind(this));

    });
  }
  private send(event: string, payload: { [key: string]: any }) {
    this.subscription.emit(event, {
      id: this.peerId,
      ...payload
    })
  }
  async onCallStart(payload: { iceServers: RTCIceServer[], id: number }) {
    console.log(payload);
    this.peerId = payload.id;
    this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
    console.log('Created PeerConnection');
    this.setupPeerConnectionListeners();
    const sdp = await this.pc.createOffer();
    await this.pc.setLocalDescription(sdp);
    console.log('Local description Set');
    this.send('wrtc:sdp:offer', {
      sdp
    });
    return true;
  }

  async onCallStandby(payload: { iceServers: RTCIceServer[], id: number }) {
    console.log(payload);
    this.peerId = payload.id;
    this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
    console.log('Created PeerConnection');
    this.setupPeerConnectionListeners();
    return true;
  }

  setupPeerConnectionListeners() {
    this.pc.addEventListener("icecandidate", this.onLocalIce.bind(this));
    this.pc.addEventListener('connectionstatechange', event => {
      console.log(`PC Connection state: ${this.pc.connectionState}`);
      // if (this.pc.connectionState === 'connected') {
      //   // Peers connected!
      // }
    });

    this.pc.addEventListener('iceconnectionstatechange', event => {
      console.log('iceconnectionstatechange');
      console.log(this.pc.iceConnectionState);
    })

    this.pc.addEventListener('track', async (event) => {
      console.log('On remote track!');
      this.remoteStream.addTrack(event.track);
    });

    this.pc.addEventListener('icegatheringstatechange', event => {
      console.log('icegatheringstatechange', this.pc.iceGatheringState);
    });

    if (this.needToAddStream && this.localStream) {
      this.localStream.getTracks().forEach(t => {
        console.log('adding track to pc - in the event list');
        console.log(t);
        this.pc.addTrack(t, this.localStream);
      });
      this.needToAddStream = false;
    }
  }

  onLocalIce(event) {
    if (event.candidate) {
      console.log('Sending candidate');
      this.send('wrtc:ice', {
        ice: event.candidate
      });
    }

  }
  async onRemoteOffer(payload) {
    const offer = new RTCSessionDescription(payload.sdp);
    await this.pc.setRemoteDescription(offer);
    console.log('Remote offer Set');
    const sdp = await this.pc.createAnswer();
    this.send('wrtc:sdp:answer', {
      sdp
    });
    await this.pc.setLocalDescription(sdp);
    console.log('Local answer Set');
    return true;
  }
  async onRemoteAnswer(payload) {
    const answer = new RTCSessionDescription(payload.sdp);
    await this.pc.setRemoteDescription(answer);
    console.log('Remote answer Set');
    return true;
  }
  async onRemoteIce(payload) {
    const ice = payload.ice;
    await this.pc.addIceCandidate(ice);
    return true;
  }

  async getUserMedia(constraints: MediaStreamConstraints = { video: false, audio: true }) {
    if (this.localStream) return this.localStream;
    this.localStream = await navigator.mediaDevices.getUserMedia(constraints);
    if (this.pc) {
      if (this.needToAddStream && this.localStream) {
        this.localStream.getTracks().forEach(t => {
          console.log('adding track to pc - in get user media');
          this.pc.addTrack(t, this.localStream);
        });
        this.needToAddStream = false;
      }
    }
    return this.localStream;
  }

  getRemoteStream() {
    return this.remoteStream;
  }

  close() {
    if (this.subscription) this.subscription.close();
    this.subscription = null;
    this.inCall = false;
  }
}
