// 帧层:包头、长度、数据段、CRC、包尾,以及流式分帧。
//
// 一条报文形如:
//
//     ##0101QN=...;ST=32;CN=1062;...;CP=&&RtdInterval=30&&1C80\r\n
//     └┬─┘└──┘└────────────────── 数据段 ──────────────────┘└──┘└──┘
//     包头 长度                       CRC  包尾
//
// 长度字段是数据段的 UTF-8 字节数,CRC 也按字节计算;两者必须用同一套
// 计数方式,否则含中文的数据区会算错长度,进而错位到 CRC。

///|
/// 编码一条报文。
///
/// 数据段由字段重新生成(而不是复用输入原文),因此输出是规范形式,
/// 空格、字段顺序、`&&` 包装都会被归一化。
pub fn Frame::encode(self : Frame) -> String {
  let segment = self.segment.text()
  let len = utf8_len(segment)
  let crc = crc16_text(segment)
  "##\{zero_pad(len, 4)}\{segment}\{hex4(crc)}\r\n"
}

///|
/// 解码一条报文。
///
/// 严格校验:包头、长度、CRC、包尾全部通过才算成功。字段取值范围是否
/// 符合行业惯例不在这一步判断,交给 `Segment::validate`。
pub fn Frame::decode(text : String) -> Result[Frame, DecodeError] {
  let cs = chars_of(text)
  // 允许前置空白,方便从文档里复制粘贴
  let mut start = 0
  while start < cs.length() && is_space_char(cs[start]) {
    start = start + 1
  }
  if start + 2 > cs.length() || cs[start] != '#' || cs[start + 1] != '#' {
    return Err(MissingHeader)
  }
  let len_start = start + 2
  if len_start + 4 > cs.length() {
    return Err(Truncated(0, cs.length() - len_start))
  }
  let len_field = slice_chars(cs, len_start, len_start + 4)
  let declared = match parse_uint_all(len_field) {
    Some(v) => v
    None => return Err(BadLengthField(len_field))
  }
  // 按 UTF-8 字节累加,定位数据段结束位置
  let seg_start = len_start + 4
  let mut i = seg_start
  let mut acc = 0
  while acc < declared {
    if i >= cs.length() {
      return Err(Truncated(declared, acc))
    }
    acc = acc + utf8_char_len(cs[i])
    i = i + 1
  }
  if acc != declared {
    return Err(Truncated(declared, acc))
  }
  let seg_end = i
  if seg_end == seg_start {
    return Err(EmptySegment)
  }
  let crc_start = seg_end
  if crc_start + 4 > cs.length() {
    return Err(Truncated(declared, declared))
  }
  let crc_field = slice_chars(cs, crc_start, crc_start + 4)
  let crc_chars = chars_of(crc_field)
  let mut declared_crc = 0
  let mut k = 0
  while k < 4 {
    let v = hex_value(crc_chars[k])
    if v < 0 {
      return Err(
        BadCrc(crc_field, crc16_hex(slice_chars(cs, seg_start, seg_end))),
      )
    }
    declared_crc = declared_crc * 16 + v
    k = k + 1
  }
  let segment_text = slice_chars(cs, seg_start, seg_end)
  let computed = crc16_text(segment_text)
  if declared_crc != computed {
    return Err(BadCrc(crc_field, hex4(computed)))
  }
  // 包尾:CRLF、LF、CR 都接受,其余内容视为错误
  let tail_start = crc_start + 4
  let tail = slice_chars(cs, tail_start, cs.length())
  if tail != "\r\n" && tail != "\n" && tail != "\r" {
    return Err(BadTail(tail))
  }
  match Segment::parse(segment_text) {
    Ok(segment) => Ok({ segment, })
    Err(e) => Err(e)
  }
}

///|
/// 只做校验,成功时返回数据段长度。
pub fn Frame::check(text : String) -> Result[Int32, DecodeError] {
  match Frame::decode(text) {
    Ok(frame) => Ok(utf8_len(frame.segment.text()))
    Err(e) => Err(e)
  }
}

