///|
/// Returns the content octets of this value, requiring a primitive universal
/// value with the given tag number.
fn expect_content(value : Der, number : Int) -> BytesView raise DerError {
  match value {
    Primitive(tag, content) => {
      if tag.cls != Universal || tag.number != number {
        raise ExpectedTag(
          Tag::new(Universal, number).to_string(),
          tag.to_string(),
        )
      }
      content
    }
    Constructed(tag, _) =>
      raise InvalidValue(
        "expected " +
        Tag::new(Universal, number).to_string() +
        ", got " +
        tag.to_string(),
      )
  }
}

///|
/// Decodes this value as a BOOLEAN.
pub fn Der::as_boolean(self : Der) -> Bool raise DerError {
  decode_boolean(expect_content(self, TAG_BOOLEAN))
}

///|
/// Decodes this value as an INTEGER.
pub fn Der::as_integer(self : Der) -> @bigint.BigInt raise DerError {
  decode_integer(expect_content(self, TAG_INTEGER))
}

///|
/// Checks that this value is a NULL.
pub fn Der::as_null(self : Der) -> Unit raise DerError {
  decode_null(expect_content(self, TAG_NULL))
}

///|
/// Decodes this value as an OBJECT IDENTIFIER.
pub fn Der::as_object_identifier(self : Der) -> ObjectIdentifier raise DerError {
  decode_object_identifier(expect_content(self, TAG_OBJECT_IDENTIFIER))
}

///|
/// Returns a view of this value's OCTET STRING content.
pub fn Der::as_octet_string_view(self : Der) -> BytesView raise DerError {
  expect_content(self, TAG_OCTET_STRING)
}

///|
/// Copies this value's OCTET STRING content into a fresh buffer.
pub fn Der::as_octet_string(self : Der) -> Bytes raise DerError {
  @buffer.from_bytes(self.as_octet_string_view()).to_bytes()
}

///|
/// Decodes this value as a BIT STRING.
pub fn Der::as_bit_string(self : Der) -> (Int, Bytes) raise DerError {
  decode_bit_string(expect_content(self, TAG_BIT_STRING))
}

///|
/// Decodes this value as a UTF8String.
pub fn Der::as_utf8_string(self : Der) -> String raise DerError {
  decode_utf8_string(expect_content(self, TAG_UTF8_STRING))
}

///|
/// Decodes this value as a time: `UTCTime` or `GeneralizedTime`.
pub fn Der::as_time(self : Der) -> Asn1Time raise DerError {
  match self {
    Primitive(tag, content) =>
      match tag.number {
        TAG_UTC_TIME => decode_utc_time(content)
        TAG_GENERALIZED_TIME => decode_generalized_time(content)
        _ =>
          raise InvalidValue(
            "expected UTCTime or GeneralizedTime, got " + tag.to_string(),
          )
      }
    Constructed(tag, _) =>
      raise InvalidValue("expected a time, got " + tag.to_string())
  }
}

///|
/// Decodes this value as a string, dispatching on the universal tag number.
///
/// `TeletexString` and `T61String` are not decoded; their content is usually
/// Latin-1, so callers should read it as bytes and convert explicitly.
pub fn Der::as_string(self : Der) -> String raise DerError {
  match self {
    Primitive(tag, content) =>
      match tag.number {
        TAG_UTF8_STRING => decode_utf8_string(content)
        TAG_PRINTABLE_STRING => decode_printable_string(content)
        TAG_IA5_STRING => decode_ia5_string(content)
        TAG_NUMERIC_STRING => decode_numeric_string(content)
        TAG_VISIBLE_STRING => decode_visible_string(content)
        TAG_BMP_STRING => decode_bmp_string(content)
        TAG_UNIVERSAL_STRING => decode_universal_string(content)
        _ =>
          raise InvalidValue("expected a string type, got " + tag.to_string())
      }
    Constructed(tag, _) =>
      raise InvalidValue("expected a string, got " + tag.to_string())
  }
}

///|
/// Returns the children of this value, accepting SEQUENCE and SET.
pub fn Der::as_sequence(self : Der) -> Array[Der] raise DerError {
  match self {
    Constructed(tag, children) =>
      if tag.cls == Universal &&
        (tag.number == TAG_SEQUENCE || tag.number == TAG_SET) {
        children
      } else {
        raise InvalidValue("expected SEQUENCE or SET, got " + tag.to_string())
      }
    Primitive(tag, _) =>
      raise InvalidValue("expected SEQUENCE or SET, got " + tag.to_string())
  }
}

///|
/// Returns the single child of an explicitly tagged value (X.690, 8.14.3).
pub fn Der::as_explicit(self : Der, number : Int) -> Der raise DerError {
  match self {
    Constructed(tag, children) => {
      if tag.cls != ContextSpecific || tag.number != number {
        raise ExpectedTag(
          Tag::new(ContextSpecific, number, constructed=true).to_string(),
          tag.to_string(),
        )
      }
      match children {
        [child] => child
        _ =>
          raise InvalidValue(
            "expected a single child inside " + tag.to_string(),
          )
      }
    }
    Primitive(tag, _) =>
      raise InvalidValue(
        "expected an explicitly tagged value, got " + tag.to_string(),
      )
  }
}