// 响应体的分帧读取:把「连接上的字节」按 `BodyFraming` 切成「响应体的字节」。
//
// 它实现 `@io.Reader`,所以可以:交给 `ResponseBody::open_wire` 当流,
// 也可以被 `@gzip.Decoder` 再包一层(流式 gzip 解压,gzip 语义由本包自持,
// 见 docs/15 / docs/17 口径 4)。分帧判定的纯逻辑在 httpproto,这里只做
// I/O 驱动——「怎么判」可以同步测试,「怎么读」跟着连接走。

///|
/// 当前分帧的读取状态。`Length` 的载荷是**还剩多少字节**;
/// `Done` 单列是为了让「读满定长」「chunked 收尾」与 Empty 一样
/// 从此稳定返回 EOF。
priv enum BodyMode {
  /// 无体(HEAD / 1xx / 204 / 304):永远 EOF
  Empty
  /// 定长:读满即 EOF
  Length(Int32)
  /// chunked:状态机在 httpproto,这里喂字节、收数据段
  Chunked(@httpproto.ChunkDecoder)
  /// 读到连接关闭为止
  UntilClose
  /// 已经读完(定长读满 / chunked 正常收尾)
  Done
}

///|
/// 分帧响应体。持有连接(所有权在 `send` 里一次性移交:
/// 连接的关闭由 `ResponseBody` 的 dispose 回调驱动,见 transport.mbt)。
priv struct BodyReader {
  conn : Connection
  /// 自己的输出缓冲:默认 read / read_some / read_until 先看它。
  /// 刻意**不**与连接共用 `conn.buf`——读响应头的「多读」会把原始线字节
  /// (甚至 chunked 帧头)留进连接缓冲,而默认方法「缓冲有字节就直接交出、
  /// 不再过 `_direct_read`」,从这里交出就会把帧原样漏进响应体。
  /// 本缓冲里只会出现 `_direct_read` 产出的**已解码**字节。
  buf : @io.ReaderBuffer
  mut mode : BodyMode
  /// chunked 模式的线上字节暂存:feed 之前先从连接拉一拍
  scratch : FixedArray[Byte]
  /// chunked 数据段超出单次读取容量的余量(FIFO);`pending_off` 是
  /// 队首段的已消费位置。不设它,一次拉取解出超过 `max_len` 的数据
  /// 就会丢字节。
  mut pending : Array[Bytes]
  mut pending_off : Int32
  /// 定长体被对端截断(还没读满就遇到 EOF)。EOF 的交出语义不变
  /// (截断的正文留给上层按内容判断),但连接已经死了——池凭它区分
  /// 「读满」与「被断」,截断的连接绝不回池(docs/17 口径 4)。
  mut truncated : Bool
  /// 响应头层面就判定的「服务端允许复用」:分帧不是 UntilClose、
  /// 请求方没声明 close、`connection_reusable` 通过(见 build_raw_response)。
  /// 与读取状态分开存,`is_reusable` 是两者的合取。
  server_allows : Bool
}

///|
/// 线上字节的一次拉取粒度。chunked 的数据段切分是逐字节的,这个缓冲
/// 只影响「每次从内核拿多少」,不影响正确性。
const BODY_PULL_SIZE : Int32 = 16384

///|
fn BodyReader::open(
  conn : Connection,
  framing : @httpproto.BodyFraming,
  server_allows~ : Bool,
) -> BodyReader {
  let mode = match framing {
    @httpproto.BodyFraming::Empty => Empty
    @httpproto.BodyFraming::Length(total) => Length(total)
    @httpproto.BodyFraming::Chunked => Chunked(@httpproto.ChunkDecoder::new())
    @httpproto.BodyFraming::UntilClose => UntilClose
  }
  {
    conn,
    buf: @io.ReaderBuffer::new(),
    mode,
    scratch: FixedArray::make(BODY_PULL_SIZE, b'\x00'),
    pending: [],
    pending_off: 0,
    truncated: false,
    server_allows,
  }
}

///|
/// 这条连接在读完后能不能回池(docs/17 口径 4:体未消费完不回池)。
/// 三个条件缺一不可:响应头层面服务端允许(含分帧不是 UntilClose——
/// UntilClose 的体以关闭收尾,模式永远到不了 Done)、定长体真读满了
/// (不是被截断)、分帧状态机正常收尾。chunked 被截断会直接报
/// Malformed 走失败路径,到不了这里。
fn BodyReader::is_reusable(self : BodyReader) -> Bool {
  self.server_allows && !self.truncated && self.mode is (Done | Empty)
}

