///|
/// A generic decoded BER value (X.690). Constructed values expose their
/// children; primitive values expose their raw content octets.
pub struct BerValue {
  tag : BerTag
  content : Bytes
  children : Array[BerValue]?
} derive(Eq, @debug.Debug)

///|
pub fn BerValue::primitive(tag : BerTag, content : Bytes) -> BerValue {
  { tag, content, children: None }
}

///|
pub fn BerValue::constructed(
  tag : BerTag,
  children : Array[BerValue],
) -> BerValue {
  { tag, content: Bytes::new(0), children: Some(children) }
}

///|
pub fn BerValue::is_constructed(self : BerValue) -> Bool {
  self.children is Some(_)
}

///|
pub fn BerValue::children_or_empty(self : BerValue) -> Array[BerValue] {
  match self.children {
    Some(children) => children
    None => []
  }
}

///|
/// A human-readable dump of the value tree; used by the `ber_dump` example
/// and debugging tooling.
pub fn BerValue::to_string(self : BerValue) -> String {
  let sb = StringBuilder()
  self.render(0, sb)
  sb.to_string()
}

///|
fn BerValue::render(self : BerValue, depth : Int, sb : StringBuilder) -> Unit {
  for _ in 0..
      for child in children {
        sb.write_string("\n")
        child.render(depth + 1, sb)
      }
    None => {
      let n = self.content.length()
      if n > 0 {
        let view : BytesView = self.content[:]
        let shown = if n > 16 { view[:16] } else { view }
        sb.write_string(": ")
        sb.write_string(to_hex_lower(shown))
        if n > 16 {
          sb.write_string(" ... (\{n} bytes)")
        }
      }
    }
  }
}

///|
/// Limits enforced during BER decoding to protect against malformed or
/// malicious input (anti-DoS).
pub struct BerLimits {
  max_length : Int
  max_depth : Int
  max_elements : Int
  max_integer_bytes : Int
} derive(Eq, @debug.Debug)

///|
pub impl Default for BerLimits with fn default() {
  {
    max_length: 64 * 1024 * 1024,
    max_depth: 64,
    max_elements: 4096,
    max_integer_bytes: 8,
  }
}

///|
/// A tight limit set used by tests and defensive callers.
pub fn strict_limits() -> BerLimits {
  {
    max_length: 1024 * 1024,
    max_depth: 16,
    max_elements: 256,
    max_integer_bytes: 4,
  }
}

///|
/// Errors produced by the BER encoder/decoder.
pub(all) suberror BerError {
  Truncated
  InvalidLength
  InvalidTag
  IndefinitePrimitive
  MissingEoc
  UnexpectedEoc
  TooDeep
  TooManyElements
  IntegerTooLarge
  LengthExceedsLimit(Int)
  NotSingleValue
  InvalidOid
} derive(Eq, @debug.Debug)