'use strict';
function CallRoom() {};
CallRoom.prototype.errorMessages = {};
CallRoom.prototype.me;
CallRoom.prototype.roomKey;
CallRoom.prototype.roomLink;
CallRoom.prototype.initiator;
CallRoom.prototype.pcConfig;
CallRoom.prototype.turnUrl = '';

CallRoom.prototype.dataChannelOption = {reliable:false};
CallRoom.prototype.pcConstraints = {
    "optional": [{DtlsSrtpKeyAgreement:true}]
};
CallRoom.prototype.mediaConstraints = {
    audio: true,
    video: true,
    DtlsSrtpKeyAgreement:true
};
CallRoom.prototype.offerConstraints = {
    offerToReceiveAudio: 1,
    offerToReceiveVideo: 1
};
CallRoom.prototype.answerConstraints ={
    mandatory: {
        OfferToReceiveAudio: true,
        OfferToReceiveVideo: true
    }
};
CallRoom.prototype.audioSendCodec = 'telephone-event/8000'; // 发送音频编码
CallRoom.prototype.audioRecvCodec = 'telephone-event/8000'; // 接收音频编码
CallRoom.prototype.videoSendInitialBitrate = ''; // 视频初始比特率
// CallRoom.prototype.videoRecvBitrate = '-1'; // 视频接收比特率
// CallRoom.prototype.videoSendBitrate = '-1'; // 视频发送比特率
// CallRoom.prototype.audioRecvBitrate = '-1'; // 音频接收比特率
// CallRoom.prototype.audioSendBitrate = '-1'; // 音频发送比特率
CallRoom.prototype.stereoscopic = false; // 是否是立体观察仪
CallRoom.prototype.stereo = false; // 是否添加立体声

CallRoom.prototype.localVideo;
CallRoom.prototype.miniVideo;
CallRoom.prototype.remoteVideo;
CallRoom.prototype.hasLocalStream;
CallRoom.prototype.localStream;
CallRoom.prototype.remoteStream;
CallRoom.prototype.pc;
CallRoom.prototype.dataChannelRec;
CallRoom.prototype.dataChannelSend;
CallRoom.prototype.sendInput;
CallRoom.prototype.sendButton;
CallRoom.prototype.receiveMessage;
CallRoom.prototype.status;
CallRoom.prototype.socket;
CallRoom.prototype.xmlhttp;
CallRoom.prototype.started = false;
CallRoom.prototype.turnDone = false;
CallRoom.prototype.websocketOpen = false;
CallRoom.prototype.signalingReady = false;
CallRoom.prototype.msgQueue = [];
CallRoom.prototype.card;
CallRoom.prototype.containerDiv;
CallRoom.prototype.isVideoMuted = false;
CallRoom.prototype.isAudioMuted = false;
CallRoom.prototype.startTime;
CallRoom.prototype.endTime;
CallRoom.prototype.gatheredIceCandidateTypes = {
    Local: {},
    Remote: {}
};
CallRoom.prototype.infoDivErrors = [];
CallRoom.prototype.stats;
CallRoom.prototype.getStatsTimer;

CallRoom.prototype.pushFunction;
CallRoom.prototype.cancelFunction;
CallRoom.prototype.resetFunction;
CallRoom.prototype.deviceData;

CallRoom.prototype.waitInterval;
CallRoom.prototype.websocketHeartbeatInterval;

/**
 * 初始化
 * @param params
 */
CallRoom.prototype.init = function(params) {
    var self = this;
    var errorMessages = self.errorMessages;
    if (errorMessages.length > 0) {
        for (var i = 0; i < errorMessages.length; ++i) {
            window.alert(errorMessages[i]);
        }
        return;
    }
    //控件参数初始化
    if(params.card){
        self.card = document.getElementById(params.card);
    }
    if(params.containerDiv){
        self.containerDiv = document.getElementById(params.containerDiv);
    }
    if(params.localVideo){
        self.localVideo = document.getElementById(params.localVideo);
        if(self.localVideo){
            self.localVideo.addEventListener('loadedmetadata', function() {});
        }
    }
    if(params.sendInput){
        self.sendInput = document.getElementById(params.sendInput);
    }
    if(params.status){
        self.status = document.getElementById(params.status);
    }
    if(params.sendButton){
        self.sendButton = document.getElementById(params.sendButton);
    }
    if(params.receiveMessage){
        self.receiveMessage = document.getElementById(params.receiveMessage);
    }
    if(params.miniVideo){
        self.miniVideo = document.getElementById(params.miniVideo);
    }
    if(params.remoteVideo){
        self.remoteVideo = document.getElementById(params.remoteVideo);
    }
    if(params.mediaConstraints){
        self.mediaConstraints = params.mediaConstraints;
    }
    if(params.pcConstraints){
        self.pcConstraints = params.pcConstraints;
    }
    if(params.offerConstraints){
        self.offerConstraints = params.offerConstraints;
    }
    if(params.turnUrl){
        self.turnUrl = params.turnUrl;
    }

    document.onkeydown = function(event) {
        switch (event.keyCode) {
            case 68:
                self.toggleAudioMute();
                return false;
            case 69:
                self.toggleVideoMute();
                return false;
            case 73:
                self.toggleInfoDiv();
                return false;
            default:
                return;
        }
    };

    window.onbeforeunload = function() {
        try{
            self.sendMessage({type: 'bye'});
        }catch (e){

        }finally {
            self.onHangup();
        }
    };
};

