// Copyright 2025 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(all) suberror ReaderError {
  EndOfStream
  InvalidString
  UnknownWireType(UInt)
} derive(Eq, Show)

///|
struct BytesReader {
  data : Bytes
  len : Int
  mut start : Int
} derive(Show)

///|
pub fn BytesReader::from_bytes(data : Bytes) -> BytesReader {
  { data, len: data.length(), start: 0 }
}

///|
pub impl Reader for BytesReader with read(
  self,
  bytes : FixedArray[Byte],
  offset~ : Int,
  max_length~ : Int,
) -> Int? {
  if offset >= self.len || max_length <= 0 || self.start >= self.len {
    return None
  }
  let offset = offset % bytes.length()
  let read_length = @cmp.minimum(
    @cmp.minimum(bytes.length() - offset, max_length),
    self.len - self.start,
  )
  bytes.blit_from_bytes(offset, self.data, self.start, read_length)
  self.start += read_length
  Some(read_length)
}

///|
pub impl AsyncReader for BytesReader with read(
  self,
  bytes : FixedArray[Byte],
  offset~ : Int,
  max_length~ : Int,
) -> Int? raise {
  if offset >= bytes.length() || max_length <= 0 || self.start >= self.len {
    return None
  }
  let read_length = @cmp.minimum(
    @cmp.minimum(bytes.length() - offset, max_length),
    self.len - self.start,
  )
  bytes.blit_from_bytes(offset, self.data, self.start, read_length)
  self.start += read_length
  Some(read_length)
}

///|
pub(all) struct LimitedReader[T] {
  reader : T
  mut limit : Int?
}

///|
pub fn[T] LimitedReader::new(reader : T, limit? : Int) -> LimitedReader[T] {
  { reader, limit }
}

///|
pub impl[T : Reader] Reader for LimitedReader[T] with read(
  self,
  bytes : FixedArray[Byte],
  offset~ : Int,
  max_length~ : Int,
) -> Int? {
  if self.limit is Some(0) {
    return None
  }
  let max_length = if self.limit is Some(limit) {
    @cmp.minimum(limit, max_length)
  } else {
    max_length
  }
  let read_length = self.reader.read(bytes, offset~, max_length~)
  if read_length is Some(length) && self.limit is Some(limit) {
    self.limit = Some(limit - length)
  }
  read_length
}

///|
pub impl[T : AsyncReader] AsyncReader for LimitedReader[T] with read(
  self,
  bytes : FixedArray[Byte],
  offset~ : Int,
  max_length~ : Int,
) -> Int? raise {
  if self.limit is Some(0) {
    return None
  }
  let max_length = if self.limit is Some(limit) {
    @cmp.minimum(limit, max_length)
  } else {
    max_length
  }
  let read_length = self.reader.read(bytes, offset~, max_length~)
  if read_length is Some(length) && self.limit is Some(limit) {
    self.limit = Some(limit - length)
  }
  read_length
}