// 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.

///|
pub(open) trait Reader {
  fn _get_internal_buffer(Self) -> ReaderBuffer
  fn _direct_read(Self, FixedArray[Byte], offset~ : Int, max_len~ : Int) -> Int raise
  fn read(Self, FixedArray[Byte], offset? : Int, max_len? : Int) -> Int raise = _
  fn drop(Self, Int) -> Int raise = _
  fn read_exactly(Self, len : Int) -> Bytes raise = _
  fn read_some(Self, max_len? : Int) -> Bytes? raise = _
  fn read_all(Self) -> &Data raise = _
  fn read_until(Self, StringView) -> String? raise = _
}

///|
pub(all) suberror ReaderClosed derive(Debug, ToJson)

///|
impl Reader with fn read(
  self,
  dst,
  offset? = 0,
  max_len? = dst.length() - offset,
) {
  let buf = self._get_internal_buffer()
  if buf.len() > 0 {
    let n = @cmp.minimum(max_len, buf.len())
    buf.buf().blit_to(dst, src_offset=buf.start(), dst_offset=offset, len=n)
    buf.drop(n)
    n
  } else {
    self._direct_read(dst, offset~, max_len~)
  }
}

///|
impl Reader with fn drop(self, len) {
  let buf = self._get_internal_buffer()
  if buf.len() >= len {
    buf.drop(len)
    len
  } else {
    let buffered = buf.len()
    buf.drop(buffered)
    buf.enlarge_to(1)
    for dropped = buffered; dropped < len; {
      let received = self._direct_read(
        buf.buf(),
        offset=0,
        max_len=buf.buf().length(),
      )
      buf.set_len(received)
      if received == 0 {
        return dropped
      }
      continue dropped + received
    } nobreak {
      buf.set_start(buf.len() - (dropped - len))
      buf.set_len(dropped - len)
      len
    }
  }
}

///|
impl Reader with fn read_exactly(self, len) {
  let buf = FixedArray::make(len, b'\x00')
  for received = 0; received < len; {
    let new_received = self.read(buf, offset=received, max_len=len - received)
    if new_received == 0 {
      raise ReaderClosed
    }
    continue received + new_received
  }
  buf.unsafe_reinterpret_as_bytes()
}

///|
impl Reader with fn read_some(self, max_len?) {
  let buf = self._get_internal_buffer()
  if buf.len() > 0 {
    let buf_bytes = buf.buf().unsafe_reinterpret_as_bytes()
    if max_len is Some(max_len) && max_len < buf.len() {
      let data = buf_bytes[buf.start():buf.start() + max_len].to_owned()
      buf.set_start(buf.start() + max_len)
      buf.set_len(buf.len() - max_len)
      return Some(data)
    } else {
      let data = buf_bytes[buf.start():buf.start() + buf.len()].to_owned()
      buf.clear()
      return Some(data)
    }
  }
  buf.enlarge_to(if max_len is Some(max_len) { max_len } else { 1 })
  let n = self._direct_read(buf.buf(), offset=0, max_len=buf.buf().length())
  if n == 0 {
    return None
  }
  if max_len is Some(max_len) && max_len < n {
    buf.set_start(max_len)
    buf.set_len(n - max_len)
    Some(buf.buf().unsafe_reinterpret_as_bytes()[:max_len].to_owned())
  } else {
    let data = buf.buf().unsafe_reinterpret_as_bytes()[:n].to_owned()
    buf.clear()
    Some(data)
  }
}

///|
impl Reader with fn read_all(self) {
  let buffer_list = []
  let mut buffer = FixedArray::make(1024, b'\x00')
  let mut offset = 0
  while self.read(buffer, offset~, max_len=buffer.length() - offset) is n &&
        n > 0 {
    offset += n
    if offset == buffer.length() {
      buffer_list.push(buffer)
      buffer = FixedArray::make(1024, b'\x00')
      offset = 0
    }
  }
  let total_size = buffer_list.length() * 1024 + offset
  let result = FixedArray::make(total_size, b'\x00')
  for i, buf in buffer_list {
    result.unsafe_blit(i * 1024, buf, 0, 1024)
  }
  result.unsafe_blit(buffer_list.length() * 1024, buffer, 0, offset)
  result.unsafe_reinterpret_as_bytes()
}

///|
impl Reader with fn read_until(self, sep) {
  let sep = @utf8.encode(sep)
  let buf = self._get_internal_buffer()
  match buf.find_opt(sep, reader=self) {
    None if buf.len() > 0 => {
      let buf_bytes = buf.buf().unsafe_reinterpret_as_bytes()
      let remaining = @utf8.decode(
        buf_bytes[buf.start():buf.start() + buf.len()],
      )
      buf.drop(buf.len())
      Some(remaining)
    }
    None => None
    Some(index) => {
      let buf_bytes = buf.buf().unsafe_reinterpret_as_bytes()
      let result = @utf8.decode(buf_bytes[buf.start():buf.start() + index])
      buf.drop(index + sep.length())
      Some(result)
    }
  }
}