/**
 * 连接
 */
CallRoom.prototype.connect = function(url,pushFunction,cancelFunction,resetFunction,data) {
    var self = this;
    $.post(
        url,
        data.data,
        function(repJson){
            if (repJson.code == 0) {
                /**
                 * {"room":"","initiator":"","roomLink":"","tcpLink":"","roomKey":"","me":"","pcConfig":""}
                 * room 房间状态码 取值有noroom、busy、full、ok。 noroom表示已挂断  busy表示占线  full表示已有人接听 ok表示正常，可以通话
                 * initiator 进入房间顺序  0表示先进入，1表示后进入
                 * roomLink Websocket连接
                 * tcpLink TCP长连接参数 格式是"IP地址/端口/房间号/用户id"
                 * roomKey 房间号 请求的r
                 * me 用户id Websocket请求和通讯用到
                 * pcConfig turn和stun配置 中转服务器信息
                 */
                var info = repJson.info;

                if(info.room != 'ok'){
                    var tips = info.room == 'busy'?'占线':info.room == 'full'?'已有人接听':'已挂断';
                    alert(tips);
                    return;
                }
                self.pushFunction = pushFunction;
                self.cancelFunction = cancelFunction;
                self.resetFunction = resetFunction;
                self.deviceData = data;
                self.roomKey = info.roomKey;
                self.roomLink = info.roomLink;
                self.initiator = info.initiator;
                self.pcConfig = info.pcConfig;
                self.me = info.me;
                self.signalingReady = info.initiator == 1 ? true : false;
                //重设状态
                self.resetStatus(self);
                //打开通道
                self.openWebsocket();
                self.maybeRequestTurn();
                var mediaConstraints = self.mediaConstraints;
                if (mediaConstraints.audio === false && mediaConstraints.video === false) {
                    self.hasLocalStream = false;
                    //开始视频
                    self.maybeStart(self);
                } else {
                    self.hasLocalStream = true;
                    //获取用户多媒体 即摄像头与麦克风
                    self.doGetUserMedia(self);
                }
            }else{
                alert(repJson.msg);
            }
        },'json');
};

/**
 * 重设状态
 */
CallRoom.prototype.resetStatus = function(self) {
    if (!self.initiator) {
        self.setStatus('Waiting for someone to join: <a href=' + self.roomLink + '>' + self.roomLink + '</a>');
    } else {
        self.setStatus('Initializing...');
    }
};


/**
 * 打开通道
 */
CallRoom.prototype.websocketHeartbeat = function(self) {
    self.sendMessage({type: 'hi'});
};

/**
 * 打开通道
 */
CallRoom.prototype.openWebsocket = function() {
    trace('Opening channel.');
    var self = this;
    self.socket = new WebSocket(self.roomLink);
    self.socket.onopen = function() {//通道打开
        trace('Channel opened.');
        if(self.pushFunction!=null){
            self.pushFunction();
        }
        self.websocketOpen = true;

        self.websocketHeartbeatInterval = setInterval(self.websocketHeartbeat(self), 10000);

        //开始视频
        self.maybeStart(self);
    };
    self.socket.onmessage = function(message) {//接收服务器发过来的信息
        trace('S->C: ' + message.data);
        var msg = JSON.parse(message.data);
        if (msg.type === 'hi') {
            self.sendMessage({type: 'hi'});
        }else if (!self.initiator && !self.started) {
            if (msg.type === 'offer') {
                self.msgQueue.unshift(msg);
                self.signalingReady = true;
                //开始视频
                self.maybeStart(self);
            } else {
                self.msgQueue.push(msg);
            }
        } else {
            //处理信令信息
            self.processSignalingMessage(msg);
        }
    };
    self.socket.onerror = function() {//打开通道错误
        self.messageError('Channel error.');
    };
    self.socket.onclose = function() {//通道关闭
        trace('Channel closed.');

        clearInterval(self.websocketHeartbeatInterval);

        self.websocketOpen = false;
        self.socket = null;
        self.onRemoteHangup();
    };
};

/**
 * turn请求
 */
CallRoom.prototype.maybeRequestTurn = function() {
    var self = this;
    if (self.turnUrl === '') {
        self.turnDone = true;
        return;
    }
    for (var i = 0, len = self.pcConfig.iceServers.length; i < len; i++) {
        if(self.pcConfig.iceServers[i].urls instanceof Array){
            if (self.pcConfig.iceServers[i].urls[0].substr(0, 5) === 'turn:') {
                self.turnDone = true;
                return;
            }
        }else{
            if (self.pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
                self.turnDone = true;
                return;
            }
        }
    }
    var currentDomain = document.domain;
    if (currentDomain.search('localhost') === -1 && currentDomain.search('apprtc') === -1) {
        self.turnDone = true;
        return;
    }
    self.xmlhttp = new XMLHttpRequest();
    self.xmlhttp.onreadystatechange = function() {//turn请求回复
        if (self.xmlhttp.readyState !== 4) {
            return;
        }
        if (self.xmlhttp.status === 200) {
            var turnServer = JSON.parse(self.xmlhttp.responseText);
            var iceServers = self.createIceServers(turnServer.uris, turnServer.username, turnServer.password);
            if (iceServers !== null) {
                self.pcConfig.iceServers = self.pcConfig.iceServers.concat(iceServers);
            }
        } else {
            self.messageError('No TURN server; unlikely that media will traverse networks. If this persists please report it to discuss-webrtc@googlegroups.com.');
        }
        self.turnDone = true;
        //开始视频
        self.maybeStart(self);
    };
    self.xmlhttp.open('GET', turnUrl, true);
    self.xmlhttp.send();
};

