class Vernier::Output::Firefox

def counter_data

def counter_data
  profile.hooks.flat_map do |hook|
    if hook.respond_to?(:firefox_counters)
      hook.firefox_counters
    end
  end.compact
end

def data

def data
  #markers_by_thread = profile.markers.group_by { |marker| marker[0] }
  threads = profile.threads.map do |ruby_thread_id, thread_info|
    #markers = markers_by_thread[ruby_thread_id] || []
    Thread.new(
      ruby_thread_id,
      profile,
      @categorizer,
      #markers: markers,
      **thread_info,
    )
  end
  {
    meta: {
      interval: 1, # FIXME: memory vs wall
      startTime: profile.started_at / 1_000_000.0,
      #endTime: (profile.timestamps&.max || 0) / 1_000_000.0,
      processType: 0,
      product: "Ruby/Vernier",
      stackwalk: 1,
      version: 28,
      preprocessedProfileVersion: 48,
      symbolicated: true,
      markerSchema: marker_schema,
      sampleUnits: {
        time: "ms",
        eventDelay: "ms",
        threadCPUDelta: "µs"
      }, # FIXME: memory vs wall
      categories: @categorizer.categories.map do |category|
        {
          name: category.name,
          color: category.color,
          subcategories: category.subcategories.map(&:name)
        }
      end,
      sourceCodeIsNotOnSearchfox: true,
      initialVisibleThreads: threads.each_index.to_a,
      initialSelectedThreads: Array(threads.find_index(&:is_start)),
      vernierUserMetadata: profile.meta[:user_metadata],
      extra: [
        label: "User-Supplied Metadata",
        entries: profile.meta[:user_metadata].map do |k, v|
          {
            label: k,
            format: "string",
            value: v
          }
        end
      ]
    },
    counters: counter_data,
    libs: [],
    threads: threads.map(&:data)
  }
end

def initialize(profile)

def initialize(profile)
  @profile = profile
  @categorizer = Categorizer.new
end

def marker_schema

def marker_schema
  hook_additions = profile.hooks.flat_map do |hook|
    if hook.respond_to?(:firefox_marker_schema)
      hook.firefox_marker_schema
    end
  end.compact
  [
    {
      name: "THREAD_RUNNING",
      display: [ "marker-chart" ],
      data: [
        {
          label: "Description",
          value: "The thread has acquired the GVL and is executing"
        }
      ]
    },
    {
      name: "THREAD_STALLED",
      display: [ "marker-chart" ],
      data: [
        {
          label: "Description",
          value: "The thread is ready, but stalled waiting for the GVL to be available"
        }
      ]
    },
    {
      name: "THREAD_SUSPENDED",
      display: [ "marker-chart" ],
      data: [
        {
          label: "Description",
          value: "The thread has voluntarily released the GVL (ex. to sleep, for I/O, waiting on a lock)"
        }
      ]
    },
    {
      name: "GC_PAUSE",
      display: [ "marker-chart", "marker-table", "timeline-overview" ],
      tooltipLabel: "{marker.name} - {marker.data.state}",
      data: [
        {
          label: "Description",
          value: "All threads are paused as GC is performed"
        },
        { key: "state", format: "string" },
        { key: "gc_by", format: "string" },
      ]
    },
    {
      name: "FIBER_SWITCH",
      display: [ "marker-chart", "marker-table", "timeline-overview" ],
      tooltipLabel: "{marker.name} - {marker.data.fiber_id}",
      data: [
        {
          label: "Description",
          value: "Switch running Fiber"
        },
        { key: "fiber_id", format: "integer" },
      ]
    },
    *hook_additions
  ]
end

def output(gzip: false)

def output(gzip: false)
  result = ::JSON.fast_generate(data)
  if gzip
    require "zlib"
    result = Zlib.gzip(result)
  end
  result
end