context = window
ChannelGroupIds = context.JK.ChannelGroupIds
MixerActions = @MixerActions
ptrCount = 0


window.aggregate_latency_calc = (stats) -> `<span>Total Latency is calculated as:<br/>their gear output delay ({Math.round(stats.aggregate.their_out_latency)}ms)<br><span className="plusone">+</span><br/>your gear input delay ({Math.round(stats.aggregate.your_in_latency)}ms)<br/><span className="plusone">+</span></br>internet delay ({Math.round(stats.aggregate.one_way)}ms)<br/><span className="plusone">+</span><br/>delay caused by jitter queue ({Math.round(stats.aggregate.jq)}ms).</span>`

StatsInfo = {

  aggregate: {
    latency: {
      good: (user, stats) -> `<span>{user.possessive} one-way, total latency from him to you is very good.<br/><br/>{window.aggregate_latency_calc(stats)}</span>`,
      warn: (user, stats) -> `<span>{user.possessive} one-way, total latency from him to you is typical.<br/><br/>{window.aggregate_latency_calc(stats)}</span>`,
      poor: (user, stats) -> `<span>{user.possessive} one-way, total latency from him to you is poor.<br/><br/>{window.aggregate_latency_calc(stats)}</span>`
    }
  },
  system: {
    cpu: {
      good: (user, stats) -> "#{user.possessive} computer processor is not overworked by JamKazam or the your system.",
      warn: (user, stats) -> "#{user.possessive} computer processor is being heavily used.  There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues.",
      poor: (user, stats) -> "#{user.possessive} computer processor is being very heavily used.  There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues."
    }
  },
  network: {
    wifi: {
      good: (user, stats) -> "#{user.name} is using a wired connection.",
      warn: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
      poor: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
    },
    audio_bitrate_rx: {
      good: (user, stats) -> "#{user.name} has enough bandwidth to send you a high quality audio stream.",
      warn: (user, stats) -> "#{user.name} has enough bandwidth to send you a degraded, but sufficient, audio stream.",
      poor: (user, stats) -> "#{user.name} has not enough bandwidth to send you a decent quality audio stream.",
    },
    audio_bitrate_tx: {
      good: (user, stats) -> "You have enough bandwidth to send you a high quality audio stream.",
      warn: (user, stats) -> "You have enough bandwidth to send you a degraded, but sufficient, audio stream.",
      poor: (user, stats) -> "You have not enough bandwidth to send you a decent quality audio stream.",
    },
    video_rtpbw_rx: {
      good: (user, stats) -> "#{user.name} has enough bandwidth to send you a high quality video stream.",
      warn: (user, stats) -> "#{user.name} has enough bandwidth to send you a degraded, but sufficient, video stream.",
      poor: (user, stats) -> "#{user.name} has not enough bandwidth to send you a decent quality video stream.",
    },
    video_rtpbw_tx: {
      good: (user, stats) -> "You have enough bandwidth to send you a high quality video stream.",
      warn: (user, stats) -> "You have enough bandwidth to send you a degraded, but sufficient, video stream.",
      poor: (user, stats) -> "You have not enough bandwidth to send you a decent quality video stream.",
    },
    tx_ars_vs_p2p: {
      good: (user, stats) -> "Your send network path is also the lowest latency path available",
      warn: (user, stats) -> "Your send network path is not the lowest latency path available",
      poor: (user, stats) -> "Your send network path is not the lowest latency path available",
    },
    rx_ars_vs_p2p: {
      good: (user, stats) -> "Your receive network path is also the lowest latency path available",
      warn: (user, stats) -> "Your receive network path is not the lowest latency path available",
      poor: (user, stats) -> "Your receive network path is not the lowest latency path available",
    },
    ping: {
      good: (user, stats) -> "The internet connection between you and #{user.name} has very low latency.",
      warn: (user, stats) -> "The internet connection between you and #{user.name} has average latency, which may affect staying in sync.",
      poor: (user, stats) -> "The internet connection between you and #{user.name} has high latency, making it very difficult to stay in sync.",
    },
    pkt_loss: {
      good: (user, stats) -> "The internet connection between you and #{user.name} loses a small % of packets.  It should not affect your audio quality.",
      warn: (user, stats) -> "The internet connection between you and #{user.name} loses a significant % of packets.  This may result in periodical audio artifacts.",
      poor: (user, stats) -> "The internet connection between you and #{user.name} loses a high % of packets.  This will result in frequent audio artifacts.",
    },
    audiojq_median: {
      good: (user, stats) -> `<span>JamKazam has to maintain a only a small buffer of audio to preserve audio quality, resulting in minimal added latency.<br/><br/>This buffer is adding {(2.5 * stats.network.audiojq_median).toFixed(1)}ms of latency.</span>`,
      warn: (user, stats) -> `<span>JamKazam has to maintain a significant buffer of audio to preserve audio quality, resulting in potentially noticeable additional latency.<br/><br/>This buffer is adding {(2.5 * stats.network.audiojq_median).toFixed(1)}ms of latency.</span>`,
      poor: (user, stats) -> `<span>JamKazam has to maintain a large buffer of audio to preserve audio quality, resulting in noticeabley added latency.<br/><br/>This buffer is adding {(2.5 * stats.network.audiojq_median).toFixed(1)}ms of latency.</span>`,
    }
  },
  audio: {
    framesize: {
      good: (user, stats) -> "#{user.possessive} gear is reading and writing audio data at a very high rate, keeping gear-added latency low.",
      warn: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a average rate, causing a few milliseconds extra latency compared to a 2.5 Frame Size.",
      poor: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a slow rate, causing a decent amount of latency before the internet is involved.",
    },
    latency: {
      good: (user, stats) -> "#{user.possessive} gear has a small amount of latency.",
      warn: (user, stats) -> "#{user.possessive} gear has a significant amount of latency.",
      poor: (user, stats) -> "#{user.possessive} gear has a large amount of latency, making it difficult to play in time."
    },
    input_jitter: {
      good: (user, stats) -> "#{user.possessive} gear has a small amount of input jitter, meaning it is keeping good time with JamKazam as it reads in your input signal.",
      warn: (user, stats) -> "#{user.possessive} gear has a significant amount of input jitter, meaning it might be periodically adding delay or audio artifacts to your inputs.",
      poor: (user, stats) -> "#{user.possessive} gear has a large amount of input jitter, meaning it likely adding delay and audio artifacts to your inputs.",
    },
    output_jitter: {
      good: (user, stats) -> "#{user.possessive} gear has a small amount of output jitter, meaning it is keeping good time with your JamKazam as writes out your audio output.",
      warn: (user, stats) -> "#{user.possessive} gear has a significant amount of output jitter, meaning it might be periodically adding delay and audio artifacts to your output.",
      poor: (user, stats) -> "#{user.possessive} gear has a large amount of output jitter, meaning it likely adding delay and audio artifacts to your output.",
    },
    audio_in_type: {
      good: (user, stats) -> "#{user.name} using an ideal driver type for #{user.possessive.toLowerCase()} gear.",
      warn: (user, stats) -> "#{user.name} using a problematic driver type for #{user.possessive.toLowerCase()} gear.",
      poor: (user, stats) -> "#{user.name} using a driver type considered problematic.",
    }
  }
}

