///|
/// Result of decoding one SQLite variable-length integer.
pub(all) struct Varint {
  value : UInt64
  length : Int
} derive(Eq, Debug)

///|
/// A bounded cursor over immutable bytes.
///
/// Every read checks the active range before indexing the input. A reader can
/// be forked into a smaller range, which lets page and cell parsers enforce
/// their own boundaries even when the underlying database buffer is larger.
pub(all) struct BinaryReader {
  data : Bytes
  start : Int
  limit : Int
  mut position : Int
  context : String
}

///|
pub fn BinaryReader::new(data : Bytes) -> BinaryReader {
  { data, start: 0, limit: data.length(), position: 0, context: "binary input" }
}

///|
pub fn BinaryReader::named(data : Bytes, context : String) -> BinaryReader {
  { data, start: 0, limit: data.length(), position: 0, context }
}

///|
pub fn BinaryReader::range(
  data : Bytes,
  offset : Int,
  length : Int,
  context : String,
) -> BinaryReader raise ParseError {
  guard offset >= 0 && length >= 0 && offset <= data.length() else {
    raise InvalidRange(offset, length, data.length(), context)
  }
  guard length <= data.length() - offset else {
    raise InvalidRange(offset, length, data.length(), context)
  }
  { data, start: offset, limit: offset + length, position: offset, context }
}

///|
pub fn BinaryReader::absolute_position(self : BinaryReader) -> Int {
  self.position
}

///|
pub fn BinaryReader::relative_position(self : BinaryReader) -> Int {
  self.position - self.start
}

///|
pub fn BinaryReader::length(self : BinaryReader) -> Int {
  self.limit - self.start
}

///|
pub fn BinaryReader::remaining(self : BinaryReader) -> Int {
  self.limit - self.position
}

///|
pub fn BinaryReader::is_empty(self : BinaryReader) -> Bool {
  self.position >= self.limit
}

///|
fn BinaryReader::require(
  self : BinaryReader,
  count : Int,
) -> Unit raise ParseError {
  guard count >= 0 else {
    raise InvalidRange(self.position, count, self.limit, self.context)
  }
  guard count <= self.remaining() else {
    raise UnexpectedEnd(self.position, count, self.remaining(), self.context)
  }
}

///|
pub fn BinaryReader::seek_absolute(
  self : BinaryReader,
  offset : Int,
) -> Unit raise ParseError {
  guard offset >= self.start && offset <= self.limit else {
    raise InvalidRange(offset, 0, self.limit, self.context)
  }
  self.position = offset
}

///|
pub fn BinaryReader::seek_relative(
  self : BinaryReader,
  offset : Int,
) -> Unit raise ParseError {
  self.seek_absolute(self.start + offset)
}

///|
pub fn BinaryReader::skip(
  self : BinaryReader,
  count : Int,
) -> Unit raise ParseError {
  self.require(count)
  self.position += count
}

///|
pub fn BinaryReader::peek_u8(self : BinaryReader) -> Byte raise ParseError {
  self.require(1)
  self.data[self.position]
}

///|
pub fn BinaryReader::read_u8(self : BinaryReader) -> Byte raise ParseError {
  let value = self.peek_u8()
  self.position += 1
  value
}

///|
pub fn BinaryReader::read_u16_be(self : BinaryReader) -> Int raise ParseError {
  self.require(2)
  let a = self.data[self.position].to_int()
  let b = self.data[self.position + 1].to_int()
  self.position += 2
  (a << 8) | b
}

///|
pub fn BinaryReader::read_u24_be(self : BinaryReader) -> Int raise ParseError {
  self.require(3)
  let a = self.data[self.position].to_int()
  let b = self.data[self.position + 1].to_int()
  let c = self.data[self.position + 2].to_int()
  self.position += 3
  (a << 16) | (b << 8) | c
}

///|
pub fn BinaryReader::read_u32_be(
  self : BinaryReader,
) -> UInt64 raise ParseError {
  self.require(4)
  let a = self.data[self.position].to_uint64()
  let b = self.data[self.position + 1].to_uint64()
  let c = self.data[self.position + 2].to_uint64()
  let d = self.data[self.position + 3].to_uint64()
  self.position += 4
  (a << 24) | (b << 16) | (c << 8) | d
}

///|
pub fn BinaryReader::read_u32_le(
  self : BinaryReader,
) -> UInt64 raise ParseError {
  self.require(4)
  let a = self.data[self.position].to_uint64()
  let b = self.data[self.position + 1].to_uint64()
  let c = self.data[self.position + 2].to_uint64()
  let d = self.data[self.position + 3].to_uint64()
  self.position += 4
  a | (b << 8) | (c << 16) | (d << 24)
}

///|
pub fn BinaryReader::read_u64_be(
  self : BinaryReader,
) -> UInt64 raise ParseError {
  self.require(8)
  let mut value = 0UL
  let mut count = 0
  while count < 8 {
    value = (value << 8) | self.read_u8().to_uint64()
    count += 1
  }
  value
}

