// 输出渲染:text / json / 类型列表。

///|
fn hex2(n : Int) -> String {
  let digits = "0123456789abcdef"
  let builder = StringBuilder()
  match digits.get_char(n / 16) {
    Some(c) => builder.write_char(c)
    None => ()
  }
  match digits.get_char(n % 16) {
    Some(c) => builder.write_char(c)
    None => ()
  }
  builder.to_string()
}

///|
fn json_escape(s : String) -> String {
  let builder = StringBuilder()
  for c in s {
    match c {
      '"' => builder.write_string("\\\"")
      '\\' => builder.write_string("\\\\")
      '\n' => builder.write_string("\\n")
      '\r' => builder.write_string("\\r")
      '\t' => builder.write_string("\\t")
      _ => {
        let code = c.to_int()
        if code < 32 {
          builder.write_string("\\u00")
          builder.write_string(hex2(code))
        } else {
          builder.write_char(c)
        }
      }
    }
  }
  builder.to_string()
}

///|
fn padded(s : String, width : Int) -> String {
  let builder = StringBuilder()
  builder.write_string(s)
  let mut n = s.length()
  while n < width {
    builder.write_char(' ')
    n = n + 1
  }
  builder.to_string()
}

///|
/// 渲染人类可读的识别结果。
pub fn render_text(
  path : String,
  detection : Detection,
  explain : Bool,
) -> String {
  let builder = StringBuilder()
  builder.write_string(path)
  builder.write_string("\n")
  match detection.file_type {
    Some(file_type) => {
      builder.write_string("  type:       \{file_type.id}\n")
      builder.write_string("  mime:       \{file_type.mime}\n")
      builder.write_string("  extension:  .\{file_type.ext}\n")
      builder.write_string(
        "  category:   \{category_name(file_type.category)}\n",
      )
    }
    None => {
      builder.write_string("  type:       unknown\n")
      builder.write_string("  mime:       application/octet-stream\n")
      builder.write_string("  extension:  -\n")
      builder.write_string("  category:   unknown\n")
    }
  }
  builder.write_string(
    "  confidence: \{confidence_name(detection.confidence)} (\{detection.score}/100)\n",
  )
  builder.write_string("  evidence:   \{detection.reason}\n")
  if explain {
    builder.write_string("  offset:     \{detection.offset}\n")
    builder.write_string("  bytes:      \{detection.matched_len}\n")
    let mut index = 1
    for item in detection.evidence {
      builder.write_string(
        "    [\{index}] \{item.kind} +\{item.weight} @\{item.offset}: \{item.label}\n",
      )
      index = index + 1
    }
  }
  if detection.candidates.length() > 0 {
    builder.write_string("  candidates: ")
    let mut first = true
    for candidate in detection.candidates {
      if !first {
        builder.write_string(", ")
      }
      builder.write_string(candidate.id)
      first = false
    }
    builder.write_string("\n")
  }
  builder.to_string()
}

///|
/// 渲染单行 JSON。
pub fn render_json(path : String, detection : Detection) -> String {
  let builder = StringBuilder()
  builder.write_string("{\"path\":\"\{json_escape(path)}\"")
  match detection.file_type {
    Some(file_type) => {
      builder.write_string(",\"type\":\"\{json_escape(file_type.id)}\"")
      builder.write_string(",\"mime\":\"\{json_escape(file_type.mime)}\"")
      builder.write_string(",\"ext\":\"\{json_escape(file_type.ext)}\"")
      builder.write_string(
        ",\"category\":\"\{category_name(file_type.category)}\"",
      )
    }
    None => {
      builder.write_string(",\"type\":\"unknown\"")
      builder.write_string(",\"mime\":\"application/octet-stream\"")
      builder.write_string(",\"ext\":\"\"")
      builder.write_string(",\"category\":\"unknown\"")
    }
  }
  builder.write_string(
    ",\"confidence\":\"\{confidence_name(detection.confidence)}\"",
  )
  builder.write_string(",\"score\":\{detection.score}")
  builder.write_string(",\"offset\":\{detection.offset}")
  builder.write_string(",\"bytes\":\{detection.matched_len}")
  builder.write_string(",\"reason\":\"\{json_escape(detection.reason)}\"")
  builder.write_string(",\"evidence\":[")
  let mut first_evidence = true
  for item in detection.evidence {
    if !first_evidence {
      builder.write_string(",")
    }
    builder.write_string("{\"kind\":\"\{json_escape(item.kind)}\"")
    builder.write_string(",\"label\":\"\{json_escape(item.label)}\"")
    builder.write_string(",\"offset\":\{item.offset}")
    builder.write_string(",\"bytes\":\{item.matched_len}")
    builder.write_string(",\"weight\":\{item.weight}}")
    first_evidence = false
  }
  builder.write_string("]")
  builder.write_string(",\"candidates\":[")
  let mut first = true
  for candidate in detection.candidates {
    if !first {
      builder.write_string(",")
    }
    builder.write_string("\"\{json_escape(candidate.id)}\"")
    first = false
  }
  builder.write_string("]}")
  builder.to_string()
}

///|
/// 渲染类型清单(`--list-types`)。
pub fn render_types() -> String {
  let builder = StringBuilder()
  builder.write_string(
    "\{padded("ID", 12)}\{padded("EXT", 10)}\{padded("CATEGORY", 12)}MIME\n",
  )
  for file_type in known_types() {
    builder.write_string("\{padded(file_type.id, 12)}")
    builder.write_string("\{padded(".\{file_type.ext}", 10)}")
    builder.write_string("\{padded(category_name(file_type.category), 12)}")
    builder.write_string("\{file_type.mime}\n")
  }
  builder.to_string()
}