///|
fn ir_bool(value : Bool) -> String {
  if value {
    "true"
  } else {
    "false"
  }
}

///|
pub fn SemanticIndex::ir_module_names(self : SemanticIndex) -> String {
  let builder = StringBuilder()
  builder.write_char('[')
  for index, mod in self.modules {
    if index > 0 {
      builder.write_char(',')
    }
    builder.write_char('"')
    builder.write_string(mod.name)
    builder.write_char('"')
  }
  builder.write_char(']')
  builder.to_string()
}

///|
pub fn SemanticIndex::ir_contracts(self : SemanticIndex) -> String {
  let builder = StringBuilder()
  builder.write_char('[')
  for index, contract in self.port_contracts() {
    if index > 0 {
      builder.write_char(',')
    }
    builder.write_string("{\"parent\":\"")
    builder.write_string(contract.parent_module)
    builder.write_string("\",\"instance\":\"")
    builder.write_string(contract.instance_name)
    builder.write_string("\",\"port\":\"")
    builder.write_string(contract.port_name)
    builder.write_string("\",\"status\":\"")
    builder.write_string(
      match contract.status {
        Match => "match"
        WidthMismatch => "width-mismatch"
        DirectionMismatch => "direction-mismatch"
        MissingPort => "missing-port"
        UnknownModule => "unknown-module"
        UnknownWidth => "unknown-width"
      },
    )
    builder.write_string("\"}")
  }
  builder.write_char(']')
  builder.to_string()
}

///|
pub fn SemanticIndex::ir_metrics(self : SemanticIndex) -> String {
  let builder = StringBuilder()
  builder.write_string("{\"module_count\":")
  builder.write_object(self.module_count())
  builder.write_string(",\"edge_count\":")
  builder.write_object(self.edge_count())
  builder.write_string(",\"root_count\":")
  builder.write_object(self.hierarchy_roots().length())
  builder.write_string(",\"leaf_count\":")
  let mut leaves = 0
  for mod in self.modules {
    if self.module_is_leaf(mod.name) {
      leaves += 1
    }
  }
  builder.write_object(leaves)
  builder.write_string(",\"release_ready\":")
  builder.write_string(ir_bool(self.release_ready()))
  builder.write_char('}')
  builder.to_string()
}

///|
pub fn SemanticIndex::ir_document(self : SemanticIndex) -> String {
  "{\"modules\":" +
  self.ir_module_names() +
  ",\"metrics\":" +
  self.ir_metrics() +
  ",\"contracts\":" +
  self.ir_contracts() +
  ",\"issues\":\"" +
  self.issue_summary() +
  "\"}"
}

///|
pub fn SemanticIndex::ir_lines(self : SemanticIndex) -> Array[String] {
  let result : Array[String] = []
  for line in self.ir_document().split("\n") {
    result.push(line.to_owned())
  }
  result
}

///|
pub fn SemanticIndex::ir_is_nonempty(self : SemanticIndex) -> Bool {
  self.module_count() > 0
}

///|
pub fn SemanticIndex::ir_has_contract_errors(self : SemanticIndex) -> Bool {
  self.contract_status_count(WidthMismatch) > 0 ||
  self.contract_status_count(DirectionMismatch) > 0
}

///|
pub fn SemanticIndex::ir_size(self : SemanticIndex) -> Int {
  self.ir_document().to_array().length()
}

///|
pub fn SemanticIndex::ir_summary(self : SemanticIndex) -> String {
  "bytes=" +
  self.ir_size().to_string() +
  ", modules=" +
  self.module_count().to_string() +
  ", contracts=" +
  self.port_contracts().length().to_string() +
  ", errors=" +
  self.ir_has_contract_errors().to_string()
}