<%

var _s   = (locals.series || []).map(function (v) { return (v == null || isNaN(v)) ? null : Number(v); });
var _lab = locals.labels || [];
var _spn = locals.spans || _lab;
var _inv = !!locals.invert;
var _nm  = locals.name || '';
var _fmt = locals.fmt || function (v) { return String(v); };
var _real = _s.filter(function (v) { return v != null; });
%>
<% if (_real.length < 2) { %>

  <div class="spark-none"><%= _real.length ? 'One reading so far' : 'Not measured yet' %></div>
<% } else { %>
<%
  var W = 260, H = 40, PAD = 5;
  var lo = Math.min.apply(null, _real), hi = Math.max.apply(null, _real);
  var span = hi - lo;
  var yOf = function (v) {
    if (span === 0) return H / 2;
    var t = (v - lo) / span;
    if (_inv) t = 1 - t;
    return +(PAD + (1 - t) * (H - PAD * 2)).toFixed(1);
  };
  var xOf = function (i) { return _s.length < 2 ? 0 : +((i / (_s.length - 1)) * W).toFixed(1); };

  var segs = [], cur = [];
  _s.forEach(function (v, i) {
    if (v == null) { if (cur.length) segs.push(cur); cur = []; return; }
    cur.push({ x: xOf(i), y: yOf(v) });
  });
  if (cur.length) segs.push(cur);

  var firstIdx = -1, lastIdx = -1;
  _s.forEach(function (v, i) { if (v != null) { if (firstIdx < 0) firstIdx = i; lastIdx = i; } });

  var _from = _s[firstIdx], _to = _s[lastIdx];
  var _better = _inv ? (_to < _from) : (_to > _from);
  var _worse  = _inv ? (_to > _from) : (_to < _from);
  var _dir = _to === _from ? 'flat' : (_better ? 'up' : 'down');

  var _gid = 'spk' + Math.random().toString(36).slice(2, 9);

  function monotoneBody(p) {
    var n = p.length;
    if (n < 2) return '';
    if (n === 2) return 'L' + p[1].x.toFixed(1) + ',' + p[1].y.toFixed(1);
    var dx = [], dy = [], slope = [];
    for (var i = 0; i < n - 1; i++) {
      dx.push(p[i + 1].x - p[i].x);
      dy.push(p[i + 1].y - p[i].y);
      slope.push(dx[i] === 0 ? 0 : dy[i] / dx[i]);
    }
    var m = [slope[0]];
    for (var i = 1; i < n - 1; i++) {
      m.push(slope[i - 1] * slope[i] <= 0 ? 0 : (slope[i - 1] + slope[i]) / 2);
    }
    m.push(slope[n - 2]);
    for (var i = 0; i < n - 1; i++) {
      if (slope[i] === 0) { m[i] = 0; m[i + 1] = 0; continue; }
      var a = m[i] / slope[i], b = m[i + 1] / slope[i];
      if (a < 0) m[i] = 0;
      if (b < 0) m[i + 1] = 0;
      var sq = a * a + b * b;
      if (sq > 9) { var tau = 3 / Math.sqrt(sq); m[i] = tau * a * slope[i]; m[i + 1] = tau * b * slope[i]; }
    }
    var body = '';
    for (var i = 0; i < n - 1; i++) {
      var p0 = p[i], p1 = p[i + 1];
      var cx1 = p0.x + dx[i] / 3, cy1 = p0.y + m[i] * dx[i] / 3;
      var cx2 = p1.x - dx[i] / 3, cy2 = p1.y - m[i + 1] * dx[i] / 3;
      body += 'C' + cx1.toFixed(1) + ',' + cy1.toFixed(1) + ' ' + cx2.toFixed(1) + ',' + cy2.toFixed(1)
            + ' ' + p1.x.toFixed(1) + ',' + p1.y.toFixed(1) + ' ';
    }
    return body;
  }
  function linePath(p) { return 'M' + p[0].x.toFixed(1) + ',' + p[0].y.toFixed(1) + ' ' + monotoneBody(p); }
  function areaPath(p) {
    return 'M' + p[0].x.toFixed(1) + ',' + H + ' L' + p[0].x.toFixed(1) + ',' + p[0].y.toFixed(1) + ' '
         + monotoneBody(p) + ' L' + p[p.length - 1].x.toFixed(1) + ',' + H + ' Z';
  }

  var pts = [];
  _s.forEach(function (v, i) {
    if (v == null) return;
    var prev = null;
    for (var k = i - 1; k >= 0; k--) { if (_s[k] != null) { prev = _s[k]; break; } }
    pts.push({
      x: xOf(i),
      label: _lab[i] || ('#' + (i + 1)),
      rows: [{ k: _nm, v: _fmt(v), y: yOf(v), c: 'var(--spark)',
               d: prev == null ? null : +(v - prev).toFixed(2) }]
    });
  });

  var _fillSeg = segs.length ? segs[segs.length - 1] : null;
%>
<div class="vchart-wrap spark-wrap">
  <svg class="spark dir-<%= _dir %>" viewBox="0 0 <%= W %> <%= H %>" preserveAspectRatio="none"
       tabindex="0" role="img" data-points="<%= JSON.stringify(pts) %>"
       aria-label="<%= _nm %>, <%= _fmt(_from) %> to <%= _fmt(_to) %> across <%= _real.length %> readings. Use the arrow keys to read each one.">
    <defs>
      <linearGradient id="<%= _gid %>" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0%" stop-color="var(--spark)" stop-opacity=".32"/>
        <stop offset="100%" stop-color="var(--spark)" stop-opacity="0"/>
      </linearGradient>
    </defs>

    <line class="spark-base" x1="0" y1="<%= yOf(_from) %>" x2="<%= W %>" y2="<%= yOf(_from) %>"/>

    <% if (_fillSeg && _fillSeg.length >= 2) { %>
      <path class="spark-area" fill="url(#<%= _gid %>)" d="<%= areaPath(_fillSeg) %>"/>
    <% } %>

    <% segs.forEach(function (s) { if (s.length < 2) return; %>
      <path class="spark-glow" d="<%= linePath(s) %>"/>
    <% }) %>
    <% segs.forEach(function (s) { if (s.length < 2) return; %>
      <path class="spark-line" pathLength="100" d="<%= linePath(s) %>"/>
    <% }) %>

    <% segs.forEach(function (s) { if (s.length !== 1) return; %>
      <circle cx="<%= s[0].x %>" cy="<%= s[0].y %>" r="1.8" fill="var(--spark)"/>
    <% }) %>

    <% pts.forEach(function (p, i) { if (i === pts.length - 1) return; %>
      <circle class="spark-dot-mark" cx="<%= p.x %>" cy="<%= p.rows[0].y %>" r="1.4"/>
    <% }) %>

    <% if (lastIdx >= 0) { %>

      <circle class="spark-pulse" cx="<%= xOf(lastIdx) %>" cy="<%= yOf(_s[lastIdx]) %>" r="2.4"/>
      <circle class="spark-end" cx="<%= xOf(lastIdx) %>" cy="<%= yOf(_s[lastIdx]) %>" r="2.4" fill="var(--spark)"/>
    <% } %>
    <line class="vc-cross" x1="0" y1="0" x2="0" y2="<%= H %>"/>
    <circle class="vc-dot" cx="0" cy="0" r="3"/>
  </svg>

  <div class="spark-cap">

    <span><%= _spn[firstIdx] || '' %> to <%= _spn[lastIdx] || '' %></span>
    <b class="sc-<%= _dir %>"><%= _dir === 'flat' ? 'no change' : ('from ' + _fmt(_from) + ' to ' + _fmt(_to)) %></b>
  </div>
  <div class="vchart-tip" hidden aria-live="polite"></div>
</div>
<% } %>
