// High-level Protobuf message decoder.

///|
struct Decoder {
  bytes : Bytes
  mut pos : Int
}

///|
/// Create a new Decoder from bytes.
pub fn Decoder::new(bytes : Bytes) -> Decoder {
  { bytes, pos: 0 }
}

///|
/// Read the next tag. Returns (field_number, wire_type) or None at end.
pub fn Decoder::read_tag(self : Decoder) -> (UInt, UInt)? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      let (field_number, wire_type) = split_tag(value.to_uint())
      Some((field_number, wire_type))
    }
  }
}

///|
/// Read an int32 value (wire type 0).
pub fn Decoder::read_int32(self : Decoder) -> Int? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(value.to_int())
    }
  }
}

///|
/// Read an int64 value (wire type 0).
pub fn Decoder::read_int64(self : Decoder) -> Int64? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(value.reinterpret_as_int64())
    }
  }
}

///|
/// Read a uint32 value (wire type 0).
pub fn Decoder::read_uint32(self : Decoder) -> UInt? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(value.to_uint())
    }
  }
}

///|
/// Read a uint64 value (wire type 0).
pub fn Decoder::read_uint64(self : Decoder) -> UInt64? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(value)
    }
  }
}

///|
/// Read a sint32 value (wire type 0, zigzag).
pub fn Decoder::read_sint32(self : Decoder) -> Int? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(decode_zigzag32(value.to_uint()))
    }
  }
}

///|
/// Read a sint64 value (wire type 0, zigzag).
pub fn Decoder::read_sint64(self : Decoder) -> Int64? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(decode_zigzag64(value))
    }
  }
}

///|
/// Read a bool value (wire type 0).
pub fn Decoder::read_bool(self : Decoder) -> Bool? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      Some(value != 0)
    }
  }
}

///|
/// Read a fixed32 value (wire type 5).
pub fn Decoder::read_fixed32(self : Decoder) -> UInt? {
  let result = read_fixed32(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(value)
    }
  }
}

///|
/// Read an sfixed32 value (wire type 5).
pub fn Decoder::read_sfixed32(self : Decoder) -> Int? {
  let result = read_fixed32(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(value.reinterpret_as_int())
    }
  }
}

///|
/// Read a float value (wire type 5).
pub fn Decoder::read_float(self : Decoder) -> Float? {
  let result = read_fixed32(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(Float::reinterpret_from_uint(value))
    }
  }
}

///|
/// Read a fixed64 value (wire type 1).
pub fn Decoder::read_fixed64(self : Decoder) -> UInt64? {
  let result = read_fixed64(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(value)
    }
  }
}

///|
/// Read an sfixed64 value (wire type 1).
pub fn Decoder::read_sfixed64(self : Decoder) -> Int64? {
  let result = read_fixed64(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(value.reinterpret_as_int64())
    }
  }
}

///|
/// Read a double value (wire type 1).
pub fn Decoder::read_double(self : Decoder) -> Double? {
  let result = read_fixed64(self.bytes, self.pos)
  match result {
    None => None
    Some((value, new_pos)) => {
      self.pos = new_pos
      Some(value.reinterpret_as_double())
    }
  }
}

///|
/// Read a string value (wire type 2).
pub fn Decoder::read_string(self : Decoder) -> String? {
  let len = self.read_length_delim_len()
  match len {
    None => None
    Some(length) => {
      if self.pos + length > self.bytes.length() {
        return None
      }
      let s = @utf8.decode_lossy(self.bytes[self.pos:self.pos + length])
      self.pos += length
      Some(s)
    }
  }
}

///|
/// Read a bytes value (wire type 2).
pub fn Decoder::read_bytes(self : Decoder) -> Bytes? {
  let len = self.read_length_delim_len()
  match len {
    None => None
    Some(length) => {
      if self.pos + length > self.bytes.length() {
        return None
      }
      let b = self.bytes[self.pos:self.pos + length].to_owned()
      self.pos += length
      Some(b)
    }
  }
}

///|
/// Read a nested message (wire type 2). Returns a new Decoder for the nested bytes.
pub fn Decoder::read_message(self : Decoder) -> Decoder? {
  let len = self.read_length_delim_len()
  match len {
    None => None
    Some(length) => {
      if self.pos + length > self.bytes.length() {
        return None
      }
      let nested_bytes = self.bytes[self.pos:self.pos + length].to_owned()
      self.pos += length
      Some(Decoder::new(nested_bytes))
    }
  }
}

///|
/// Skip a field of the given wire type. Returns bytes consumed or None on error.
pub fn Decoder::skip_field(self : Decoder, wire_type : UInt) -> Int? {
  match wire_type {
    WIRE_VARINT => {
      let result = decode_varint(self.bytes, self.pos)
      match result {
        None => None
        Some((_value, consumed)) => {
          self.pos += consumed
          Some(consumed)
        }
      }
    }
    WIRE_FIXED64 => {
      if self.pos < 0 || self.pos + 8 > self.bytes.length() {
        return None
      }
      self.pos += 8
      Some(8)
    }
    WIRE_LENGTH_DELIMITED => {
      let len = self.read_length_delim_len()
      match len {
        None => None
        Some(length) => {
          if self.pos + length > self.bytes.length() {
            return None
          }
          self.pos += length
          Some(length)
        }
      }
    }
    WIRE_FIXED32 => {
      if self.pos < 0 || self.pos + 4 > self.bytes.length() {
        return None
      }
      self.pos += 4
      Some(4)
    }
    _ => None
  }
}

///|
/// Internal: read the length prefix of a length-delimited field.
fn Decoder::read_length_delim_len(self : Decoder) -> Int? {
  let result = decode_varint(self.bytes, self.pos)
  match result {
    None => None
    Some((value, consumed)) => {
      self.pos += consumed
      let length = value.to_int()
      if length < 0 {
        return None
      }
      Some(length)
    }
  }
}