/**
 * 获取用户多媒体 即摄像头与麦克风
 */
CallRoom.prototype.doGetUserMedia = function(self) {
    try {
        navigator.getUserMedia(self.mediaConstraints, function(stream) {// 获取多媒体成功
            trace('User has granted access to local media.');
            if(self.localVideo){
                attachMediaStream(self.localVideo, stream);
                self.localStream = stream;
            }else{
                self.hasLocalStream = false;
            }
            //开始视频
            self.maybeStart(self);
        }, function(error) {//获取多媒体错误
            var errorMessage = 'Failed to get access to local media. Error name was ' + error.name + '. Continuing without sending a stream.';
            self.messageError(errorMessage);
            // alert(errorMessage);
            self.hasLocalStream = false;
            //开始视频
            self.maybeStart(self);
        });
        trace('Requested access to local media with mediaConstraints:\n' + '  \'' + JSON.stringify(self.mediaConstraints) + '\'');
    } catch(e) {
        alert('getUserMedia() failed. Is this a WebRTC capable browser?');
        self.messageError('getUserMedia failed with exception: ' + e.message);
    }
};

/**
 * 创建端连接
 */
CallRoom.prototype.createPeerConnection = function() {
    var self = this;
    try {
        self.pc = new RTCPeerConnection(self.pcConfig, self.pcConstraints);
        self.pc.onicecandidate = function(event) {//ICE Candidate事件
            if (event.candidate) {
                if (self.pcConfig!=null && self.pcConfig.iceTransports === 'relay') {
                    if (event.candidate.candidate.search('relay') === -1) {
                        return;
                    }
                }
                //将自己的ICE发送给服务器
                self.sendMessage({
                    type: 'candidate',
                    label: event.candidate.sdpMLineIndex,
                    id: event.candidate.sdpMid,
                    candidate: event.candidate.candidate
                });
                self.noteIceCandidate('Local', self.iceCandidateType(event.candidate.candidate));
            } else {
                trace('End of candidates.');
            }
        };
        trace('Created RTCPeerConnnection with:\n  config: \'' + JSON.stringify(self.pcConfig) + '\';\n  constraints: \'' + JSON.stringify(self.pcConstraints) + '\'.');
    } catch(e) {
        self.messageError('Failed to create PeerConnection, exception: ' + e.message);
        alert('Cannot create RTCPeerConnection object; ' + 'WebRTC is not supported by this browser.');
        return;
    }
    self.pc.onaddstream = function(event) { // 远程端流添加
        trace('Remote stream added.');
        attachMediaStream(self.remoteVideo, event.stream);
        self.remoteStream = event.stream;
    };
    self.pc.onremovestream = function() {
        trace('Remote stream removed.');
    };
    self.pc.onsignalingstatechange = function() {
        if (self.pc) {
            trace('Signaling state changed to: ' + self.pc.signalingState);
        }
        //更新信息
        self.updateInfoDiv();
    };
    self.pc.oniceconnectionstatechange = function() {
        if (self.pc) {
            trace('ICE connection state changed to: ' + self.pc.iceConnectionState);
        }
        //更新信息
        self.updateInfoDiv();
    };

    self.pc.ondatachannel=function(event){
        self.dataChannelRec = event.channel;

        self.dataChannelRec.onerror = function(error){
            console.log("--------Rec Data Channel Error:",error);
        };
        self.dataChannelRec.onmessage = function(event){
            console.log("--------Got Rec Data Channel Message:",event.data);
            self.receiveMessage.innerText = event.data;
        };
        self.dataChannelRec.onopen = function(){
            console.log("--------Rec Data Channel open");
        };
        self.dataChannelRec.onclose = function(event){
            console.log("--------The Rec Data Channel is Closed");
        };
    };

    self.dataChannelSend = self.pc.createDataChannel("mydataChannel",self.dataChannelOption);
    self.dataChannelSend.onopen = function() {
        var readyState = self.dataChannelSend.readyState;
        trace('--------Send channel state is: ' + readyState);
        if (readyState == "open") {
            if(self.sendInput){
                self.sendInput.disabled = false;
                self.sendInput.focus();
                self.sendInput.placeholder = "";
            }
            if(self.sendButton){
                self.sendButton.disabled = false;
            }
        } else {
            if(self.sendInput){
                self.sendInput.disabled = true;
            }
            if(self.sendButton){
                self.sendButton.disabled = true;
            }
        }
    };
    self.dataChannelSend.onclose = function() {
        var readyState = self.dataChannelSend.readyState;
        trace('--------Send channel state is: ' + readyState);
        if (readyState == "open") {
            if(self.sendInput){
                self.sendInput.disabled = false;
                self.sendInput.focus();
                self.sendInput.placeholder = "";
            }
            if(self.sendButton){
                self.sendButton.disabled = false;
            }
        } else {
            if(self.sendInput){
                self.sendInput.disabled = true;
            }
            if(self.sendButton){
                self.sendButton.disabled = true;
            }
        }
    };
};

