
var is_cloned = false;
var is_ping = false, ping_reset = false;
var ping_dest, last_ping = 0;
var upd_ping_dests = false;
var req_cnt = 0, err_cnt = 0;
var rxtx = 0;

var is_cfosspeed = null;

var ts_str = ["ignore", "analyze", "shape"];

var query = { };

(function eval_query() {
   var s = window.location.search;
   if (s.charAt(0) == '?') s = s.substr(1);
   var a = s.split('&');
   for (var i=0; i < a.length; ++i) {
      var f = a[i].split('=');
      if (f.length == 2) query[f[0]] = f[1];
      else query[f[0]] = f[0];
   }
})();

function format_num(v) {
   var val = Number(v);
   if (val < 1000) return String(val);
   var u = "K";
   if (val < 1000000) val /= 1000;
   else if (val < 1000000000) {
      val /= 1000000;
      u = 'M';
   }
   else if (val < 1000000000000) {
      val /= 1000000000;
      u = 'G';
   }
   else {
      val /= 1000000000000;
      u = 'T';
   }
   val = Math.round(val * 10) / 10;
   return val + u;
}

function check_max(a, min_scale, f) {
   function round_scale(m) {
      if (m <= 1) return 1;
      var s = Math.pow(10, Math.ceil(Math.log(m) / Math.LN10));
      if (s / 2 > 1.2 * m) return s/2;
      return s;
   }
   function set_scale(s) {
      if (s < min_scale) s = min_scale;
      a.max = s;
      a.cnt = 0;
      if (f) f(s);
   }

   var r, m = -Infinity;
   for (var i=0; i < a.v.length; ++i) if (a.v[i] > m) m = a.v[i];

   if (m > a.max) set_scale(round_scale(m));
   else if ((r = round_scale(m)) < a.max) {
      if (++a.cnt > 5) set_scale(r);
   }
   else a.cnt = 0;
}

function upd_bar_graph(name, v, options) {
   var el = $("#" + name);
   var bars = el.find(".bar");
   var d = el.data("values") || { v: [ ], max: 0, cnt: 0 };
   if (typeof (v) == "number") {
      if (d.v.length >= bars.length) d.v.pop();
      d.v.unshift(v);
   }
   else d.v = v;
   if (options.max) d.max = options.max;
   else check_max(d, options.min_scale || 100, options.print_scale);
   el.data("values", d);
   var h = el.height();
   for (var i=0; i < bars.length; ++i) {
      var idx = options.revert ? bars.length - 1 - i : i;
      var v = i < d.v.length ? Math.round(d.v[idx] / d.max * h) : 0;
      if (options.animate) $(bars[i]).animate({ height: v + "px", marginTop: (h-v)+"px" }, options.animate_time || 250);
      else $(bars[i]).css("height", v + "px").css("margin-top", (h-v)+"px");
   }
}

function clone_element(expr, n, clone_func) {
   var c = $(expr);
   var len = c.length;
   if (n > len) {
      var last = c;
      for (var i=1; i < n; ++i) {
         var e = c.clone(true);
         e.insertAfter(last);
         if (clone_func) clone_func(i, e);
         last = e;
      }
   }
   else if (n < len) {
      var cnt = len - n;
      for (var i=0; i < cnt; ++i) c[n+i].remove();
   }
}

function ping() {
   last_ping = new Date().getTime();
   var url = "/cfosspeed/stats/ping?dest=" + encodeURIComponent(ping_dest);
   if (ping_reset) {
      ping_reset = false;
      url += "&reset=1";
   }
   $.ajax({ dataType: "json", url: url, success: function(d) {
      var cnt = d.ping.length;
      while (cnt > 0 && d.ping[cnt-1].state == 3) --cnt;
      if (cnt < 1) cnt = 1;
      clone_element("#path_rtt>.bar", cnt);

      var a = [ ];
      for (var i=0; i < cnt; ++i) a.push(d.ping[i].state != 3 ? d.ping[i].last_rtt / 1000 : 0);
      upd_bar_graph("path_rtt", a, { animate:true, revert: true, print_scale: function(s) { $("#path_rtt_scale").text(s); } });
      upd_bar_graph("dest_ping_time", d.ping[cnt-1].last_rtt / 1000,
         { print_scale: function(s) { $("#dest_ping_time_scale").text(s); } });
   } });
}

