// 把一条报文渲染成人能读的形式。
//
// 设计目标:别人拿到一条现场抓包的原始字节,粘进 CLI 就能看懂每个字段
// 是什么、为什么 CRC 过不了。所以这里的输出不是「调试打印」,而是解释。

///|
/// 一条报文的单行摘要,适合日志与批量扫描。
pub fn Frame::summary(self : Frame) -> String {
  let seg = self.segment
  let name = match command_name(seg.cn) {
    Some(n) => n
    None => "未知命令"
  }
  let qn = match QnTime::parse(seg.qn) {
    Some(t) => t.text()
    None => seg.qn
  }
  "#\{seg.cn} \{name} · MN=\{seg.mn} · \{qn} · CP \{seg.cp.length()} 项"
}

///|
/// 一条报文的逐字段解释。
pub fn Frame::explain(self : Frame) -> String {
  let seg = self.segment
  let sb = StringBuilder()
  let segment_text = seg.text()
  let bytes = utf8_len(segment_text)
  let crc = crc16_hex(segment_text)

  sb.write_string(line_kv("长度", "\{bytes} 字节(数据段)", ""))
  sb.write_string(line_kv("CRC", crc, "校验通过"))
  sb.write_string(line_kv("版本", seg.flag.version().label(), ""))
  sb.write_string(line_kv("Flag", seg.flag.text(), flag_note(seg.flag)))
  sb.write_string(line_kv("QN", seg.qn, qn_note(seg.qn)))
  sb.write_string(line_kv("ST", seg.st, system_name(seg.st)))
  sb.write_string(line_kv("CN", seg.cn, cn_note(seg.cn)))
  sb.write_string(
    line_kv("PW", if seg.pw == "" { "(未提供)" } else { seg.pw }, ""),
  )
  sb.write_string(line_kv("MN", seg.mn, mn_note(seg.mn)))
  match seg.pnum {
    Some(v) => sb.write_string(line_kv("PNUM", v.to_string(), "总包数"))
    None => ()
  }
  match seg.pno {
    Some(v) => sb.write_string(line_kv("PNO", v.to_string(), "当前包号"))
    None => ()
  }
  let mut i = 0
  while i < seg.extra.length() {
    sb.write_string(
      line_kv(
        seg.extra[i].name,
        join_values(seg.extra[i].values),
        "扩展字段",
      ),
    )
    i = i + 1
  }
  if seg.cp.length() == 0 {
    sb.write_string(line_kv("CP", "(空)", ""))
  } else {
    sb.write_string(line_kv("CP", "\{seg.cp.length()} 项", ""))
    let mut j = 0
    while j < seg.cp.items.length() {
      let item = seg.cp.items[j]
      sb.write_string(
        "  \{pad_display(item.name, 18)}\{join_values(item.values)}\n",
      )
      j = j + 1
    }
  }
  let warnings = seg.validate()
  if warnings.length() > 0 {
    sb.write_string("\n")
    sb.write_string("提示:\n")
    for w in warnings {
      sb.write_string("  · \{w}\n")
    }
  }
  sb.to_string()
}

///|
fn line_kv(label : String, value : String, note : String) -> String {
  let head = pad_display(label, 8)
  if note == "" {
    "\{head}\{value}\n"
  } else {
    "\{head}\{pad_display(value, 30)}\{note}\n"
  }
}

///|
fn join_values(values : Array[String]) -> String {
  let sb = StringBuilder()
  let mut i = 0
  while i < values.length() {
    if i > 0 {
      sb.write_string(", ")
    }
    sb.write_string(values[i])
    i = i + 1
  }
  sb.to_string()
}

///|
fn flag_note(flag : Flag) -> String {
  let version_bits = zero_pad((flag.raw >> 2) & 0x3F, 6)
  let split = if flag.is_split() { "分包" } else { "不分包" }
  let ack = if flag.needs_ack() { "需应答" } else { "不需应答" }
  "版本位 \{version_bits} · \{split} · \{ack}"
}

///|
fn qn_note(qn : String) -> String {
  match QnTime::parse(qn) {
    Some(t) => t.text()
    None => "无法解析为时间戳"
  }
}

///|
fn cn_note(cn : String) -> String {
  match lookup_command(cn) {
    Some(info) => "\{info.name} · \{info.direction.label()}"
    None => "未登记的命令码"
  }
}

///|
fn mn_note(mn : String) -> String {
  "\{chars_of(mn).length()} 位"
}

///|
/// 系统编码的中文名,逐条对应 HJ 212-2017 表 5《系统编码表》。
///
/// 编码分段:10~29 环境质量、30~49 环境污染源、50~69 工况、91~99 系统交互。
/// 最容易被记错的是 32 —— 它是「地表水体环境污染源」(水质),不是烟气;
/// 烟气是 31「大气环境污染源」,而烟气工况是 51。
fn system_name(st : String) -> String {
  match st {
    "21" => "地表水质量监测"
    "22" => "空气质量监测"
    "23" => "声环境质量监测"
    "24" => "地下水质量监测"
    "25" => "土壤质量监测"
    "26" => "海水质量监测"
    "27" => "挥发性有机物监测"
    "31" => "大气环境污染源"
    "32" => "地表水体环境污染源"
    "33" => "地下水体环境污染源"
    "34" => "海洋环境污染源"
    "35" => "土壤环境污染源"
    "36" => "声环境污染源"
    "37" => "振动环境污染源"
    "38" => "放射性环境污染源"
    "39" => "工地扬尘污染源"
    "41" => "电磁环境污染源"
    "51" => "烟气排放过程监控"
    "52" => "污水排放过程监控"
    "91" => "系统交互"
    _ => ""
  }
}