CallRoom.prototype.onSendData = function(){
    var self = this;
    if(self.dataChannelSend!=null && self.sendInput){
        console.log("Send Data Channel Message:"+self.sendInput.value);
        self.dataChannelSend.send(self.sendInput.value);
    }
};

/**
 * 开始视频
 */
CallRoom.prototype.maybeStart = function(self) {
    if (!self.started && self.signalingReady && self.websocketOpen && self.turnDone && (self.localStream || !self.hasLocalStream)) {
        self.startTime = window.performance.now();
        self.setStatus('Connecting...');
        trace('Creating PeerConnection.');
        //创建端连接
        self.createPeerConnection();
        if (self.hasLocalStream) {
            trace('Adding local stream.');
            self.pc.addStream(self.localStream);
        } else {
            trace('Not sending any stream.');
        }
        self.started = true;
        if (self.initiator) {
            //呼叫
            self.doCall();
        } else {
            //被呼叫
            self.calleeStart();
        }
    }
};

CallRoom.prototype.setStatus = function(state) {
    var self = this;
    if(self.status){
        self.status.innerHTML = state;
    }
};

/**
 * 呼叫
 */
CallRoom.prototype.doCall = function() {
    var self = this;
    trace('Sending offer to peer, with constraints: \n' + '  \'' + JSON.stringify(self.offerConstraints) + '\'.');
    self.pc.createOffer(function(sessionDescription) { // 设置自己的SDP并发送给服务器
        //设置接收音频编码
        sessionDescription.sdp = self.maybePreferAudioReceiveCodec(sessionDescription.sdp);
        //设置音频接收比特率
        sessionDescription.sdp = self.maybeSetAudioReceiveBitRate(sessionDescription.sdp);
        //设置视频发送比特率
        sessionDescription.sdp = self.maybeSetVideoReceiveBitRate(sessionDescription.sdp);
        self.pc.setLocalDescription(sessionDescription, function() {//创建SDP成功
            trace('Set session description success.');
        }, self.onSetSessionDescriptionError);
        self.sendMessage(sessionDescription);
    }, function(error) { // 创建SDP错误
        self.messageError('Failed to create session description: ' + error.toString());
    }, self.offerConstraints);
};

/**
 * 被呼叫
 */
CallRoom.prototype.calleeStart = function() {
    var self = this;
    while (self.msgQueue.length > 0) {
        //处理信令信息
        self.processSignalingMessage(self.msgQueue.shift());
    }
};

/**
 * 发送answer
 */
CallRoom.prototype.doAnswer = function() {
    var self = this;
    trace('Sending answer to peer.');
    self.pc.createAnswer(function(sessionDescription) { // 设置自己的SDP并发送给服务器
        //设置接收音频编码
        sessionDescription.sdp = self.maybePreferAudioReceiveCodec(sessionDescription.sdp);
        //设置音频接收比特率
        sessionDescription.sdp = self.maybeSetAudioReceiveBitRate(sessionDescription.sdp);
        //设置音频发送比特率
        sessionDescription.sdp = self.maybeSetVideoReceiveBitRate(sessionDescription.sdp);
        self.pc.setLocalDescription(sessionDescription, function() {//创建SDP成功
            trace('Set session description success.');
        }, self.onSetSessionDescriptionError);
        self.sendMessage(sessionDescription);
    }, function(error) { // 创建SDP错误
        self.messageError('Failed to create session description: ' + error.toString());
    }, self.answerConstraints);
};

/**
 * 设置远程端
 * @param message
 */
CallRoom.prototype.setRemote = function(message) {
    var self = this;
    if (self.stereo) {
        //添加立体声
        message.sdp = self.addStereo(message.sdp);
    }
    //设置发送音频编码
    message.sdp = self.maybePreferAudioSendCodec(message.sdp);
    //设置音频发送比特率
    message.sdp = self.maybeSetAudioSendBitRate(message.sdp);
    //设置视频发送比特率
    message.sdp = self.maybeSetVideoSendBitRate(message.sdp);
    //设置视频发送初始最大和最小比特率
    message.sdp = self.maybeSetVideoSendInitialBitRate(message.sdp);
    //设置远程端SDP
    self.pc.setRemoteDescription(new RTCSessionDescription(message), function() {
        trace('Set remote session description success.');
        var remoteStreams = self.pc.getRemoteStreams();
        if (remoteStreams.length > 0 && remoteStreams[0].getVideoTracks().length > 0) {
            trace('Waiting for remote video.');
            self.waitInterval = setInterval(self.waitForRemoteVideo(self), 100);
        } else {
            trace('No remote video stream; not waiting for media to arrive.');
            self.transitionToActive();
        }
    }, self.onSetSessionDescriptionError);
};

/**
 * 发送消息给服务器
 * @param message
 */
CallRoom.prototype.sendMessage = function(message) {
    var self = this;
    if(self.socket){
        var msgString = JSON.stringify(message);
        trace('C->S: ' + msgString);
        self.socket.send(JSON.stringify({"r":self.roomKey,"u":self.me,"message":message}));
    }
};

