# src/Health.js

    import 'platform';
    import util from './Util.js'
    import l from './Logger.js'

    class Health{
      constructor(ctx){
        this.interval=5000;
        this.statsReportTimer = null;
        this.context = ctx;
        this.oldStats = null;
        this.result = null;
        this.fractionLost = {
    			audio: [
    				{rating: 1, fromAflost: 0, toAflost: 50},
    				{rating: 2, fromAflost: 51, toAflost: 150},
    				{rating: 3, fromAflost: 151, toAflost: 250},
    				{rating: 4, fromAflost: 251, toAflost: 350},
    				{rating: 5, fromAflost: 351, toAflost: 9999999},
    			],
    			video: [
    				{rating: 1, fromAflost: 0, toAflost: 40},
    				{rating: 2, fromAflost: 41, toAflost: 55},
    				{rating: 3, fromAflost: 56, toAflost: 70},
    				{rating: 4, fromAflost: 71, toAflost: 90},
    				{rating: 5, fromAflost: 91, toAflost: 9999999},
    			]
    		};
      }
      stop(){
    		if(this.statsReportTimer){
    			window.clearInterval(this.statsReportTimer);
    			this.statsReportTimer = null;
    		}
    	}

      start(){
        if (this.context.isCaller===true)return; // master만 health 던지자
        l.i("Health is start w/interval:"+ this.interval);
        if(this.statsReportTimer){
          window.clearInterval(this.statsReportTimer);
          this.statsReportTimer = null;
        }

        this.statsReportTimer = window.setInterval(util.bind(function(){
          this.getStats(util.bind(function(stats){
            var i = 0,
              len = 0,
              attr = null,
              result = {
                localCandidate: "",
                remoteCandidate: "",
                localFrameWidth : "",
                localFrameHeight : "",
                remoteFrameWidth: "",
                remoteFrameHeight: "",
                localFrameRate: "",
                remoteFrameRate: "",
                availableSendBandwidth: 0,
                availableReceiveBandwidth: 0,
                rtt: 0,
                rttRating: "",
                localAudioFractionLost: 0,
                localVideoFractionLost: 0,
                localAudioFractionRating: 0,
                localVideoFractionRating: 0,
                remoteAudioFractionLost: 0,
                remoteVideoFractionLost: 0,
                remoteAudioFractionRating: 0,
                remoteVideoFractionRating: 0,
                fractionRating: 0,
                localAudioPacketsLost: 0,
                remoteAudioPaketsLost: 0,
                localVideoPacketsLost: 0,
                remoteVideoPacketsLost: 0,
              },
              nowLocalAPLost = 0, nowLocalVPLost = 0,
              nowLocalAPSent = 0, nowLocalVPSent = 0,
              oldLocalAPLost = 0, oldLocalVPLost = 0,
              oldLocalAPSent = 0, oldLocalVPSent = 0,
              nowRemoteAPLost = 0, nowRemoteVPLost = 0,
              nowRemoteAPReceived = 0, nowRemoteVPReceived = 0,
              oldRemoteAPLost = 0, oldRemoteVPLost = 0,
              oldRemoteAPReceived = 0, oldRemoteVPReceived = 0,
              oldStats = this.oldStats,
              ffAudioRtt,
              ffVideoRtt,
              message;

            if(platform.name === "Firefox"){
              for(attr in stats){
                if(stats[attr].type === "candidatepair" && stats[attr].selected === true){
                  result.localCandidate = stats[stats[attr].localCandidateId].candidateType;
                  result.remoteCandidate = stats[stats[attr].remoteCandidateId].candidateType;
                  continue;
                }

                if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "audio" && stats[attr].isRemote === true){
                  if(stats[attr].hasOwnProperty("mozRtt")){
                    ffAudioRtt = stats[attr].mozRtt;
                  }

                  if(stats[attr].hasOwnProperty("packetsLost")){
                    nowRemoteAPLost = stats[attr].packetsLost;
                  }
                  if(stats[attr].hasOwnProperty("packetsReceived")){
                    nowRemoteAPReceived = stats[attr].packetsReceived;
                  }
                  continue;
                }

                if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "video" && stats[attr].isRemote === true){
                  if(stats[attr].hasOwnProperty("mozRtt")){
                    ffVideoRtt = stats[attr].mozRtt;
                  }

                  if(stats[attr].hasOwnProperty("packetsLost")){
                    nowRemoteVPLost = stats[attr].packetsLost;
                  }
                  if(stats[attr].hasOwnProperty("packetsReceived")){
                    nowRemoteVPReceived = stats[attr].packetsReceived;
                  }
                  continue;
                }
              }

              for(attr in oldStats){
                if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "audio" && stats[attr].isRemote === true){
                  if(stats[attr].hasOwnProperty("packetsLost")){
                    oldRemoteAPLost = stats[attr].packetsLost;
                  }
                  if(stats[attr].hasOwnProperty("packetsReceived")){
                    oldRemoteAPReceived = stats[attr].packetsReceived;
                  }
                  continue;
                }

                if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "video" && stats[attr].isRemote === true){
                  if(stats[attr].hasOwnProperty("packetsLost")){
                    oldRemoteVPLost = stats[attr].packetsLost;
                  }
                  if(stats[attr].hasOwnProperty("packetsReceived")){
                    oldRemoteVPReceived = stats[attr].packetsReceived;
                  }
                  continue;
                }
              }

              if(this.context.useVideo===false){
                result.rtt = ffAudioRtt;
              }
              else{
                result.rtt = ffVideoRtt;
              }
            }else{// chrome
              len = stats.length;
              for(; i<len; i++){
                if(stats[i].googActiveConnection === "true"){
                  result.localCandidate = stats[i].googLocalCandidateType;
                  result.remoteCandidate = stats[i].googRemoteCandidateType;

                  //result.rtt = parseInt(stats[i].googRtt);
                  continue;
                }

                if(stats[i].hasOwnProperty("audioInputLevel")){
                  nowLocalAPLost = parseInt(stats[i].packetsLost);
                  nowLocalAPSent = parseInt(stats[i].packetsSent);
                  if(this.context.useVideo ===false){
                    result.rtt = parseInt(stats[i].googRtt);
                  }
                  continue;
                }

                if(stats[i].hasOwnProperty("googFrameWidthSent")){
                  result.localFrameWidth = parseInt(stats[i].googFrameWidthSent);
                  result.localFrameHeight = parseInt(stats[i].googFrameHeightSent);
                  result.localFrameRate = parseInt(stats[i].googFrameRateSent);

                  nowLocalVPLost = parseInt(stats[i].packetsLost);
                  nowLocalVPSent = parseInt(stats[i].packetsSent);
                  if(this.context.useVideo===true){
                    result.rtt = parseInt(stats[i].googRtt);
                  }
                  continue;
                }

                if(stats[i].hasOwnProperty("audioOutputLevel")){
                  nowRemoteAPLost = parseInt(stats[i].packetsLost);
                  nowRemoteAPReceived = parseInt(stats[i].packetsReceived);
                  continue;
                }

                if(stats[i].hasOwnProperty("googFrameWidthReceived")){
                  result.remoteFrameWidth = parseInt(stats[i].googFrameWidthReceived);
                  result.remoteFrameHeight = parseInt(stats[i].googFrameHeightReceived);
                  result.remoteFrameRate = parseInt(stats[i].googFrameRateReceived);

                  nowRemoteVPLost = parseInt(stats[i].packetsLost);
                  nowRemoteVPReceived = parseInt(stats[i].packetsReceived);
                  continue;
                }

                if(stats[i].hasOwnProperty("googAvailableSendBandwidth")){
                  result.availableSendBandwidth = parseInt(stats[i].googAvailableSendBandwidth);
                  result.availableReceiveBandwidth = parseInt(stats[i].googAvailableReceiveBandwidth);
                  continue;
                }
              }

              for(i=0; i<oldStats.length; i++){
                if(oldStats[i].hasOwnProperty("audioInputLevel")){
                  oldLocalAPLost = parseInt(oldStats[i].packetsLost);
                  oldLocalAPSent = parseInt(oldStats[i].packetsSent);
                  continue;
                }

                if(oldStats[i].hasOwnProperty("googFrameWidthSent")){
                  oldLocalVPLost = parseInt(oldStats[i].packetsLost);
                  oldLocalVPSent = parseInt(oldStats[i].packetsSent);
                  continue;
                }

                if(oldStats[i].hasOwnProperty("audioOutputLevel")){
                  oldRemoteAPLost = parseInt(oldStats[i].packetsLost);
                  oldRemoteAPReceived = parseInt(oldStats[i].packetsReceived);
                  continue;
                }

                if(oldStats[i].hasOwnProperty("googFrameWidthReceived")){
                  oldRemoteVPLost = parseInt(oldStats[i].packetsLost);
                  oldRemoteVPReceived = parseInt(oldStats[i].packetsReceived);
                  continue;
                }
              }
            }
            //var localAFLost = parseFloat(parseFloat((nowLocalAPLost - oldLocalAPLost) / (nowLocalAPSent - oldLocalAPSent) * 255 || 0).toFixed(4));
            var localAFLost = parseInt((nowLocalAPLost - oldLocalAPLost) / (nowLocalAPSent - oldLocalAPSent) * 255 || 0);
            //var localVFLost = parseFloat(parseFloat((nowLocalVPLost - oldLocalVPLost) / (nowLocalVPSent - oldLocalVPSent) * 255 || 0).toFixed(4));
            var localVFLost = parseInt((nowLocalVPLost - oldLocalVPLost) / (nowLocalVPSent - oldLocalVPSent) * 255 || 0);

            result.localAudioPacketsLost = parseInt( (nowLocalAPLost - oldLocalAPLost)/this.interval  )
            result.localVideoPacketsLost = parseInt( (nowLocalVPLost - oldLocalVPLost)/this.interval)
            result.remoteAudioPacketsLost = parseInt( (nowRemoteAPLost - oldRemoteAPLost)/this.interval  )
            result.remoteVideoPacketsLost = parseInt( (nowRemoteVPLost - oldRemoteVPLost)/this.interval  )

            result.localAudioFractionLost = localAFLost;
            result.localVideoFractionLost = localVFLost;

            result.localAudioFractionRating = this.getFractionLostRating(localAFLost, this.fractionLost.audio);
            result.localVideoFractionRating = this.getFractionLostRating(localVFLost, this.fractionLost.video);

            // var remoteAFLost = parseFloat(parseFloat((nowRemoteAPLost - oldRemoteAPLost) / (nowRemoteAPReceived - oldRemoteAPReceived) * 255 || 0).toFixed(4));
            var remoteAFLost = parseInt((nowRemoteAPLost - oldRemoteAPLost) / (nowRemoteAPReceived - oldRemoteAPReceived) * 255 || 0);
            // var remoteVFLost = parseFloat(parseFloat((nowRemoteVPLost - oldRemoteVPLost) / (nowRemoteVPReceived - oldRemoteVPReceived) * 255 || 0).toFixed(4));
            var remoteVFLost = parseInt((nowRemoteVPLost - oldRemoteVPLost) / (nowRemoteVPReceived - oldRemoteVPReceived) * 255 || 0);

            result.remoteAudioFractionLost = remoteAFLost;
            result.remoteVideoFractionLost = remoteVFLost;

            result.remoteAudioFractionRating = this.getFractionLostRating(remoteAFLost, this.fractionLost.audio);
            result.remoteVideoFractionRating = this.getFractionLostRating(remoteVFLost, this.fractionLost.video);

            result.fractionRating = result.remoteVideoFractionRating;

            result.rttRating = this.getRttRating(result.rtt);

            this.oldStats = stats;
            //console.log(result);
            this.result = result;

            if (this.context.eventManager.hasEventListener('onStat')) {
              this.context.eventManager.dispatchEvent('onStat', result);
            }
            //l.i("this result:");

            message = this.context.signalingConnection.createMessage({ command: 'health', body: JSON.stringify(result) });
            //l.i(message);
            if (message)this.context.signalingConnection.send(JSON.stringify(message));
          },this));
        },this), this.interval);
      }

      getRttRating(rtt){
        var rttRating = 0;
        if (rtt >= 1000){	rttRating = 5;}
    		else if (rtt >= 800){	rttRating = 4;}
    		else if (rtt >= 600){	rttRating = 3;}
    		else if (rtt >= 400){	rttRating = 2;}
    		else if (rtt < 400){ rttRating = 1;}
    		return rttRating;
      }

      getFractionLostRating(loss, fl){
        var f = fl;
        for(let i=0; i<f.length; i++){
    			if(f[i].fromFractionLost <= loss && f[i].toFractionLost >= loss){
    				return f[i].rating;
    			}
    		}
        return 1;
      }

      getStats(fn){
    		if(platform.name === "Firefox"){
    			this.context.peerConnection.getStats(null, util.bind(function(res){
    				fn.call(this, res);
    			}, this), function(){});
    		}
    		else{
    			this.context.peerConnection.getStats(util.bind(function(res){
    				var items = [ ];
    				res.result().forEach(function (result) {
    					var item = { };
    					result.names().forEach(function (name) {
    						item[name] = result.stat(name);
    					});
    					item.id = result.id;
    					item.type = result.type;
    					item.timestamp = result.timestamp;

    					items.push(item);
    				});

    				fn.call(this, items);
    			}, this));
    		}
    	}
    }

    export default Health;