@SessionStatsHover = React.createClass({

  propTypes: {
    clientId: React.PropTypes.string
  }

  mixins: [Reflux.listenTo(@SessionStatsStore, "onStatsChanged")]

  hover: (type, field) ->
    logger.debug("hover! #{type} #{field}")
    @setState({hoverType: type, hoverField:field})

  hoverOut: () ->
    logger.debug("hover out!")
    @setState({hoverType: null, hoverField: null})


  stat: (properties, type, name, field, value) ->
    classes = {'status-icon': true}
    classifier = properties[field + '_level']
    classes[classifier] = true
    `<div className='stat' onMouseOver={this.hover.bind(this, type, field)}><span className="title">- {name}</span><div className={classNames(classes)}></div><span className="stat-value">{value}</span></div>`

  render: () ->
    extraInfo = 'Hover over a stat to learn more.'

    if @state.hoverType?
      type = @state.hoverType
      field = @state.hoverField

      extraInfo =  `<span>No extra info for this metric.</span>`

      classifier = @state.stats?[type]?[field + '_level']

      if classifier?
        info = StatsInfo[type]?[field]?[classifier](@props.participant.user, @state.stats)
        if info?
          extraInfo = `<span>{info}</span>`

    computerStats = []
    networkStats = []
    audioStats = []
    aggregateStats = []

    aggregate = @state.stats?.aggregate
    network = @state.stats?.network
    system = @state.stats?.system
    audio = @state.stats?.audio

    aggregateTag = null
    if aggregate? && !this.props.myTrack
      if aggregate.latency
        aggregateStats.push(@stat(aggregate, 'aggregate', 'Tot Latency', 'latency', Math.round(aggregate.latency)))

      aggregateTag =
        `<div className="aggregate-stats stats-holder">
          {aggregateStats}
        </div>`

    if system?
      if system.cpu?
        computerStats.push(@stat(system, 'system', 'Processor', 'cpu', Math.round(system.cpu) + ' %'))

    if audio?
      if audio.latency?
        audioStats.push(@stat(audio, 'audio', 'Latency', 'latency', audio.latency.toFixed(1) + ' ms'))
      if audio.input_jitter?
        audioStats.push(@stat(audio, 'audio', 'Input Jitter', 'input_jitter', audio.input_jitter.toFixed(2) + ' ms'))
      if audio.output_jitter?
        audioStats.push(@stat(audio, 'audio', 'Output Jitter', 'output_jitter', audio.output_jitter.toFixed(2) + ' ms'))

      if audio.audio_in_type?
        audio_type =  '?'
        audio_long = audio.audio_in_type.toLowerCase()
        if audio_long.indexOf('asio') > -1
          audio_type = 'ASIO'
        else if audio_long.indexOf('wdm') > -1
          audio_type = 'WDM'
        else if audio_long.indexOf('core') > -1
          audio_type = 'CoreAudio'
        else if audio_long.indexOf('alsa') > -1
          audio_type = 'JamBlaster'
        audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type))
      if audio.framesize?
        framesize = audio.framesize + ' ms'
        audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize))

    networkTag = null
    if network? && !this.props.myTrack
      if network.ping?
        networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms'))
      if network.audiojq_median?
        networkStats.push(@stat(network, 'network', 'Jitter Queue', 'audiojq_median', network.audiojq_median.toFixed(1)))
      if network.jitter_var?
        networkStats.push(@stat(network, 'network', 'Jitter', 'jitter_var', network.jitter_var.toFixed(1) + ' ms'))
      if network.pkt_loss?
        networkStats.push(@stat(network, 'network', 'Packet Loss', 'pkt_loss', network.pkt_loss.toFixed(1) + ' %'))
      if network.wifi?
        if network.wifi
          value = 'Wi-Fi'
        else
          value = 'Wired'
        networkStats.push(@stat(network, 'network', 'Connectivity', 'wifi', value))
      if network.audio_bitrate_rx?
        networkStats.push(@stat(network, 'network', 'Audio Bw Rx', 'audio_bitrate_rx', Math.round(network.net_bitrate_rx) + ' k'))
      if network.audio_bitrate_tx?
        networkStats.push(@stat(network, 'network', 'Audio Bw Tx', 'audio_bitrate_tx', Math.round(network.net_bitrate_tx) + ' k'))
      if network.video_rtpbw_rx?
        networkStats.push(@stat(network, 'network', 'Video Bw Rx', 'video_rtpbw_rx', Math.round(network.video_rtpbw_rx) + ' k'))
      if network.video_rtpbw_tx?
        networkStats.push(@stat(network, 'network', 'Video Bw Tx', 'video_rtpbw_tx', Math.round(network.video_rtpbw_tx) + ' k'))
      if network.rx_ars_vs_p2p?
        networkStats.push(@stat(network, 'network', 'RX Net Path', 'rx_ars_vs_p2p', network.rx_ars_vs_p2p))
      if network.tx_ars_vs_p2p?
        networkStats.push(@stat(network, 'network', 'TX Net Path', 'tx_ars_vs_p2p', network.tx_ars_vs_p2p))

      networkTag =
        `<div className="network-stats stats-holder">
          <h3>Internet</h3>
          {networkStats}
        </div>`

    `<div className="stats-hover">
      <h3>Session Diagnostics &amp; Stats: {this.props.participant.user.name}</h3>

      <div className="stats-area">
        {aggregateTag}
        <div className="computer-stats stats-holder">
          <h3>Computer</h3>
          {computerStats}
        </div>
        <div className="audio-stats stats-holder">
          <h3>Audio Interface</h3>
          {audioStats}
        </div>
        {networkTag}
      </div>
      <div className="stats-info">
        {extraInfo}
      </div>
    </div>`

  onStatsChanged: (stats) ->
    stats = window.SessionStatsStore.stats
    if stats?
      if stats.parent?
        # if we have a parent, then use stats from the JamBlaster (parent), not ourselves.  Otherwise we'll get bad stats (no Audio etc)
        clientStats = stats.parent[@props.participant.client_id]
      else
        clientStats = stats[@props.participant.client_id]
    else
      clientStats = null
    @setState({stats: clientStats})

  getInitialState: () ->
    stats = window.SessionStatsStore.stats
    if stats?
      clientStats = stats[@props.participant.client_id]
    else
      clientStats = null
    {stats: clientStats, hoverType: null, hoverField: null}


  closeHover: (e) ->
    e.preventDefault()
    $container = $(this.getDOMNode()).closest('.react-holder')
    $container.data('bt').btOff()
})