/**
 * 处理信令信息
 * @param message
 */
CallRoom.prototype.processSignalingMessage = function(message) {
    var self = this;
    if (!self.started) {
        self.messageError('peerConnection has not been created yet!');
        return;
    }
    if (message.type === 'offer') {
        self.setRemote(message);
        self.doAnswer();
    } else if (message.type === 'answer') {
        self.setRemote(message);
    } else if (message.type === 'candidate') {
        var candidate = new RTCIceCandidate({
            sdpMLineIndex: message.label,
            candidate: message.candidate
        });
        self.noteIceCandidate('Remote', self.iceCandidateType(message.candidate));
        self.pc.addIceCandidate(candidate, self.onAddIceCandidateSuccess, self.onAddIceCandidateError);
    } else if (message.type === 'bye') {
        self.onRemoteHangup();
    }
};

/**
 * 添加ICE Candidate成功
 */
CallRoom.prototype.onAddIceCandidateSuccess = function() {
    trace('Remote candidate added successfully.');
};

/**
 * 添加ICE Candidate错误
 * @param error
 */
CallRoom.prototype.onAddIceCandidateError = function(error) {
    var self = this;
    self.messageError('Failed to add remote candidate: ' + error.toString());
};

/**
 * 消息错误
 * @param msg
 */
CallRoom.prototype.messageError = function(msg) {
    var self = this;
    trace(msg);
    self.infoDivErrors.push(msg);
    //更新信息
    self.updateInfoDiv();
    self.showInfoDiv();
};

/**
 * 设置SDP错误
 * @param error
 */
CallRoom.prototype.onSetSessionDescriptionError = function(error) {
    var self = this;
    self.messageError('Failed to set session description: ' + error.toString());
};

/**
 * ICE Candidate 类型
 * @param candidateSDP
 * @returns {*}
 */
CallRoom.prototype.iceCandidateType = function(candidateSDP) {
    switch (candidateSDP.split(' ')[7]) {
        case 'host':
            return 'HOST';
        case 'srflx':
            return 'STUN';
        case 'relay':
            return 'TURN';
        default:
            return 'UNKNOWN';
    }
};

/**
 * 刷新状态
 */
CallRoom.prototype.refreshStats = function() {
    var self = this;
    if (self.pc) {
        self.pc.getStats(function(response) {
            self.stats = response.result();
            //更新信息
            self.updateInfoDiv();
        });
    }
};

/**
 * 提取统计
 * @param stats
 * @param statObj
 * @param statName
 * @returns {*}
 */
CallRoom.prototype.extractStatAsInt = function(stats, statObj, statName) {
    var self = this;
    //提取统计
    var str = self.extractStat(stats, statObj, statName);
    if (str) {
        var val = self.parseInt(str);
        if (val !== -1) {
            return val;
        }
    }
    return null;
};

/**
 * 提取统计
 * @param stats
 * @param statObj
 * @param statName
 * @returns {*}
 */
CallRoom.prototype.extractStat = function(stats, statObj, statName) {
    var self = this;
    //获取状态
    var report = self.getStatsReport(stats, statObj, statName);
    if (report && report.names().indexOf(statName) !== -1) {
        return report.stat(statName);
    }
    return null;
};

/**
 * 获取状态
 * @param stats
 * @param statObj
 * @param statName
 * @param statVal
 * @returns {*}
 */
CallRoom.prototype.getStatsReport = function(stats, statObj, statName, statVal) {
    if (stats) {
        for (var i = 0; i < stats.length; ++i) {
            var report = stats[i];
            if (report.type === statObj) {
                var found = true;
                if (statName) {
                    var val = report.stat(statName);
                    found = statVal !== undefined ? val === statVal: val;
                }
                if (found) {
                    return report;
                }
            }
        }
    }
};

/**
 * 计算延时
 * @param captureStart
 * @param remoteVideoCurrentTime
 * @returns {*}
 */
CallRoom.prototype.computeE2EDelay = function(captureStart, remoteVideoCurrentTime) {
    if (captureStart) {
        var nowNTP = Date.now() + 2208988800000;
        var e2eDelay = nowNTP - captureStart - remoteVideoCurrentTime * 1000;
        return e2eDelay.toFixed(0);
    }
    return null;
};

/**
 * 挂起
 */
CallRoom.prototype.onHangup = function() {
    var self = this;
    trace('Hanging up.');
    self.transitionToDone();
    if(self.localStream){
        self.localStream.getTracks().forEach(function (track) { track.stop(); });
    }
    self.stop();
    if(self.socket){
        self.socket.close();
    }
};

/**
 * 远程端挂起
 */
CallRoom.prototype.onRemoteHangup = function() {
    var self = this;
    trace('Session terminated.');
    self.initiator = 0;
    self.transitionToWaiting();
    self.stop();
};

/**
 * 停止
 */
CallRoom.prototype.stop = function() {
    var self = this;
    self.started = false;
    self.signalingReady = false;
    self.isAudioMuted = false;
    self.isVideoMuted = false;
    if(self.pc){
        self.pc.close();
        self.pc = null;
    }
    if(self.resetFunction){
        self.resetFunction();
    }
    self.remoteStream = null;
    self.msgQueue.length = 0;
};

