///|
/// Option codec: `0x01` followed by the payload for `Some`, a single `0x00`
/// byte for `None`.
pub impl[T : BinarySerializer] BinarySerializer for T? with fn serialize(
  self,
  ctx,
) {
  match self {
    Some(v) => {
      ctx.write_u8(b'\x01')
      v.serialize(ctx)
    }
    None => ctx.write_u8(b'\x00')
  }
}

///|
pub impl[T : BinaryDeserializer] BinaryDeserializer for T? with fn deserialize(
  ctx,
) {
  match ctx.input.read_u8() {
    b'\x01' => Some(T::deserialize(ctx))
    b'\x00' => None
    other =>
      raise DeserializationFailure(
        "Failed to deserialize Option: invalid tag: \{other.to_int()}",
      )
  }
}

///|
/// Result codec (the stand-in for Scala's `Either`): `0x01` + payload for
/// `Ok` (right), `0x00` + payload for `Err` (left).
pub impl[T : BinarySerializer, E : BinarySerializer] BinarySerializer for Result[
  T,
  E,
] with fn serialize(self, ctx) {
  match self {
    Ok(v) => {
      ctx.write_u8(b'\x01')
      v.serialize(ctx)
    }
    Err(e) => {
      ctx.write_u8(b'\x00')
      e.serialize(ctx)
    }
  }
}

///|
pub impl[T : BinaryDeserializer, E : BinaryDeserializer] BinaryDeserializer for Result[
  T,
  E,
] with fn deserialize(ctx) {
  match ctx.input.read_u8() {
    b'\x01' => Ok(T::deserialize(ctx))
    b'\x00' => Err(E::deserialize(ctx))
    other =>
      raise DeserializationFailure(
        "Failed to deserialize Result: invalid tag: \{other.to_int()}",
      )
  }
}

///|
/// Byte-block codec (`Bytes`): `var_u32(length)` followed by the raw bytes.
///
/// This is the dedicated byte-block format used for byte arrays in the Scala
/// (`Chunk[Byte]`) and Rust (`Vec`) libraries — note that it uses an
/// *unsigned* varint, unlike the generic collection encoding.
pub impl BinarySerializer for Bytes with fn serialize(self, ctx) {
  ctx.write_var_u32(self.length().reinterpret_as_uint())
  if self.length() > 0 {
    ctx.write_bytes(self.exact_view())
  }
}

///|
pub impl BinaryDeserializer for Bytes with fn deserialize(ctx) {
  let len = ctx.read_var_u32().reinterpret_as_int()
  if len < 0 {
    // reinterpret_as_int folded a >= 2^31 varint negative; the region
    // fence would otherwise trap instead of failing cleanly.
    raise DeserializationFailure("Byte block length out of range: \{len}")
  }
  ctx.input.read_bytes(len)
}

///|
/// Array codec (the generic counted collection form): `var_i32(length)`
/// followed by the element payloads, no separators.
///
/// The deserializer also accepts the unknown-size form (`var_i32(-1)`, then
/// `0x01`-tagged elements terminated by `0x00`), so arrays can read data
/// written from any iterable, matching the cross-collection compatibility of
/// the original libraries.
pub impl[T : BinarySerializer] BinarySerializer for Array[T] with fn serialize(
  self,
  ctx,
) {
  ctx.write_var_i32(self.length())
  for v in self {
    v.serialize(ctx)
  }
}

///|
/// Specialized counted-form serializer for `Array[Int]`: plain loop over
/// `ctx.write_i32` — wire-identical to the generic path (count prefix +
/// 4-byte BE payloads), minus iterator/generic dispatch overhead.
pub impl BinarySerializer for ArrayOfInt with fn serialize(self, ctx) {
  ctx.write_var_i32(self.values.length())
  for v in self.values {
    ctx.write_i32(v)
  }
}

///|
/// Newtype marking an `Array[Int]` with the specialized codec (MoonBit
/// coherence: a second `BinarySerializer` impl cannot be attached to
/// `Array[Int]` itself).
pub struct ArrayOfInt {
  values : Array[Int32]
}

///|
/// Wrap an `Array[Int]` to use the specialized serializer. Deserialization
/// via the plain `Array[Int]` codec accepts the identical bytes, so callers
/// can keep decoding into a plain array.
pub fn ArrayOfInt::new(values : Array[Int32]) -> ArrayOfInt {
  { values, }
}

///|
pub impl[T : BinaryDeserializer] BinaryDeserializer for Array[T] with fn deserialize(
  ctx,
) {
  ctx.read_flexible_array()
}

