// 指令参数(CP)数据区。
//
// 标准约定:字段名与字段值用 `=` 连接;同一项的不同取值用 `,` 分隔;
// 不同项之间用 `;` 分隔;整个数据区外面套一层 `&& ... &&`。
//
// 解析必须容忍现场设备的写法差异:空格、末尾多余的分号、缺失的 `&&`。
// 但序列化一律输出规范形式,因此「解码再编码」是一步归一化,而不是原样回吐。

///|
pub fn DataArea::new() -> DataArea {
  { items: [], }
}

///|
/// 解析数据区文本。
///
/// 接受带 `&&...&&` 包装的形式,也接受裸数据区(便于单独测试数据区)。
pub fn DataArea::parse(text : String) -> Result[DataArea, DecodeError] {
  let mut cs = trim_chars(chars_of(text))
  // 标准 6.3.2 规定指令参数 CP 的取值是 `&&数据区&&`。没有这层包装说明
  // 对方用的不是 HJ 212 的数据区格式,宁可报错也不要猜。
  if cs.length() < 2 || cs[0] != '&' || cs[1] != '&' {
    return Err(BadDataArea("数据区必须以 \"&&\" 开头"))
  }
  let close = find_seq(cs, "&&", 2)
  if close < 0 {
    return Err(BadDataArea("以 \"&&\" 开头但没有结束的 \"&&\""))
  }
  let mut tail = close + 2
  while tail < cs.length() {
    if !is_space_char(cs[tail]) {
      return Err(TrailingGarbage(slice_chars(cs, tail, cs.length())))
    }
    tail = tail + 1
  }
  cs = trim_chars(chars_slice(cs, 2, close))
  let items : Array[DataItem] = []
  if cs.length() == 0 {
    return Ok({ items, })
  }
  let mut start = 0
  let mut i = 0
  while i <= cs.length() {
    let at_end = i == cs.length()
    if at_end || cs[i] == ';' {
      let piece = trim_chars(chars_slice(cs, start, i))
      if piece.length() > 0 {
        match parse_item(piece) {
          Ok(item) => items.push(item)
          Err(e) => return Err(e)
        }
      }
      start = i + 1
    }
    i = i + 1
  }
  Ok({ items, })
}

///|
/// 解析单个 `名称=值[,值...]`。
fn parse_item(cs : Array[Char]) -> Result[DataItem, DecodeError] {
  let eq = find_char(cs, '=', 0)
  if eq < 0 {
    return Err(BadField(slice_chars(cs, 0, cs.length())))
  }
  let name = trim_text(slice_chars(cs, 0, eq))
  if name == "" {
    return Err(BadDataArea("出现空的字段名"))
  }
  if !is_valid_field_name(name) {
    return Err(BadDataArea("字段名 \"\{name}\" 含非法字符"))
  }
  let vcs = trim_chars(chars_slice(cs, eq + 1, cs.length()))
  let values : Array[String] = []
  let mut start = 0
  let mut i = 0
  while i <= vcs.length() {
    let at_end = i == vcs.length()
    if at_end || vcs[i] == ',' {
      values.push(trim_text(slice_chars(vcs, start, i)))
      start = i + 1
    }
    i = i + 1
  }
  Ok({ name, values, })
}

///|
fn find_char(cs : Array[Char], target : Char, from : Int32) -> Int32 {
  let mut i = from
  while i < cs.length() {
    if cs[i] == target {
      return i
    }
    i = i + 1
  }
  -1
}

///|
/// 字段名只允许字母、数字、下划线与连字符(如 `Data-Flag`)。
fn is_valid_field_name(name : String) -> Bool {
  if name == "" {
    return false
  }
  for c in name {
    let ok = (c >= 'A' && c <= 'Z') ||
      (c >= 'a' && c <= 'z') ||
      (c >= '0' && c <= '9') ||
      c == '_' ||
      c == '-'
    if !ok {
      return false
    }
  }
  true
}

///|
/// 渲染成规范形式(不含 `&&` 包装)。
pub fn DataArea::text(self : DataArea) -> String {
  let sb = StringBuilder()
  let mut i = 0
  while i < self.items.length() {
    if i > 0 {
      sb.write_char(';')
    }
    let item = self.items[i]
    sb.write_string(item.name)
    sb.write_char('=')
    let mut j = 0
    while j < item.values.length() {
      if j > 0 {
        sb.write_char(',')
      }
      sb.write_string(item.values[j])
      j = j + 1
    }
    i = i + 1
  }
  sb.to_string()
}

///|
/// 渲染成报文里的 `CP` 取值,带 `&&` 包装。
pub fn DataArea::wrapped_text(self : DataArea) -> String {
  "&&\{self.text()}&&"
}

///|
/// 取某个字段的第一个值。
pub fn DataArea::get(self : DataArea, name : String) -> String? {
  let mut i = 0
  while i < self.items.length() {
    if self.items[i].name == name {
      let values = self.items[i].values
      if values.length() > 0 {
        return Some(values[0])
      }
      return Some("")
    }
    i = i + 1
  }
  None
}

///|
/// 取某个字段的全部值。
pub fn DataArea::get_all(self : DataArea, name : String) -> Array[String] {
  let out : Array[String] = []
  let mut i = 0
  while i < self.items.length() {
    if self.items[i].name == name {
      for v in self.items[i].values {
        out.push(v)
      }
    }
    i = i + 1
  }
  out
}

///|
/// 字段是否出现。
pub fn DataArea::has(self : DataArea, name : String) -> Bool {
  match self.get(name) {
    Some(_) => true
    None => false
  }
}

///|
/// 追加或覆盖一个单值字段。
pub fn DataArea::set(self : DataArea, name : String, value : String) -> Unit {
  self.set_values(name, [value])
}

///|
/// 追加或覆盖一个多值字段。
pub fn DataArea::set_values(
  self : DataArea,
  name : String,
  values : Array[String],
) -> Unit {
  let mut i = 0
  while i < self.items.length() {
    if self.items[i].name == name {
      self.items[i].values = values
      return
    }
    i = i + 1
  }
  self.items.push({ name, values, })
}

///|
/// 字段个数。
pub fn DataArea::length(self : DataArea) -> Int32 {
  self.items.length()
}