/**
 * 等待远程端视频
 */
CallRoom.prototype.waitForRemoteVideo = function(self) {
    if (self.remoteVideo.currentTime > 0) {
        clearInterval(self.waitInterval);
        self.transitionToActive();
    }
};

/**
 * 变到活动
 */
CallRoom.prototype.transitionToActive = function() {
    var self = this;
    self.endTime = window.performance.now();
    trace('Call setup time: ' + (self.endTime - self.startTime).toFixed(0) + 'ms.');
    //更新信息
    self.updateInfoDiv();
    if (self.stereoscopic) {
        setupStereoscopic(self.remoteVideo, document.getElementById('remoteCanvas'));
    } else {
        if(self.miniVideo && self.localVideo){
            reattachMediaStream(self.miniVideo, self.localVideo);
        }
    }
    setTimeout(function() {
        if(self.localVideo){
            self.localVideo.src = '';
        }
    },800);
    self.setStatus('<input type=\'button\' id=\'hangup\' value=\'Hang up\' ' + 'onclick=\'onHangup()\' />');
};

/**
 * 变到等待
 */
CallRoom.prototype.transitionToWaiting = function() {
    var self = this;
    self.startTime = self.endTime = null;
    if(self.miniVideo && self.localVideo) {
        reattachMediaStream(self.localVideo, self.miniVideo);
    }
    setTimeout(function() {
        self.miniVideo.src = '';
        self.remoteVideo.src = '';},800);
    //重设状态
    self.resetStatus(self);
};

/**
 * 变到完成
 */
CallRoom.prototype.transitionToDone = function() {
    var self = this;
    self.setStatus('You have left the call. <a href=' + self.roomLink + '>Click here</a> to rejoin.');
};

/**
 * 全屏
 */
CallRoom.prototype.enterFullScreen = function() {
    var self = this;
    var element = event.target.id === 'remoteCanvas' ? event.target : self.containerDiv;
    element.webkitRequestFullScreen();
};

/**
 * 记录ICE Candidate
 * @param location
 * @param type
 */
CallRoom.prototype.noteIceCandidate = function(location, type) {
    var self = this;
    var types = self.gatheredIceCandidateTypes[location];
    if (!types[type]) {
        types[type] = 1;
    } else {++types[type];
    }
    //更新信息
    self.updateInfoDiv();
};

CallRoom.prototype.getInfoDiv = function() {
    return document.getElementById('infoDiv');
};

/**
 * 创建行
 * @param label
 * @param value
 * @returns {string}
 */
CallRoom.prototype.buildLine = function(label, value) {
    var columnWidth = 12;
    var line = '';
    if (label) {
        line += label + ':';
        while (line.length < columnWidth) {
            line += ' ';
        }
        if (value) {
            line += value;
        }
    }
    line += '\n';
    return line;
};

/**
 * 更新信息
 */
CallRoom.prototype.updateInfoDiv = function() {
    var self = this;
    var contents = '<pre>';
    if (self.pc) {
        //提取统计
        var rtt = self.extractStatAsInt(self.stats, 'ssrc', 'googRtt');
        var captureStart = self.extractStatAsInt(self.stats, 'ssrc', 'googCaptureStartNtpTimeMs');
        var e2eDelay = self.computeE2EDelay(captureStart, self.remoteVideo.currentTime);
        //获取状态
        var activeCandPair = self.getStatsReport(self.stats, 'googCandidatePair', 'googActiveConnection', 'true');
        var localAddr=null,remoteAddr=null;
        if (activeCandPair) {
            localAddr = activeCandPair.stat('googLocalAddress');
            remoteAddr = activeCandPair.stat('googRemoteAddress');
        }
        contents += self.buildLine('States');
        contents += self.buildLine('Signaling', self.pc.signalingState);
        contents += self.buildLine('Gathering', self.pc.iceGatheringState);
        contents += self.buildLine('Connection', self.pc.iceConnectionState);
        for (var endpoint in self.gatheredIceCandidateTypes) {
            var types = [];
            for (var type in self.gatheredIceCandidateTypes[endpoint]) {
                types.push(type + ':' + self.gatheredIceCandidateTypes[endpoint][type]);
            }
            types.sort();
            contents += self.buildLine(endpoint, types.join(' '));
        }
        if (localAddr && remoteAddr) {
            contents += self.buildLine('LocalAddr', localAddr);
            contents += self.buildLine('RemoteAddr', remoteAddr);
        }
        contents += self.buildLine();
        contents += self.buildLine('Stats');
        if (self.endTime !== null) {
            contents += self.buildLine('Setup time', (self.endTime - self.startTime).toFixed(0).toString() + 'ms');
        }
        if (rtt !== null) {
            contents += self.buildLine('RTT', rtt.toString() + 'ms');
        }
        if (e2eDelay !== null) {
            contents += self.buildLine('End to end', e2eDelay.toString() + 'ms');
        }
    }
    contents += '</pre>';
    var div = self.getInfoDiv();
    if(div!=null){
        div.innerHTML = contents;
        for (var msg in infoDivErrors) {
            div.innerHTML += '<p style="background-color: red; color: yellow;">' + self.infoDivErrors[msg] + '</p>';
        }
    }
};