///|
/// Deserialize an array accepting both wire forms of the generic collection
/// encoding:
/// - counted: `var_i32(n)` followed by `n` element payloads
/// - unknown-size: `var_i32(-1)`, then `0x01` + element for each item,
///   terminated by `0x00`
pub fn[T : BinaryDeserializer] DeserializationContext::read_flexible_array(
  self : DeserializationContext,
) -> Array[T] raise DesertFailure {
  let count = self.read_var_i32()
  if count >= 0 {
    let arr : Array[T] = []
    for _i in 0.. arr.push(T::deserialize(self))
        b'\x00' => go = false
        other =>
          raise DeserializationFailure(
            "Failed to deserialize collection: invalid element tag: \{other.to_int()}",
          )
      }
    }
    arr
  } else {
    raise DeserializationFailure(
      "Failed to deserialize collection: invalid length: \{count}",
    )
  }
}

///|
/// Serialize a lazy iterator using the unknown-size collection form
/// (`var_i32(-1)`, then `0x01` + element per item, terminated by `0x00`).
///
/// Use this when the number of items is not known upfront (streaming); when
/// the size is known, prefer a concrete collection codec, which is more
/// compact (no per-element tag).
pub fn[T : BinarySerializer] SerializationContext::write_iter_unknown_size(
  self : SerializationContext,
  iter : Iter[T],
) -> Unit raise DesertFailure {
  self.write_var_i32(-1)
  for v in iter {
    self.write_u8(b'\x01')
    v.serialize(self)
  }
  self.write_u8(b'\x00')
}

///|
/// HashMap codec: the generic counted collection encoding applied to
/// key/value pairs, where each pair is written as a 2-tuple (`0x00` version
/// byte, then key, then value).
///
/// Entry order is the hash-map iteration order and is *not* canonicalized —
/// byte-for-byte output is only stable for a single implementation; cross
/// implementation compatibility holds because both readers accept any order.
pub impl[K : BinarySerializer, V : BinarySerializer] BinarySerializer for @hashmap.HashMap[
  K,
  V,
] with fn serialize(self, ctx) {
  ctx.write_var_i32(self.length())
  for kv in self.iter() {
    let (k, v) = kv
    ctx.write_u8(b'\x00')
    k.serialize(ctx)
    v.serialize(ctx)
  }
}

///|
pub impl[K : BinaryDeserializer + Hash + Eq, V : BinaryDeserializer] BinaryDeserializer for @hashmap.HashMap[
  K,
  V,
] with fn deserialize(ctx) {
  let count = ctx.read_var_i32()
  if count < 0 {
    raise DeserializationFailure(
      "Failed to deserialize HashMap: invalid length: \{count}",
    )
  }
  let map : @hashmap.HashMap[K, V] = @hashmap.HashMap([])
  for _i in 0.. ()
      other =>
        raise DeserializationFailure(
          "Failed to deserialize HashMap: invalid entry tag: \{other.to_int()}",
        )
    }
    let k = BinaryDeserializer::deserialize(ctx)
    let v = BinaryDeserializer::deserialize(ctx)
    map.set(k, v)
  }
  map
}

///|
/// Builtin `Map` codec — identical wire format to the `HashMap` codec: the
/// generic counted collection encoding over key/value pairs, each pair
/// written as a 2-tuple. Entry order is not canonicalized.
pub impl[K : BinarySerializer, V : BinarySerializer] BinarySerializer for Map[
  K,
  V,
] with fn serialize(self, ctx) {
  ctx.write_var_i32(self.length())
  for kv in self.iter() {
    let (k, v) = kv
    ctx.write_u8(b'\x00')
    k.serialize(ctx)
    v.serialize(ctx)
  }
}

///|
pub impl[K : BinaryDeserializer + Hash + Eq, V : BinaryDeserializer] BinaryDeserializer for Map[
  K,
  V,
] with fn deserialize(ctx) {
  let count = ctx.read_var_i32()
  if count < 0 {
    raise DeserializationFailure(
      "Failed to deserialize Map: invalid length: \{count}",
    )
  }
  let map : Map[K, V] = Map([])
  for _i in 0.. ()
      other =>
        raise DeserializationFailure(
          "Failed to deserialize Map: invalid entry tag: \{other.to_int()}",
        )
    }
    let k = BinaryDeserializer::deserialize(ctx)
    let v = BinaryDeserializer::deserialize(ctx)
    map.set(k, v)
  }
  map
}

///|
/// HashSet codec — identical wire format to any generic collection: the
/// counted encoding over the elements, accepting both forms on read. Element
/// order is not canonicalized.
pub impl[T : BinarySerializer] BinarySerializer for @hashset.HashSet[T] with fn serialize(
  self,
  ctx,
) {
  ctx.write_var_i32(self.length())
  for v in self.iter() {
    v.serialize(ctx)
  }
}

///|
pub impl[T : BinaryDeserializer + Hash + Eq] BinaryDeserializer for @hashset.HashSet[
  T,
] with fn deserialize(ctx) {
  let elements : Array[T] = ctx.read_flexible_array()
  let set : @hashset.HashSet[T] = @hashset.HashSet([])
  for e in elements {
    set.add(e)
  }
  set
}

///|
pub extend ArrayOfInt with BinarySerializer::{serialize}