///|
pub fn BinaryReader::read_i8(self : BinaryReader) -> Int raise ParseError {
  let value = self.read_u8().to_int()
  if value >= 0x80 {
    value - 0x100
  } else {
    value
  }
}

///|
pub fn BinaryReader::read_i16_be(self : BinaryReader) -> Int raise ParseError {
  let value = self.read_u16_be()
  if value >= 0x8000 {
    value - 0x10000
  } else {
    value
  }
}

///|
pub fn BinaryReader::read_i24_be(self : BinaryReader) -> Int raise ParseError {
  let value = self.read_u24_be()
  if value >= 0x800000 {
    value - 0x1000000
  } else {
    value
  }
}

///|
pub fn BinaryReader::read_i32_be(self : BinaryReader) -> Int64 raise ParseError {
  let value = self.read_u32_be()
  if value >= 0x80000000UL {
    value.reinterpret_as_int64() - 0x100000000L
  } else {
    value.reinterpret_as_int64()
  }
}

///|
pub fn BinaryReader::read_i64_be(self : BinaryReader) -> Int64 raise ParseError {
  self.read_u64_be().reinterpret_as_int64()
}

///|
pub fn BinaryReader::read_f64_be(
  self : BinaryReader,
) -> Double raise ParseError {
  self.read_u64_be().reinterpret_as_double()
}

///|
pub fn BinaryReader::read_bytes(
  self : BinaryReader,
  count : Int,
) -> Bytes raise ParseError {
  self.require(count)
  let begin = self.position
  self.position += count
  Bytes::makei(count, fn(index) { self.data[begin + index] })
}

///|
pub fn BinaryReader::read_ascii(
  self : BinaryReader,
  count : Int,
) -> String raise ParseError {
  let bytes = self.read_bytes(count)
  let chars : Array[Char] = []
  for index = 0; index < bytes.length(); index = index + 1 {
    chars.push(bytes[index].to_int().to_char().unwrap())
  }
  String::from_array(chars)
}

///|
pub fn BinaryReader::fork(
  self : BinaryReader,
  count : Int,
  context : String,
) -> BinaryReader raise ParseError {
  self.require(count)
  let child = BinaryReader::range(self.data, self.position, count, context)
  self.position += count
  child
}

///|
/// Decode SQLite's one-to-nine-byte unsigned varint representation.
pub fn BinaryReader::read_varint(
  self : BinaryReader,
) -> Varint raise ParseError {
  let begin = self.position
  let mut value = 0UL
  for index = 0; index < 8; index = index + 1 {
    let byte = self.read_u8().to_int()
    if byte < 0x80 {
      value = (value << 7) | byte.to_uint64()
      return { value, length: self.position - begin }
    }
    value = (value << 7) | (byte & 0x7f).to_uint64()
  }
  let ninth = self.read_u8().to_uint64()
  value = (value << 8) | ninth
  { value, length: 9 }
}

///|
/// Decode a varint without changing the reader's position.
pub fn BinaryReader::peek_varint(
  self : BinaryReader,
) -> Varint raise ParseError {
  let saved = self.position
  let value = self.read_varint()
  self.position = saved
  value
}

///|
/// Read an unsigned big-endian integer using exactly `width` bytes.
pub fn BinaryReader::read_uint_be(
  self : BinaryReader,
  width : Int,
) -> UInt64 raise ParseError {
  guard width >= 0 && width <= 8 else {
    raise InvalidValue(self.position, "integer width", width.to_string())
  }
  self.require(width)
  let mut value = 0UL
  let mut count = 0
  while count < width {
    value = (value << 8) | self.read_u8().to_uint64()
    count += 1
  }
  value
}

///|
/// Read a two's-complement signed big-endian integer using 1..8 bytes.
pub fn BinaryReader::read_int_be(
  self : BinaryReader,
  width : Int,
) -> Int64 raise ParseError {
  guard width >= 1 && width <= 8 else {
    raise InvalidValue(self.position, "signed integer width", width.to_string())
  }
  let value = self.read_uint_be(width)
  if width == 8 {
    return value.reinterpret_as_int64()
  }
  let bits = width * 8
  let sign_bit = 1UL << (bits - 1)
  if (value & sign_bit) == 0UL {
    value.reinterpret_as_int64()
  } else {
    value.reinterpret_as_int64() - (1UL << bits).reinterpret_as_int64()
  }
}

///|
pub fn bytes_equal(left : Bytes, right : Bytes) -> Bool {
  if left.length() != right.length() {
    return false
  }
  for index = 0; index < left.length(); index = index + 1 {
    if left[index] != right[index] {
      return false
    }
  }
  true
}

///|
pub fn bytes_hex(data : Bytes) -> String {
  let digits : Array[Char] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
  ]
  let buffer = StringBuilder::new()
  for index = 0; index < data.length(); index = index + 1 {
    let value = data[index].to_int()
    buffer.write_char(digits[(value >> 4) & 0x0f])
    buffer.write_char(digits[value & 0x0f])
  }
  buffer.to_string()
}