///|
pub(all) struct TimelineBucket {
  start_tick : Int
  end_tick : Int
  mut events : Int
  mut executions : Int
  mut metrics : Int
  mut messages : Int
}

///|
pub(all) struct TimelineView {
  bucket_size : Int
  buckets : Array[TimelineBucket]
}

///|
pub fn build_timeline(
  entries : Array[TraceEntry],
  bucket_size? : Int = 5,
) -> TimelineView {
  let size = if bucket_size < 1 { 1 } else { bucket_size }
  let buckets : Array[TimelineBucket] = []
  for entry in entries {
    let start = entry.tick / size * size
    let end = start + size - 1
    let index = find_timeline_bucket(buckets, start, end)
    if index < 0 {
      buckets.push(empty_timeline_bucket(start, end))
      add_entry_to_bucket(buckets[buckets.length() - 1], entry)
    } else {
      add_entry_to_bucket(buckets[index], entry)
    }
  }
  buckets.sort_by(fn(a, b) { a.start_tick.compare(b.start_tick) })
  { bucket_size: size, buckets }
}

///|
fn empty_timeline_bucket(start : Int, end : Int) -> TimelineBucket {
  {
    start_tick: start,
    end_tick: end,
    events: 0,
    executions: 0,
    metrics: 0,
    messages: 0,
  }
}

///|
fn find_timeline_bucket(
  buckets : Array[TimelineBucket],
  start : Int,
  end : Int,
) -> Int {
  for i in 0.. Unit {
  bucket.events += 1
  if entry.kind == "execute" {
    bucket.executions += 1
  }
  if entry.kind.contains("metric.") {
    bucket.metrics += 1
  }
  if entry.kind.contains("message.") {
    bucket.messages += 1
  }
}

///|
pub fn TimelineView::bucket_count(self : TimelineView) -> Int {
  self.buckets.length()
}

///|
pub fn TimelineView::total_events(self : TimelineView) -> Int {
  let mut total = 0
  for bucket in self.buckets {
    total += bucket.events
  }
  total
}

///|
pub fn TimelineView::peak_bucket(self : TimelineView) -> TimelineBucket {
  if self.buckets.length() == 0 {
    empty_timeline_bucket(0, 0)
  } else {
    let mut best = self.buckets[0]
    for bucket in self.buckets {
      if bucket.events > best.events {
        best = bucket
      }
    }
    best
  }
}

///|
pub fn TimelineBucket::line(self : TimelineBucket) -> String {
  self.start_tick.to_string() +
  ".." +
  self.end_tick.to_string() +
  " events=" +
  self.events.to_string() +
  " executions=" +
  self.executions.to_string() +
  " metrics=" +
  self.metrics.to_string() +
  " messages=" +
  self.messages.to_string()
}

///|
pub fn TimelineView::render(self : TimelineView) -> String {
  let buf = StringBuilder::new()
  buf.write_string("# Timeline bucket=" + self.bucket_size.to_string() + "\n")
  for bucket in self.buckets {
    buf.write_string("- " + bucket.line() + "\n")
  }
  buf.to_string()
}

///|
pub fn Sim::timeline(self : Sim, bucket_size? : Int = 5) -> TimelineView {
  build_timeline(self.trace(), bucket_size~)
}