CallRoom.prototype.isInfoDivVisible = function() {
    var self = this;
    if(self.getInfoDiv()!=null){
        return self.getInfoDiv().style.display === 'block';
    }else{
        return false;
    }
};

CallRoom.prototype.showInfoDiv = function() {
    var self = this;
    if (self.getStatsTimer) {
        throw 'Inconsistent infodiv state';
    }
    var div = self.getInfoDiv();
    if(div!=null){
        div.style.display = 'block';
    }
    self.refreshStats();
    self.getStatsTimer = setInterval(self.refreshStats, 1000);
};

CallRoom.prototype.hideInfoDiv = function() {
    var self = this;
    var div = self.getInfoDiv();
    if(div!=null){
        div.style.display = 'none';
    }
    clearInterval(self.getStatsTimer);
};

CallRoom.prototype.toggleInfoDiv = function() {
    var self = this;
    if (self.isInfoDivVisible()) {
        self.hideInfoDiv();
    } else {
        self.showInfoDiv();
    }
};

CallRoom.prototype.toggleVideoMute = function() {
    var self = this;
    var videoTracks = self.localStream.getVideoTracks();
    if (videoTracks.length === 0) {
        trace('No local video available.');
        return;
    }
    trace('Toggling video mute state.');
    var i;
    if (self.isVideoMuted) {
        for (i = 0; i < videoTracks.length; i++) {
            videoTracks[i].enabled = true;
        }
        trace('Video unmuted.');
    } else {
        for (i = 0; i < videoTracks.length; i++) {
            videoTracks[i].enabled = false;
        }
        trace('Video muted.');
    }
    self.isVideoMuted = !self.isVideoMuted;
};

CallRoom.prototype.toggleAudioMute = function() {
    var self = this;
    var audioTracks = self.localStream.getAudioTracks();
    if (audioTracks.length === 0) {
        trace('No local audio available.');
        return;
    }
    trace('Toggling audio mute state.');
    var i;
    if (self.isAudioMuted) {
        for (i = 0; i < audioTracks.length; i++) {
            audioTracks[i].enabled = true;
        }
        trace('Audio unmuted.');
    } else {
        for (i = 0; i < audioTracks.length; i++) {
            audioTracks[i].enabled = false;
        }
        trace('Audio muted.');
    }
    self.isAudioMuted = !self.isAudioMuted;
};

/**
 * 设置音频发送比特率
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybeSetAudioSendBitRate = function(sdp) {
    var self = this;
    if (!self.audioSendBitrate) {
        return sdp;
    }
    trace('Prefer audio send bitrate: ' + self.audioSendBitrate);
    //设置比特率
    return self.preferBitRate(sdp, self.audioSendBitrate, 'audio');
};

/**
 * 设置音频接收比特率
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybeSetAudioReceiveBitRate = function(sdp) {
    var self = this;
    if (!self.audioRecvBitrate) {
        return sdp;
    }
    trace('Prefer audio receive bitrate: ' + self.audioRecvBitrate);
    //设置比特率
    return self.preferBitRate(sdp, self.audioRecvBitrate, 'audio');
};

/**
 * 设置视频接收比特率
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybeSetVideoReceiveBitRate = function(sdp) {
    var self = this;
    if (!self.videoRecvBitrate) {
        return sdp;
    }
    trace('Prefer video receive bitrate: ' + self.videoRecvBitrate);
    return self.preferBitRate(sdp, self.videoRecvBitrate, 'video');
};

/**
 * 设置视频发送比特率
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybeSetVideoSendBitRate = function(sdp) {
    var self = this;
    if (!self.videoSendBitrate) {
        return sdp;
    }
    trace('Prefer video send bitrate: ' + self.videoSendBitrate);
    return self.preferBitRate(sdp, self.videoSendBitrate, 'video');
};

/**
 * 设置比特率
 * @param sdp
 * @param bitrate
 * @param mediaType
 * @returns {string|*}
 */
CallRoom.prototype.preferBitRate = function(sdp, bitrate, mediaType) {
    var self = this;
    var sdpLines = sdp.split('\r\n');
    var mLineIndex = self.findLine(sdpLines, 'm=', mediaType);
    if (mLineIndex === null) {
        self.messageError('Failed to add bandwidth line to sdp, as no m-line found');
        return sdp;
    }
    var nextMLineIndex = self.findLineInRange(sdpLines, mLineIndex + 1, -1, 'm=');
    if (nextMLineIndex === null) {
        nextMLineIndex = sdpLines.length;
    }
    var cLineIndex = self.findLineInRange(sdpLines, mLineIndex + 1, nextMLineIndex, 'c=');
    if (cLineIndex === null) {
        self.messageError('Failed to add bandwidth line to sdp, as no c-line found');
        return sdp;
    }
    var bLineIndex = self.findLineInRange(sdpLines, cLineIndex + 1, nextMLineIndex, 'b=AS');
    if (bLineIndex) {
        sdpLines.splice(bLineIndex, 1);
    }
    var bwLine = 'b=AS:' + bitrate;
    sdpLines.splice(cLineIndex + 1, 0, bwLine);
    sdp = sdpLines.join('\r\n');
    return sdp;
};