///|
/// 输出侧用自己的缓冲(见结构体字段注释):这里交出的字节永远已经过
/// 分帧解码;连接自己的缓冲只是线上字节的中转,经 `conn.read` 进 `_direct_read`。
impl @io.Reader for BodyReader with fn _get_internal_buffer(self : BodyReader) -> @io.ReaderBuffer {
  self.buf
}

///|
/// 按分帧模式读出**解码后**(去 chunked 帧)的字节;0 = 响应体结束。
///
/// 两个口径:
/// - 定长不足(连接提前关闭):按 EOF 交出已到的部分——截断的正文留给
///   上层按内容判断,不在这里改写语义;
/// - chunked 截断:`ChunkDecoder::finish` 会显式报 `Malformed`
///   ——半截的 chunked 无法自洽,宽容等于交出坏数据。
impl @io.Reader for BodyReader with fn _direct_read(
  self : BodyReader,
  dst : FixedArray[Byte],
  offset~ : Int32,
  max_len~ : Int32,
) -> Int32 {
  // 余量优先:上次解出没装下的数据段照常交出(哪怕分帧已收尾)
  let mut filled = self.drain_pending(dst, offset~, max_len~)
  if filled > 0 {
    return filled
  }
  match self.mode {
    Empty => 0
    Done => 0
    UntilClose => self.conn.read(dst, offset~, max_len~)
    Length(remaining) => {
      let take = if max_len < remaining { max_len } else { remaining }
      let n = self.conn.read(dst, offset~, max_len=take)
      if n == 0 {
        // 还没读满就 EOF:对端截断。照旧按 EOF 交出已到的部分,
        // 但记下「连接已死」——is_reusable 靠它拒绝回池。
        self.truncated = true
        self.mode = Done
        return 0
      }
      if n >= remaining {
        self.mode = Done
      } else {
        self.mode = Length(remaining - n)
      }
      n
    }
    Chunked(decoder) => {
      while filled < max_len {
        let got = self.conn.read(self.scratch, offset=0, max_len=BODY_PULL_SIZE)
        if got == 0 {
          // 连接关闭:正常收尾则 Done,否则「被截断」显式失败
          decoder.finish()
          self.mode = Done
          return filled
        }
        let pieces = decoder.feed(Bytes::from_array(self.scratch[0:got]))
        for piece in pieces {
          filled += self.stash(dst, offset~, max_len~, piece, filled)
        }
        if decoder.is_done() {
          self.mode = Done
          // 终止 chunk 之后解出的尾段可能还压在 pending 里
          return filled +
            self.drain_pending(
              dst,
              offset=offset + filled,
              max_len=max_len - filled,
            )
        }
        // 一有解码产出就返回:`read_some` 的契约是「数据一到尽快交出」,
        // 等满 max_len 会把「同一次拉取之后才到的数据」也堵在连接上——
        // SSE 这类边到边读协议的下一个事件就卡在了一次读取里。
        // 循环只为「这批线上字节全是帧头、还没解出载荷」的情形继续。
        if filled > 0 {
          return filled
        }
      }
      filled
    }
  }
}

///|
/// 把一个数据段尽量装进目标缓冲,装不下的整个压入 pending。
/// 返回实际装入的字节数。
fn BodyReader::stash(
  self : BodyReader,
  dst : FixedArray[Byte],
  offset~ : Int32,
  max_len~ : Int32,
  piece : Bytes,
  filled : Int32,
) -> Int32 {
  let mut i = 0
  while i < piece.length() && filled + i < max_len {
    dst[offset + filled + i] = piece[i]
    i += 1
  }
  if i < piece.length() {
    // 余量切片是「从 i 开始的新字节」,队首消费位置从 0 起算
    self.pending.push(piece[i:].to_owned())
    if self.pending.length() == 1 {
      self.pending_off = 0
    }
  }
  i
}

///|
/// 清 pending:把上次剩下的数据段按序拷进目标缓冲。
fn BodyReader::drain_pending(
  self : BodyReader,
  dst : FixedArray[Byte],
  offset~ : Int32,
  max_len~ : Int32,
) -> Int32 {
  let mut filled = 0
  while filled < max_len && self.pending.length() > 0 {
    let piece = self.pending[0]
    let mut i = self.pending_off
    while i < piece.length() && filled < max_len {
      dst[offset + filled] = piece[i]
      i += 1
      filled += 1
    }
    if i >= piece.length() {
      self.pending = array_tail(self.pending)
      self.pending_off = 0
    } else {
      self.pending_off = i
    }
  }
  filled
}

///|
/// 去掉数组首元素的浅拷贝( pending 队列的出队;数组很小,不值得原地搬)。
fn array_tail(items : Array[Bytes]) -> Array[Bytes] {
  let rest = []
  for i in 1..