<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title><%= h title %></title>
  <style>
    * { box-sizing: border-box; }
    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
      margin: 0;
      padding: 0;
      background: #f5f5f5;
      color: #333;
    }
    #header {
      background: #0D2483;
      color: #fff;
      padding: 1rem 1.5rem;
      display: flex;
      align-items: center;
      gap: 16px;
      flex-wrap: wrap;
    }
    #header .header-left {
      display: flex;
      flex-direction: column;
      gap: 2px;
    }
    #header h6 {
      margin: 0;
      font-size: 0.75rem;
      text-transform: uppercase;
      letter-spacing: 1.5px;
      color: rgba(255, 255, 255, 0.6);
      font-weight: 600;
    }
    #header h1 {
      margin: 0;
      font-size: 22px;
      font-weight: 600;
      white-space: nowrap;
    }
    .logo {
      width: 140px;
      height: 30px;
      opacity: 0.5;
      background-repeat: no-repeat;
      background-image: url("data:image/svg+xml,%3Csvg id='Layer_1' data-name='Layer 1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 190 41'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%23fff%7D%3C/style%3E%3C/defs%3E%3Cpath class='cls-1' d='M63.49 16.74c0-1.37-.91-2-2.26-2h-3.38v3.93h3.58c1.24.01 2.06-.56 2.06-1.93zM110.43 14.75h-3.6v5h3.6a2.32 2.32 0 0 0 2.5-2.49 2.34 2.34 0 0 0-2.5-2.51zM128.77 14.75h-3.53v4.94h3.53a2.28 2.28 0 0 0 2.47-2.45 2.3 2.3 0 0 0-2.47-2.49zM61.81 21.83h-4v4.32h3.91c1.44 0 2.45-.62 2.45-2.14s-.99-2.18-2.36-2.18zM21 14.75h-3.57v4.94H21a2.28 2.28 0 0 0 2.47-2.45A2.3 2.3 0 0 0 21 14.75z'/%3E%3Cpath class='cls-1' d='M184 .44H5.87A4.93 4.93 0 0 0 .94 5.37V35.5a4.94 4.94 0 0 0 4.93 4.94H184a4.94 4.94 0 0 0 4.94-4.94V5.37A4.94 4.94 0 0 0 184 .44zm-34.38 10.78c4.79 0 8.46 2.89 9.18 7.54h-3.86a5.48 5.48 0 0 0-10.68 0h-3.83c.71-4.65 4.36-7.54 9.19-7.54zM24 29.44L20.46 23h-3v6.46h-3.72v-18h7.68c3.41 0 5.78 2.07 5.78 5.76a5.34 5.34 0 0 1-2.88 5.14l3.82 7.06zm24.31-7.3c0 4.8-2.92 7.56-7.63 7.56S33 26.94 33 22.14V11.48h3.69v10.61c0 2.81 1.37 4.32 4 4.32s3.94-1.51 3.94-4.32V11.48h3.69zm13.92 7.3h-8v-18h7.52c3.24 0 5.59 1.61 5.59 5A4 4 0 0 1 65.51 20 4.22 4.22 0 0 1 68 24.21c0 3.43-2.4 5.23-5.81 5.23zm19.66-6.92v6.92h-3.7v-6.92l-6.81-11h4.29L80 18.85l4.34-7.37h4.32zm16-.5h-7.62v-3.26h7.59zm13 1h-4v6.39h-3.72v-18h7.77c3.41 0 5.79 2.09 5.79 5.79s-2.41 5.85-5.82 5.85zm20.86 6.39L128.26 23h-3v6.46h-3.72v-18h7.69c3.4 0 5.78 2.07 5.78 5.76a5.34 5.34 0 0 1-2.88 5.14l3.87 7.08zm17.85.26c-4.88 0-8.55-2.95-9.21-7.68h3.81A5.49 5.49 0 0 0 155 22h3.84c-.69 4.75-4.38 7.7-9.22 7.7zm18.4-.23h-3.72v-3.7H168zm6.36-7.54h-10.05v-3.26h10.08zm1.44-7.16h-11.49v-3.26h11.52z'/%3E%3C/svg%3E");
    }
    #controls {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
    }
    #controls label {
      font-size: 13px;
      color: rgba(255, 255, 255, 0.6);
    }
    #controls select, #controls input[type="text"] {
      font-size: 13px;
      padding: 4px 8px;
      border: 1px solid rgba(255, 255, 255, 0.3);
      border-radius: 3px;
      background: rgba(255, 255, 255, 0.15);
      color: #fff;
    }
    #controls select option {
      background: #fff;
      color: #333;
    }
    #controls button {
      font-size: 13px;
      padding: 4px 12px;
      border: 1px solid rgba(255, 255, 255, 0.3);
      border-radius: 3px;
      background: rgba(255, 255, 255, 0.15);
      color: #fff;
      cursor: pointer;
    }
    #controls button:hover {
      background: rgba(255, 255, 255, 0.25);
    }
    #chart-container {
      padding: 10px 20px 0 20px;
      overflow-x: auto;
    }
    #tooltip {
      position: fixed;
      display: none;
      background: rgba(0,0,0,0.88);
      color: #fff;
      padding: 8px 12px;
      border-radius: 4px;
      font-size: 12px;
      line-height: 1.5;
      pointer-events: none;
      z-index: 1000;
      max-width: 500px;
      white-space: nowrap;
    }
    #tooltip .tt-name {
      font-weight: 600;
      margin-bottom: 2px;
      white-space: normal;
      word-break: break-all;
    }
    #tooltip .tt-detail {
      color: #ccc;
    }
    svg {
      display: block;
    }
    svg text {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
      font-size: 11px;
      fill: white;
      pointer-events: none;
    }
    svg rect.frame {
      stroke: #fff;
      stroke-width: 0.5;
      cursor: pointer;
    }
    svg rect.frame:hover {
      stroke: #333;
      stroke-width: 1;
    }
    svg rect.frame.highlight {
      stroke: #0984e3;
      stroke-width: 2;
    }
    #zoom-info {
      padding: 6px 20px;
      font-size: 12px;
      color: #636e72;
      display: none;
    }
    #zoom-info a {
      color: #0984e3;
      cursor: pointer;
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <div id="header">
    <div class="header-left">
      <h6>Flame Graph</h6>
      <h1><%= h @result.measure_mode_name %></h1>
    </div>
    <div id="controls">
      <label for="thread-select">Thread:</label>
      <select id="thread-select"></select>
      <label for="search-input">Search:</label>
      <input type="text" id="search-input" placeholder="Method name...">
      <button id="reset-zoom-btn">Reset Zoom</button>
      <button id="toggle-icicle-btn">Icicle</button>
    </div>
    <div style="margin-left: auto; display: flex; align-items: flex-end; flex-direction: column;">
      <div style="font-size: 12px; margin-bottom: 0.5rem; color: rgba(255,255,255,0.6);"><%= Time.now.strftime(self.time_format) %></div>
      <div class="logo"></div>
    </div>
  </div>
  <div id="zoom-info">Zoomed into: <span id="zoom-target"></span> &mdash; <a id="zoom-reset-link">reset</a></div>
  <div id="thread-info" style="padding: 6px 20px; font-size: 12px; color: #636e72;"></div>
  <div id="chart-container">
    <svg id="flame-svg"></svg>
  </div>
  <div id="tooltip"></div>

  <script>
  (function() {
    var threads = <%= flame_data_json %>;
    var currentThreadIdx = 0;
    var zoomNode = null;
    var searchPattern = null;
    var icicleMode = false;

    var FRAME_HEIGHT = 18;
    var MIN_WIDTH_PX = 1;
    var PADDING_BOTTOM = 4;
    var CHAR_WIDTH = 6.5;
    var TEXT_PADDING = 4;

    // --- Color ---
    function hashCode(s) {
      var hash = 0;
      for (var i = 0; i < s.length; i++) {
        hash = ((hash << 5) - hash) + s.charCodeAt(i);
        hash = hash & hash;
      }
      return Math.abs(hash);
    }

    function frameColor(name) {
      var h = hashCode(name);
      var hue = 190 + (h % 50);
      var sat = 50 + (h % 30);
      var lit = 55 + (h % 20);
      return 'hsl(' + hue + ',' + sat + '%,' + lit + '%)';
    }

    // --- Layout ---
    function maxDepth(node) {
      var max = 0;
      if (node.children) {
        for (var i = 0; i < node.children.length; i++) {
          var d = maxDepth(node.children[i]);
          if (d > max) max = d;
        }
      }
      return max + 1;
    }

    function render() {
      var thread = threads[currentThreadIdx];
      var root = zoomNode || thread.data;
      var totalTime = root.value;
      var depth = maxDepth(root);

      // Update thread info text above the SVG
      var threadInfo = 'Thread ' + thread.id + ' | Total: ' + root.value.toFixed(4) + 's';
      if (zoomNode) threadInfo += ' (zoomed)';
      document.getElementById('thread-info').textContent = threadInfo;

      var container = document.getElementById('chart-container');
      var svgWidth = Math.max(container.clientWidth - 40, 800);
      var svgHeight = depth * FRAME_HEIGHT + PADDING_BOTTOM;

      var svg = document.getElementById('flame-svg');
      svg.setAttribute('width', svgWidth);
      svg.setAttribute('height', svgHeight);
      svg.innerHTML = '';

      renderNode(svg, root, 0, svgWidth, 0, totalTime, svgHeight);
      updateZoomInfo();
    }

    function renderNode(svg, node, x, width, depth, totalTime, svgHeight) {
      if (width < MIN_WIDTH_PX) return;

      var y = icicleMode
        ? depth * FRAME_HEIGHT
        : svgHeight - PADDING_BOTTOM - (depth + 1) * FRAME_HEIGHT;
      var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
      rect.setAttribute('class', 'frame');
      rect.setAttribute('x', x);
      rect.setAttribute('y', y);
      rect.setAttribute('width', width);
      rect.setAttribute('height', FRAME_HEIGHT - 1);
      rect.setAttribute('fill', frameColor(node.name));
      rect.setAttribute('rx', 1);

      if (searchPattern && searchPattern.test(node.name)) {
        rect.classList.add('highlight');
      }

      rect.addEventListener('click', function(e) {
        e.stopPropagation();
        zoomNode = node;
        render();
      });

      rect.addEventListener('mouseenter', function(e) {
        showTooltip(e, node, totalTime);
      });
      rect.addEventListener('mousemove', function(e) {
        positionTooltip(e);
      });
      rect.addEventListener('mouseleave', hideTooltip);

      svg.appendChild(rect);

      // Text label
      var maxChars = Math.floor((width - TEXT_PADDING * 2) / CHAR_WIDTH);
      if (maxChars > 2) {
        var label = node.name;
        if (label.length > maxChars) {
          label = label.substring(0, maxChars - 2) + '..';
        }
        var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('x', x + TEXT_PADDING);
        text.setAttribute('y', y + FRAME_HEIGHT - 5);
        text.textContent = label;
        svg.appendChild(text);
      }

      // Children
      if (node.children) {
        var childX = x;
        for (var i = 0; i < node.children.length; i++) {
          var child = node.children[i];
          var childWidth = (child.value / (node.value || 1)) * width;
          renderNode(svg, child, childX, childWidth, depth + 1, totalTime, svgHeight);
          childX += childWidth;
        }
      }
    }

    // --- Tooltip ---
    var tooltipEl = document.getElementById('tooltip');

    function showTooltip(e, node, totalTime) {
      var pct = totalTime > 0 ? (node.value / totalTime * 100).toFixed(2) : '0.00';
      var selfPct = totalTime > 0 ? (node.self_value / totalTime * 100).toFixed(2) : '0.00';
      tooltipEl.innerHTML =
        '<div class="tt-name">' + escapeHtml(node.name) + '</div>' +
        '<div class="tt-detail">Total: ' + node.value.toFixed(4) + 's (' + pct + '%)</div>' +
        '<div class="tt-detail">Self: ' + node.self_value.toFixed(4) + 's (' + selfPct + '%)</div>' +
        '<div class="tt-detail">Calls: ' + node.called + '</div>';
      tooltipEl.style.display = 'block';
      positionTooltip(e);
    }

    function positionTooltip(e) {
      var x = e.clientX + 12;
      var y = e.clientY + 12;
      if (x + tooltipEl.offsetWidth > window.innerWidth - 10) {
        x = e.clientX - tooltipEl.offsetWidth - 12;
      }
      if (y + tooltipEl.offsetHeight > window.innerHeight - 10) {
        y = e.clientY - tooltipEl.offsetHeight - 12;
      }
      tooltipEl.style.left = x + 'px';
      tooltipEl.style.top = y + 'px';
    }

    function hideTooltip() {
      tooltipEl.style.display = 'none';
    }

    function escapeHtml(s) {
      var div = document.createElement('div');
      div.appendChild(document.createTextNode(s));
      return div.innerHTML;
    }

    // --- Zoom ---
    function updateZoomInfo() {
      var info = document.getElementById('zoom-info');
      if (zoomNode && zoomNode !== threads[currentThreadIdx].data) {
        info.style.display = 'block';
        document.getElementById('zoom-target').textContent = zoomNode.name;
      } else {
        info.style.display = 'none';
      }
    }

    function resetZoom() {
      zoomNode = null;
      render();
    }

    document.getElementById('reset-zoom-btn').addEventListener('click', resetZoom);
    document.getElementById('zoom-reset-link').addEventListener('click', resetZoom);

    // --- Icicle toggle ---
    var icicleBtn = document.getElementById('toggle-icicle-btn');
    icicleBtn.addEventListener('click', function() {
      icicleMode = !icicleMode;
      icicleBtn.textContent = icicleMode ? 'Flame' : 'Icicle';
      render();
    });

    // --- Thread selector ---
    var threadSelect = document.getElementById('thread-select');
    for (var i = 0; i < threads.length; i++) {
      var opt = document.createElement('option');
      opt.value = i;
      opt.textContent = 'Thread ' + threads[i].id + ' (Fiber ' + threads[i].fiber_id + ')';
      threadSelect.appendChild(opt);
    }
    threadSelect.addEventListener('change', function() {
      currentThreadIdx = parseInt(this.value, 10);
      zoomNode = null;
      render();
    });

    // --- Search ---
    var searchInput = document.getElementById('search-input');
    var searchTimeout = null;
    searchInput.addEventListener('input', function() {
      clearTimeout(searchTimeout);
      searchTimeout = setTimeout(function() {
        var val = searchInput.value.trim();
        if (val.length > 0) {
          try {
            searchPattern = new RegExp(val, 'i');
          } catch(e) {
            searchPattern = new RegExp(val.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
          }
        } else {
          searchPattern = null;
        }
        render();
      }, 200);
    });

    // --- Resize ---
    var resizeTimeout = null;
    window.addEventListener('resize', function() {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(render, 150);
    });

    // --- Init ---
    render();
  })();
  </script>
</body>
</html>
