// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
struct BinaryReaderController {
  mut buf : FixedArray[Byte]
  mut buf_start : Int32
  mut buf_len : Int32
}

///|
const BUFFER_PREFIX_SIZE = 8

///|
/// Create a new binary reader controller
pub fn BinaryReaderController::BinaryReaderController() -> BinaryReaderController {
  {
    buf: FixedArray::make(BUFFER_PREFIX_SIZE, b'0'),
    buf_start: BUFFER_PREFIX_SIZE,
    buf_len: 0,
  }
}

///|
#doc(hidden)
#internal(internal, "for internal use only")
pub fn BinaryReaderController::buffered(
  self : BinaryReaderController,
) -> BytesView {
  let end = self.buf_start + self.buf_len
  self.buf.unsafe_reinterpret_as_bytes()[self.buf_start:end]
}

///|
/// Return the underlying mutable allocation for internal zero-copy adapters.
/// Valid data starts at `buffered().start_offset()` and spans
/// `buffered().length()` bytes; callers must not assume it starts at zero.
#doc(hidden)
#internal(internal, "for internal use only")
pub fn BinaryReaderController::raw_buffer(
  self : BinaryReaderController,
) -> FixedArray[Byte] {
  self.buf
}

///|
#doc(hidden)
#internal(internal, "for internal use only")
pub fn BinaryReaderController::clear(self : BinaryReaderController) -> Unit {
  self.buf = FixedArray::make(BUFFER_PREFIX_SIZE, b'0')
  self.buf_start = BUFFER_PREFIX_SIZE
  self.buf_len = 0
}

///|
const SEGMENT_SIZE = 1024

///|
fn BinaryReaderController::enlarge_to(
  self : BinaryReaderController,
  n : Int32,
) -> Unit {
  // Keep aligned prefix padding outside every exposed view. This prevents
  // `BytesView::to_owned()` from reusing the mutable reader allocation.
  let capacity = self.buf.length()
  if n <= capacity - self.buf_start {
    return
  }
  if n <= capacity - BUFFER_PREFIX_SIZE {
    self.buf.blit_to(
      self.buf,
      src_offset=self.buf_start,
      dst_offset=BUFFER_PREFIX_SIZE,
      len=self.buf_len,
    )
    self.buf_start = BUFFER_PREFIX_SIZE
    return
  }
  let required_capacity = n + BUFFER_PREFIX_SIZE
  let padding = required_capacity % SEGMENT_SIZE
  let target_len = if padding == 0 {
    required_capacity
  } else {
    required_capacity - padding + SEGMENT_SIZE
  }
  let new_buf = FixedArray::make(target_len, b'0')
  self.buf.blit_to(
    new_buf,
    src_offset=self.buf_start,
    dst_offset=BUFFER_PREFIX_SIZE,
    len=self.buf_len,
  )
  self.buf = new_buf
  self.buf_start = BUFFER_PREFIX_SIZE
}

///|
#doc(hidden)
pub fn BinaryReaderController::drop(
  self : BinaryReaderController,
  n : Int32,
) -> Unit {
  guard! n <= self.buf_len
  self.buf_len -= n
  if self.buf_len == 0 {
    self.buf_start = BUFFER_PREFIX_SIZE
  } else {
    self.buf_start += n
  }
}

///|
fn BinaryReaderController::blit_to(
  self : BinaryReaderController,
  dst : FixedArray[Byte],
  offset~ : Int32,
  max_len~ : Int32,
) -> Int32 {
  guard self.buf_len > 0 else { return 0 }
  let n = @cmp.minimum(self.buf_len, max_len)
  self.buf.blit_to(dst, src_offset=self.buf_start, dst_offset=offset, len=n)
  self.drop(n)
  n
}