function do_refresh(d) {
   if (!is_cloned) {
      is_cloned = true;
      clone_element("#cpu>.bar", d.cpu.v.length);
      clone_element("#gpu>.bar", d.gpu.v.length);
   }

   var is_sp = ("cfosspeed" in d) && !query.no_cfosspeed;
   if (is_sp !== is_cfosspeed) {
      is_cfosspeed = is_sp;
      if (is_cfosspeed) {
         $("#sp_inner").show();
         $("#sp_inner_none").hide();
      }
      else {
         $("#sp_inner").hide();
         $("#sp_inner_none").show();
      }
   }

   var rx, tx;

   if (!is_cfosspeed) {
      rx = d.nic.rx; tx = d.nic.tx;
      upd_bar_graph("rx_speed", rx, { animate: true, animate_time: 150, print_scale: function(s) { $("#rx_scale").text(s); } });
      upd_bar_graph("tx_speed", tx, { print_scale: function(s) { $("#tx_scale").text(s); } });
      $("#rx_speed_num").text(format_num(rx));
      $("#tx_speed_num").text(format_num(tx));
   }

   upd_bar_graph("cpu", d.cpu.v, { max: 100, animate: true });
   upd_bar_graph("gpu", d.gpu.v, { max: 100, animate: true });

   if (is_ping && new Date().getTime() - last_ping > 2000) ping();

   if (is_cfosspeed) {
      var sp = d.cfosspeed;
      var si = null;
      for (var i=0; i < sp.stat_infos.length; ++i) {
         if (sp.stat_infos[i].open) {
            si = sp.stat_infos[i];
            break;
         }
      }
      if (si) {
         $("#sp_adapter").html("Adapter: " + si.conn_name + "<br/>" + "MAC: " + si.interface_mac);
         var st = "Router Mac IPv4: " + si.router_mac + "<br/>";
         for (var i=0; i < si.ip.length; ++i) st += "Address: " + si.ip[i] + "<br/>";
         $("#sp_ipv4").html(st);
         var st = "Router Mac IPv6: " + si.router_mac_v6 + "<br/>";
         for (var i=0; i < si.ipv6.length; ++i) st += "Address: " + si.ipv6[i] + "<br/>";
         $("#sp_ipv6").html(st);
         $("#sp_ts_stats").html("Traffic Shaping: " + ts_str[si.ts_state] + "<br/>" +
            "Max Rx Speed: " + format_num(si.rx_scale) + "/s<br/>" + "Max Tx Speed: " + format_num(si.tx_scale) + "/s<br/>" +
            "Minimum Ping: " + (si.min_delay / 1000) + "ms<br/>" +
            "Variance: " + (si.variance / 1000) + "ms<br/>" +
            "Connections: " + si.tcp_udp_cnt);

         upd_bar_graph("rx_speed", si.total_rx_speed, { max: 100 });
         $("#rx_scale").text(format_num(si.rx_scale));
         upd_bar_graph("tx_speed", si.total_tx_speed, { max: 100 });
         $("#tx_scale").text(format_num(si.tx_scale));
         rx = si.rx_cps; tx = si.tx_cps;
         $("#rx_speed_num").text(format_num(rx));
         $("#tx_speed_num").text(format_num(tx));
         upd_bar_graph("sp_ping_time", si.ping_time,
            { min_scale: 50, print_scale: function(s) { $("#sp_ping_time_scale").text(s); } });
         $("#sp_ping_time_num").text(si.ping_time);
      }
   }
   if (rx >= 1000 || tx >= 1000) {
      var r = rx == 0 || tx == 0 ? 0 : rx / tx;
      $("#rxtx_balance").text(Math.round((r-rxtx) * 10) / 10);
      rxtx = r;
   }
   else $("#rxtx_balance").text("0");
   $("#req_cnt").text(++req_cnt);
   setTimeout("refresh()", 1000);
}

function req_error() {
   $("#err_cnt").text(++err_cnt);
   setTimeout("refresh()", 1000);
}

function refresh() {
   if (upd_ping_dests) refresh_ping_dests();
   $.ajax({ dataType: "json", url: "/cfosspeed/stats/general", error: req_error, success: do_refresh });
}

function set_ping() {
   var c = $("#ping")[0];
   if (!c.checked) {
      is_ping = false;
      return;
   }
   var d = $("#ping_dest")[0].value.replace(/\s/g, "");
   if (!d) {
      c.checked = false;
      is_ping = false;
      return;
   }
   $("#path_rtt_dest").text("Path RTT to " + d);
   ping_reset = true;
   is_ping = true;
   ping_dest = d;
}

var ping_dests = [ ];

function select_dest(idx) {
   upd_ping_dests = false;
   $("#settings_frame").hide();
   if (idx != -1) $("#ping_dest")[0].value = ping_dests[idx].d;
}

function refresh_ping_dests() {
   var url = "/cfosspeed/stats/ping_dests";
   if (!is_cfosspeed) url += "?tcpview=1";
   $.ajax({ dataType: "json", url: url, success: function(dd) {
      if (!upd_ping_dests) return;
      ping_dests = dd;
      var t = '<table><tbody>';
      for (var i=0; i < ping_dests.length; ++i) {
         var pd = ping_dests[i];
         if (pd.d == "::1" || pd.d == "127.0.0.1") continue;
         t += '<tr onclick="select_dest(' + i + ')"><td>' + pd.t + '</td><td>' + pd.d + '</td><td>' + pd.p
            + '</td><td>' + pd.r + '</td><td>' + pd.t + '</td></tr>';
      }
      t += '</tbody></table>';
      $("#sel").html(t);
   } });
}

function select_ping_dest() {
   upd_ping_dests = true;
   $("#settings_frame").show();
}

function freeze(is_freeze) {
   if (is_freeze) {
      upd_ping_dests = false;
      $("#freeze").hide();
      $("#continue").show();
   }
   else {
      upd_ping_dests = true;
      $("#freeze").show();
      $("#continue").hide();
   }
}

function unit_tests() {
   if (format_num("123") != "123") alert("test 1 failed");
   if (format_num("12354") != "12.4K") alert("test 2 failed");
   if (format_num("123456789") != "123.5M") alert("test 3 failed");
   if (format_num("12345678901") != "12.3G") alert("test 4 failed");
   if (format_num("1234567890123") != "1.2T") alert("test 5 failed");
}

function init_analyzer() {
   unit_tests();
   refresh();
}
