///|
/// Public overview of one signal and its observable activity.
pub struct SignalOverview {
  id : String
  path : String
  width : Int
  kind : String
  depth : Int
  changes : Int
  transitions : Int
  unknown : Int
} derive(Debug, Eq)

///|
/// Public overview of a complete trace.
pub struct TraceOverview {
  signal_count : Int
  change_count : Int
  end_time : Int64
  maximum_depth : Int
  valid : Bool
  diagnostics : Int
} derive(Debug, Eq)

///|
/// Create one signal overview.
pub fn overview_signal(trace : Trace, signal : Signal) -> SignalOverview {
  let item = stats(trace, signal.id)
  {
    id: signal.id,
    path: signal_path(signal),
    width: signal.width,
    kind: signal.kind,
    depth: signal_depth(signal),
    changes: item.changes,
    transitions: edges(trace, signal.id).length(),
    unknown: item.unknown_values,
  }
}

///|
/// Create all signal overviews.
pub fn signal_overviews(trace : Trace) -> Array[SignalOverview] {
  trace.signals.map(fn(signal) { overview_signal(trace, signal) })
}

///|
/// Create a trace overview for dashboards and CLI summaries.
pub fn trace_overview(trace : Trace) -> TraceOverview {
  let diagnostics = deep_validate(trace)
  {
    signal_count: trace.signals.length(),
    change_count: trace.changes.length(),
    end_time: trace.end_time,
    maximum_depth: maximum_scope_depth(trace),
    valid: deep_is_valid(trace),
    diagnostics: diagnostics.length(),
  }
}

///|
/// Serialize a signal overview as a stable JSON object.
pub fn SignalOverview::to_json_string(self : SignalOverview) -> String {
  "{\"id\":\"" +
  escape_text(self.id) +
  "\",\"path\":\"" +
  escape_text(self.path) +
  "\",\"width\":" +
  self.width.to_string() +
  ",\"kind\":\"" +
  escape_text(self.kind) +
  "\",\"depth\":" +
  self.depth.to_string() +
  ",\"changes\":" +
  self.changes.to_string() +
  ",\"transitions\":" +
  self.transitions.to_string() +
  ",\"unknown\":" +
  self.unknown.to_string() +
  "}"
}

///|
/// Serialize a trace overview as a stable JSON object.
pub fn TraceOverview::to_json_string(self : TraceOverview) -> String {
  "{\"signal_count\":" +
  self.signal_count.to_string() +
  ",\"change_count\":" +
  self.change_count.to_string() +
  ",\"end_time\":" +
  self.end_time.to_string() +
  ",\"maximum_depth\":" +
  self.maximum_depth.to_string() +
  ",\"valid\":" +
  self.valid.to_string() +
  ",\"diagnostics\":" +
  self.diagnostics.to_string() +
  "}"
}

///|
/// Render a complete catalog as JSON.
pub fn overview_json(trace : Trace) -> String {
  "{\"trace\":" +
  trace_overview(trace).to_json_string() +
  ",\"signals\":[" +
  signal_overviews(trace).map(fn(item) { item.to_json_string() }).join(",") +
  "]}"
}