///|
/// 流式分帧的结果。
pub(all) enum FrameEvent {
  /// 解出一条合法报文
  Good(Frame)
  /// 一条报文结构不合法,附带原始文本
  Bad(DecodeError, String)
  /// 不是报文的字节,属于噪声(半包残片、日志前缀等)
  Noise(String)
}

///|
/// 增量分帧器。
///
/// 现场设备按 TCP 流发送,一次 `read` 可能拿到半条报文,也可能拿到三条半。
/// 分帧器负责在任意切分点保持状态:喂进去一段文本,吐出这段文本里能确认的
/// 全部事件,剩下的留在缓冲区里等下一段。
pub(all) struct Framer {
  mut buffer : String
}

///|
pub fn Framer::new() -> Framer {
  { buffer: "", }
}

///|
/// 缓冲区里尚未成帧的字节数。
pub fn Framer::pending(self : Framer) -> Int32 {
  utf8_len(self.buffer)
}

///|
/// 喂入一段文本,返回能确认的事件。
pub fn Framer::feed(self : Framer, chunk : String) -> Array[FrameEvent] {
  self.buffer = self.buffer + chunk
  let cs = chars_of(self.buffer)
  let events : Array[FrameEvent] = []
  let mut cursor = 0
  let mut scanning = true
  while scanning {
    let hdr = find_seq(cs, "##", cursor)
    if hdr < 0 {
      // 末尾可能是半个包头,留一个字符等下一段
      let mut keep = cs.length()
      if keep > cursor && cs[keep - 1] == '#' {
        keep = keep - 1
      }
      if keep > cursor {
        events.push(Noise(slice_chars(cs, cursor, keep)))
      }
      cursor = keep
      scanning = false
    } else {
      if hdr > cursor {
        events.push(Noise(slice_chars(cs, cursor, hdr)))
      }
      if hdr + 6 > cs.length() {
        cursor = hdr
        scanning = false
      } else {
        let len_field = slice_chars(cs, hdr + 2, hdr + 6)
        match parse_uint_all(len_field) {
          None => {
            events.push(Bad(BadLengthField(len_field), len_field))
            cursor = hdr + 1
          }
          Some(declared) => {
            let mut i = hdr + 6
            let mut acc = 0
            let mut complete = true
            while acc < declared {
              if i >= cs.length() {
                complete = false
                break
              }
              acc = acc + utf8_char_len(cs[i])
              i = i + 1
            }
            if !complete {
              cursor = hdr
              scanning = false
            } else if acc != declared {
              events.push(Bad(Truncated(declared, acc), len_field))
              cursor = hdr + 1
            } else if i + 4 > cs.length() {
              // CRC 还没到齐,等下一段
              cursor = hdr
              scanning = false
            } else {
              let crc_end = i + 4
              let mut end = crc_end
              let mut need_more = false
              if crc_end >= cs.length() {
                // CRC 刚够,包尾还没到;TCP 可能正好切在这里
                need_more = true
              } else if cs[crc_end] == '\r' {
                if crc_end + 1 >= cs.length() {
                  // 只有 \r,\n 可能在下一次 read
                  need_more = true
                } else if cs[crc_end + 1] == '\n' {
                  end = crc_end + 2
                } else {
                  end = crc_end + 1
                }
              } else if cs[crc_end] == '\n' {
                end = crc_end + 1
              }
              if need_more {
                cursor = hdr
                scanning = false
              } else {
                let raw = slice_chars(cs, hdr, end)
                match Frame::decode(raw) {
                  Ok(frame) => events.push(Good(frame))
                  Err(e) => events.push(Bad(e, raw))
                }
                if end > cursor {
                  cursor = end
                } else {
                  cursor = hdr + 1
                }
              }
            }
          }
        }
      }
    }
  }
  self.buffer = slice_chars(cs, cursor, cs.length())
  events
}

///|
/// 一次性解码多帧文本,返回全部事件。
pub fn decode_all(text : String) -> Array[FrameEvent] {
  let framer = Framer::new()
  framer.feed(text)
}