// 流式扫描:分块喂字节,边喂边出 tag。
//
// 为什么要有这个:整份扫描(`scan_tags`)要把整个文件读进内存。
// 流式不保留完整文件,但会保留当前块、未完成行及扫描警告。
// 超长行和大量警告仍可能使内存随输入增长。
//
// 正确性的地基是一个巧合:**编码判断只看文件前 1024 字节**
//(`decide_encoding` 的实现范围)。所以流式这边
// 攒够前 1024 字节再定编码、再往外吐 tag,和整份扫描对同一个文件的
// 判断必然一致。语料上逐 tag 对过账(`corpus_wbtest` 里那条等价性测试)。
//
// `scan_tags` 自己也改成了这层的封装:一处状态机、两个入口,
// 行为不会有第二套。

///|
/// 流式扫描器:`feed` 分块喂入,`finish` 收尾。
///
/// tag 是「组码一行、值一行」的配对,配对可能横跨块边界——内部
/// 缓冲没读完的行,`feed` 之间不用调用方操心。
pub(all) struct TagStream {
  /// 没消费完的字节。定编码之前,这是攒着的文件头部前缀
  priv mut buffer : Bytes
  priv mut line_no : Int
  /// 上一行读到的组码(tag 成对,读到组码就得等它的值)
  priv mut pending : Int?
  priv warn : Array[String]
  /// 文件编码。**定下来之前不往外吐任何 tag**——值怎么解码全靠它
  priv mut enc : Encoding?
  priv mut finished : Bool
  /// 碰到 `0 / EOF` 之后一律不看了(EOF 后面可能是垃圾字节)
  priv mut eof_seen : Bool
}

///|
pub fn TagStream::new() -> TagStream {
  let empty : Array[Byte] = []
  {
    buffer: Bytes::from_array(empty),
    line_no: 0,
    pending: None,
    warn: [],
    enc: None,
    finished: false,
    eof_seen: false,
  }
}

///|
/// 分块喂入,返回这次新扫出来的 tag。
///
/// 头部攒够 1024 字节之前可能一个都不吐(在等编码判断)——
/// 这是设计,不是卡住了;`finish` 会把不足 1024 字节的文件收尾。
pub fn TagStream::feed(self : TagStream, chunk : Bytes) -> Array[Tag] {
  let out : Array[Tag] = []
  if self.finished || self.eof_seen {
    return out
  }
  self.buffer = concat_bytes(self.buffer, chunk)
  if self.enc is None {
    if self.buffer.length() < 1024 {
      return out
    }
    self.choose_encoding()
  }
  self.consume_lines(out)
  out
}

///|
/// 收尾:把缓冲里剩下的消费掉(最后一行没有换行符也算一整行),
/// 报出「组码后面没有值」这类截断警告。之后 `feed` 再喂什么都返回空。
pub fn TagStream::finish(self : TagStream) -> Array[Tag] {
  let out : Array[Tag] = []
  if self.finished {
    return out
  }
  self.finished = true
  if self.eof_seen {
    return out
  }
  if self.enc is None {
    // 不足 1024 字节的小文件:用现有的全部判断——和整份扫描对
    // 短文件的行为一致(decide_encoding 自己会截到 1024)
    self.choose_encoding()
  }
  self.consume_lines(out)
  match self.pending {
    Some(code) =>
      self.warn.push(
        "文件在最后一行截断了:组码 \{code} 后面没有值(可能是文件被切断了)",
      )
    None => ()
  }
  out
}

///|
/// 扫描中攒下的警告(和 `TagScan.warnings` 同源)。
pub fn TagStream::warnings(self : TagStream) -> Array[String] {
  self.warn.copy()
}

///|
/// 文件编码;还没定(还没攒够 1024 字节)时是 `None`。
pub fn TagStream::encoding(self : TagStream) -> Encoding? {
  self.enc
}

///|
fn TagStream::choose_encoding(self : TagStream) -> Unit {
  let enc = decide_encoding(self.buffer)
  self.enc = Some(enc)
  if enc.is_fallback() {
    self.warn.push(
      "未支持的\{enc.to_display()}按Latin-1降级解码;文字可能失真",
    )
  }
}

///|
/// 消费缓冲里所有**能确定边界**的行,产出 tag。
///
/// 一行算「确定」:看到了换行符——`\r\n` 要看得到下一个字节,
/// 不然不知道 `\r` 后面跟不跟 `\n`;或者正在收尾(缓冲末尾即文件末尾)。
fn TagStream::consume_lines(self : TagStream, out : Array[Tag]) -> Unit {
  let data = self.buffer
  let n = data.length()
  let mut pos = 0
  let mut done = false
  while pos < n && !done {
    let e = line_end(data, pos)
    if e >= n && !self.finished {
      // 行还没写完——留着等下一块
      break
    }
    if e < n && data[e] == b'\r' && e + 1 >= n && !self.finished {
      // `\r` 是块里的最后一个字节:可能是 `\r\n` 的前半——等下一块
      break
    }
    let start = pos
    pos = advance_past_newline(data, e)
    self.line_no = self.line_no + 1
    match self.pending {
      None =>
        match parse_group_code(data, start, e) {
          Some(code) => self.pending = Some(code)
          None =>
            if line_is_blank(data, start, e) {
              () // 空行是正常现象,不报警告
            } else {
              self.warn.push(
                "第 \{self.line_no} 行:这一行应该是组码,但读不出整数(" +
                "内容:\{preview(data, start, e)}),已跳过",
              )
            }
        }
      Some(code) => {
        let value = match parse_value(code, data, start, e, self.enc.unwrap()) {
          Ok(value) => value
          Err(msg) => {
            self.warn.push(
              "第\{self.line_no}行组码\{code}:\{msg};保留原文,几何结果需复核",
            )
            Str(decode_text(slice_range(data, start, e), self.enc.unwrap()))
          }
        }
        out.push(Tag::new(code, value))
        self.pending = None
        // `0 / EOF` 是文件结束标志,后面的都不看
        if code == 0 && value.as_string() == "EOF" {
          self.eof_seen = true
          done = true
        }
      }
    }
  }
  // 丢掉消费过的部分;EOF 之后剩下的(垃圾字节)也一并丢掉
  if done {
    let empty : Array[Byte] = []
    self.buffer = Bytes::from_array(empty)
  } else if pos > 0 {
    self.buffer = slice_range(data, pos, n)
  }
}

///|
/// 拼两块字节。
fn concat_bytes(a : Bytes, b : Bytes) -> Bytes {
  if a.length() == 0 {
    return b
  }
  if b.length() == 0 {
    return a
  }
  let out : Array[Byte] = []
  for i = 0; i < a.length(); i = i + 1 {
    out.push(a[i])
  }
  for i = 0; i < b.length(); i = i + 1 {
    out.push(b[i])
  }
  Bytes::from_array(out)
}