// 输出渲染:text / json / html。

///|
fn text_of(value : String?) -> String {
  match value {
    Some(text) => text
    None => ""
  }
}

///|
fn number_of(value : Int?) -> String {
  match value {
    Some(number) => number.to_string()
    None => ""
  }
}

///|
fn json_number(value : Int?) -> String {
  match value {
    Some(number) => number.to_string()
    None => "null"
  }
}

///|
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("\\u")
          builder.write_string(hex4(code))
        } else {
          builder.write_char(c)
        }
      }
    }
  }
  builder.to_string()
}

///|
fn hex4(n : Int) -> String {
  let digits = "0123456789abcdef"
  let builder = StringBuilder()
  let mut shift = 12
  while shift >= 0 {
    let digit = (n >> shift) & 15
    match digits.get_char(digit) {
      Some(c) => builder.write_char(c)
      None => ()
    }
    shift = shift - 4
  }
  builder.to_string()
}

///|
fn html_escape(s : String) -> String {
  let builder = StringBuilder()
  for c in s {
    match c {
      '&' => builder.write_string("&")
      '<' => builder.write_string("<")
      '>' => builder.write_string(">")
      '"' => builder.write_string(""")
      '\'' => builder.write_string("'")
      _ => builder.write_char(c)
    }
  }
  builder.to_string()
}

///|
/// 渲染为纯文本(`-` / `+` 前缀)。
pub fn render_text(result : DiffResult) -> String {
  let builder = StringBuilder()
  for row in result.rows {
    match row.kind {
      Equal => {
        builder.write_string("  ")
        builder.write_string(text_of(row.old_line))
        builder.write_string("\n")
      }
      Delete => {
        builder.write_string("- ")
        builder.write_string(text_of(row.old_line))
        builder.write_string("\n")
      }
      Insert => {
        builder.write_string("+ ")
        builder.write_string(text_of(row.new_line))
        builder.write_string("\n")
      }
      Change => {
        builder.write_string("- ")
        builder.write_string(text_of(row.old_line))
        builder.write_string("\n+ ")
        builder.write_string(text_of(row.new_line))
        builder.write_string("\n")
      }
    }
  }
  builder.to_string()
}

///|
/// 渲染为单行 JSON。
pub fn render_json(result : DiffResult) -> String {
  let builder = StringBuilder()
  builder.write_string("{\"stats\":{\"added\":")
  builder.write_string(result.stats.added.to_string())
  builder.write_string(",\"removed\":")
  builder.write_string(result.stats.removed.to_string())
  builder.write_string(",\"changed\":")
  builder.write_string(result.stats.changed.to_string())
  builder.write_string(",\"unchanged\":")
  builder.write_string(result.stats.unchanged.to_string())
  builder.write_string("},\"rows\":[")
  let mut first = true
  for row in result.rows {
    if !first {
      builder.write_string(",")
    }
    let kind = match row.kind {
      Equal => "equal"
      Delete => "delete"
      Insert => "insert"
      Change => "change"
    }
    builder.write_string("{\"kind\":\"\{kind}\",\"old\":\"")
    builder.write_string(json_escape(text_of(row.old_line)))
    builder.write_string("\",\"new\":\"")
    builder.write_string(json_escape(text_of(row.new_line)))
    builder.write_string("\",\"oldNo\":")
    builder.write_string(json_number(row.old_no))
    builder.write_string(",\"newNo\":")
    builder.write_string(json_number(row.new_no))
    builder.write_string("}")
    first = false
  }
  builder.write_string("],\"patch\":\"")
  builder.write_string(json_escape(result.patch))
  builder.write_string("\"}")
  builder.to_string()
}

///|
fn render_spans(spans : Array[WordSpan], fallback : String) -> String {
  if spans.length() == 0 {
    return html_escape(fallback)
  }
  let builder = StringBuilder()
  for span in spans {
    let class_name = match span.kind {
      Equal => "eq"
      Delete => "del"
      Insert => "ins"
    }
    builder.write_string("")
    builder.write_string(html_escape(span.text))
    builder.write_string("")
  }
  builder.to_string()
}

///|
/// 渲染为独立 HTML 页面(左右对照)。
pub fn render_html(
  result : DiffResult,
  old_name : String,
  new_name : String,
) -> String {
  let builder = StringBuilder()
  builder.write_string(
    "\n\n\n\n",
  )
  builder.write_string("mbt-diffview\n\n\n\n
\n

mbt-diffview

\n", ) builder.write_string("
") builder.write_string("+\{result.stats.added} ") builder.write_string("-\{result.stats.removed} ") builder.write_string("~\{result.stats.changed} ") builder.write_string("=\{result.stats.unchanged}") builder.write_string("
\n
\n\n") builder.write_string( "\n", ) builder.write_string( "\n\n", ) for row in result.rows { let class_name = match row.kind { Equal => "equal" Delete => "delete" Insert => "insert" Change => "change" } builder.write_string("") builder.write_string("") builder.write_string("") builder.write_string("") builder.write_string("\n") } builder.write_string("\n
\{html_escape(old_name)}\{html_escape(new_name)}
\{number_of(row.old_no)}") builder.write_string(render_spans(row.old_spans, text_of(row.old_line))) builder.write_string("\{number_of(row.new_no)}") builder.write_string(render_spans(row.new_spans, text_of(row.new_line))) builder.write_string("
\n\n\n") builder.to_string() }