/*
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
 /* eslint-env node */
'use strict';

describe('establishes a connection', () => {
  let pc1;
  let pc2;
  function noop() {}
  function throwError(err) {
    console.error(err.toString());
    throw err;
  }

  function negotiate(pc, otherPc) {
    return pc.createOffer()
    .then(function(offer) {
      return pc.setLocalDescription(offer);
    }).then(function() {
      return otherPc.setRemoteDescription(pc.localDescription);
    }).then(function() {
      return otherPc.createAnswer();
    }).then(function(answer) {
      return otherPc.setLocalDescription(answer);
    }).then(function() {
      return pc.setRemoteDescription(otherPc.localDescription);
    });
  }

  beforeEach(() => {
    pc1 = new RTCPeerConnection(null);
    pc2 = new RTCPeerConnection(null);

    pc1.onicecandidate = event => pc2.addIceCandidate(event.candidate);
    pc2.onicecandidate = event => pc1.addIceCandidate(event.candidate);
  });
  afterEach(() => {
    pc1.close();
    pc2.close();
  });

  it('with legacy callbacks', (done) => {
    pc1.onicecandidate = function(event) {
      pc2.addIceCandidate(event.candidate, noop, throwError);
    };
    pc2.onicecandidate = function(event) {
      pc1.addIceCandidate(event.candidate, noop, throwError);
    };
    pc1.oniceconnectionstatechange = function() {
      if (pc1.iceConnectionState === 'connected' ||
          pc1.iceConnectionState === 'completed') {
        done();
      }
    };

    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      pc1.addStream(stream);

      pc1.createOffer(
        function(offer) {
          pc1.setLocalDescription(offer,
            function() {
              pc2.setRemoteDescription(offer,
                function() {
                  pc2.createAnswer(
                    function(answer) {
                      pc2.setLocalDescription(answer,
                        function() {
                          pc1.setRemoteDescription(answer, noop, throwError);
                        },
                        throwError
                      );
                    },
                    throwError
                  );
                },
                throwError
              );
            },
            throwError
          );
        },
        throwError
      );
    });
  });

  it('with promises', (done) => {
    pc1.oniceconnectionstatechange = function() {
      if (pc1.iceConnectionState === 'connected' ||
          pc1.iceConnectionState === 'completed') {
        done();
      }
    };

    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      pc1.addStream(stream);
      return negotiate(pc1, pc2);
    })
    .catch(throwError);
  });

  it('with streams in both directions', (done) => {
    pc1.oniceconnectionstatechange = function() {
      if (pc1.iceConnectionState === 'connected' ||
          pc1.iceConnectionState === 'completed') {
        done();
      }
    };

    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      pc1.addStream(stream);
      pc2.addStream(stream);
      return negotiate(pc1, pc2);
    })
    .catch(throwError);
  });

  describe('with addTrack', () => {
    it('and all tracks of a stream', (done) => {
      pc1.oniceconnectionstatechange = function() {
        if (pc1.iceConnectionState === 'connected' ||
            pc1.iceConnectionState === 'completed') {
          done();
        }
      };

      pc2.onaddstream = function(event) {
        expect(event).to.have.property('stream');
        expect(event.stream.getAudioTracks()).to.have.length(1);
        expect(event.stream.getVideoTracks()).to.have.length(1);
      };

      var constraints = {audio: true, video: true};
      navigator.mediaDevices.getUserMedia(constraints)
      .then(function(stream) {
        stream.getTracks().forEach(function(track) {
          pc1.addTrack(track, stream);
        });
        return negotiate(pc1, pc2);
      })
      .catch(throwError);
    });

    it('but only the audio track of an av stream', (done) => {
      pc1.oniceconnectionstatechange = function() {
        if (pc1.iceConnectionState === 'connected' ||
            pc1.iceConnectionState === 'completed') {
          done();
        }
      };

      pc2.onaddstream = function(event) {
        expect(event).to.have.property('stream');
        expect(event.stream.getAudioTracks()).to.have.length(1);
        expect(event.stream.getVideoTracks()).to.have.length(0);
      };

      var constraints = {audio: true, video: true};
      navigator.mediaDevices.getUserMedia(constraints)
      .then(function(stream) {
        stream.getAudioTracks().forEach(function(track) {
          pc1.addTrack(track, stream);
        });
        return negotiate(pc1, pc2);
      })
      .catch(throwError);
    });

    it('as two streams', (done) => {
      let streams = [];
      pc1.oniceconnectionstatechange = function() {
        if (pc1.iceConnectionState === 'connected' ||
            pc1.iceConnectionState === 'completed') {
          expect(streams).to.have.length(2);
          done();
        }
      };

      pc2.onaddstream = function(event) {
        expect(event).to.have.property('stream');
        expect(event.stream.getTracks()).to.have.length(1);
        streams.push(event.stream);
      };

      var constraints = {audio: true, video: true};
      navigator.mediaDevices.getUserMedia(constraints)
      .then(function(stream) {
        var audioStream = new MediaStream(stream.getAudioTracks());
        var videoStream = new MediaStream(stream.getVideoTracks());
        audioStream.getTracks().forEach(function(track) {
          pc1.addTrack(track, audioStream);
        });
        videoStream.getTracks().forEach(function(track) {
          pc1.addTrack(track, videoStream);
        });
        return negotiate(pc1, pc2);
      })
      .catch(throwError);
    });
  });

  it('with no explicit end-of-candidates', function(done) {
    pc1.oniceconnectionstatechange = function() {
      if (pc1.iceConnectionState === 'connected' ||
          pc1.iceConnectionState === 'completed') {
        done();
      }
    };

    pc1.onicecandidate = (event) => {
      if (event.candidate) {
        pc2.addIceCandidate(event.candidate, noop, throwError);
      }
    };
    pc2.onicecandidate = (event) => {
      if (event.candidate) {
        pc1.addIceCandidate(event.candidate, noop, throwError);
      }
    };

    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      stream.getTracks().forEach(function(track) {
        pc1.addTrack(track, stream);
      });
      return negotiate(pc1, pc2);
    })
    .catch(throwError);
  });

  describe('with datachannel', function() {
    it('establishes a connection', (done) => {
      pc1.oniceconnectionstatechange = function() {
        if (pc1.iceConnectionState === 'connected' ||
            pc1.iceConnectionState === 'completed') {
          done();
        }
      };

      pc1.createDataChannel('foo');
      negotiate(pc1, pc2)
      .catch(throwError);
    });
  });

  it('and calls the video loadedmetadata', (done) => {
    pc2.addEventListener('addstream', function(e) {
      var v = document.createElement('video');
      v.autoplay = true;
      v.addEventListener('loadedmetadata', function() {
        done();
      });
      v.srcObject = e.stream;
    });
    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      stream.getTracks().forEach(function(track) {
        pc1.addTrack(track, stream);
      });
      return negotiate(pc1, pc2);
    })
    .catch(throwError);
  });

  it('and triggers the connectionstatechange event', (done) => {
    pc1.onconnectionstatechange = function() {
      if (pc1.connectionState === 'connected') {
        done();
      }
    };

    var constraints = {video: true};
    navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      pc1.addStream(stream);
      return negotiate(pc1, pc2);
    })
    .catch(throwError);
  });
});