/**
 * 设置视频发送初始最大和最小比特率
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybeSetVideoSendInitialBitRate = function(sdp) {
    var self = this;
    if (!self.videoSendInitialBitrate) {
        return sdp;
    }
    var maxBitrate = self.videoSendInitialBitrate;
    if (self.videoSendBitrate) {
        if (self.videoSendInitialBitrate > self.videoSendBitrate) {
            self.messageError('Clamping initial bitrate to max bitrate of ' + self.videoSendBitrate + ' kbps.');
            self.videoSendInitialBitrate = self.videoSendBitrate;
        }
        maxBitrate = self.videoSendBitrate;
    }
    var sdpLines = sdp.split('\r\n');
    var mLineIndex = self.findLine(sdpLines, 'm=', 'video');
    if (mLineIndex === null) {
        self.messageError('Failed to find video m-line');
        return sdp;
    }
    var vp8RtpmapIndex = self.findLine(sdpLines, 'a=rtpmap', 'VP8/90000');
    var vp8Payload = self.getCodecPayloadType(sdpLines[vp8RtpmapIndex]);
    var vp8Fmtp = 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + self.videoSendInitialBitrate.toString() + '; x-google-max-bitrate=' + maxBitrate.toString();
    console.log(vp8Fmtp);
    sdpLines.splice(vp8RtpmapIndex + 1, 0, vp8Fmtp);
    return sdpLines.join('\r\n');
};

/**
 * 设置发送音频编码
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybePreferAudioSendCodec = function(sdp) {
    var self = this;
    if (self.audioSendCodec === '') {
        trace('No preference on audio send codec.');
        return sdp;
    }
    trace('Prefer audio send codec: ' + self.audioSendCodec);
    //设置音频编码
    return self.preferAudioCodec(sdp, self.audioSendCodec);
};

/**
 * 设置接收音频编码
 * @param sdp
 * @returns {*}
 */
CallRoom.prototype.maybePreferAudioReceiveCodec = function(sdp) {
    var self = this;
    if (self.audioRecvCodec === '') {
        trace('No preference on audio receive codec.');
        return sdp;
    }
    trace('Prefer audio receive codec: ' + self.audioRecvCodec);
    //设置音频编码
    return self.preferAudioCodec(sdp, self.audioRecvCodec);
};

/**
 * 设置音频编码
 * @param sdp
 * @param codec
 * @returns {string|*}
 */
CallRoom.prototype.preferAudioCodec = function(sdp, codec) {
    var self = this;
    var sdpLines = sdp.split('\r\n');
    var mLineIndex = self.findLine(sdpLines, 'm=', 'audio');
    if (mLineIndex === null) {
        return sdp;
    }
    var codecIndex = self.findLine(sdpLines, 'a=rtpmap', codec);
    if (codecIndex) {
        var payload = self.getCodecPayloadType(sdpLines[codecIndex]);
        if (payload) {
            //设置默认编码
            sdpLines[mLineIndex] = self.setDefaultCodec(sdpLines[mLineIndex], payload);
        }
    }
    sdp = sdpLines.join('\r\n');
    return sdp;
};

/**
 * 添加立体声
 * @param sdp
 * @returns {string|*}
 */
CallRoom.prototype.addStereo = function(sdp) {
    var self = this;
    var sdpLines = sdp.split('\r\n');
    var opusIndex = self.findLine(sdpLines, 'a=rtpmap', 'opus/48000');
    var opusPayload=null;
    if (opusIndex) {
        opusPayload = self.getCodecPayloadType(sdpLines[opusIndex]);
    }
    var fmtpLineIndex = self.findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
    if (fmtpLineIndex === null) {
        return sdp;
    }
    sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1');
    sdp = sdpLines.join('\r\n');
    return sdp;
};

/**
 * 查找行
 * @param sdpLines
 * @param prefix
 * @param substr
 */
CallRoom.prototype.findLine = function(sdpLines, prefix, substr) {
    var self = this;
    return self.findLineInRange(sdpLines, 0, -1, prefix, substr);
};

/**
 * 在给定范围查找行
 * @param sdpLines
 * @param startLine
 * @param endLine
 * @param prefix
 * @param substr
 * @returns {*}
 */
CallRoom.prototype.findLineInRange = function(sdpLines, startLine, endLine, prefix, substr) {
    var realEndLine = endLine !== -1 ? endLine: sdpLines.length;
    for (var i = startLine; i < realEndLine; ++i) {
        if (sdpLines[i].indexOf(prefix) === 0) {
            if (!substr || sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
                return i;
            }
        }
    }
    return null;
};

/**
 * 获取编码负载类型
 * @param sdpLine
 * @returns {null}
 */
CallRoom.prototype.getCodecPayloadType = function(sdpLine) {
    var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
    var result = sdpLine.match(pattern);
    return (result && result.length === 2) ? result[1] : null;
};

/**
 * 设置默认编码
 * @param mLine
 * @param payload
 * @returns {string}
 */
CallRoom.prototype.setDefaultCodec = function(mLine, payload) {
    var elements = mLine.split(' ');
    var newLine = [];
    var index = 0;
    for (var i = 0; i < elements.length; i++) {
        if (index === 3) {
            newLine[index++] = payload;
        }
        if (elements[i] !== payload) {
            newLine[index++] = elements[i];
        }
    }
    return newLine.join(' ');
};