///|
/// RFC 4506 types. Bounds are nonnegative and checked before allocation.
pub(all) enum Type {
  TVoid
  TInt
  TUInt
  THyper
  TUHyper
  TBool
  TFloat
  TDouble
  TString(Int)
  TOpaque(Int, Bool)
  TArray(Type, Int, Bool)
  TOptional(Type)
  TRef(String)
  TEnum(Array[(String, Int)])
  TStruct(Array[Field])
  TUnion(Type, Array[Arm], Type?)
}

///|
pub(all) struct Field {
  name : String
  ty : Type
}

///|
pub(all) struct Arm {
  labels : Array[Int]
  ty : Type
}

///|
pub struct Schema {
  types : Map[String, Type]
  constants : Map[String, Int]
  limits : Limits
}

///|
pub fn Schema::lookup(self : Schema, name : String) -> Type raise XdrError {
  match self.types.get(name) {
    Some(t) => t
    None => raise Invalid(0, "unknown type: " + name)
  }
}

///|
pub fn Schema::constant(self : Schema, name : String) -> Int? {
  self.constants.get(name)
}

///|
pub fn Schema::type_names(self : Schema) -> Array[String] {
  let names : Array[String] = []
  for name, _ in self.types {
    names.push(name)
  }
  names.sort()
  names
}

///|
pub fn Schema::resolve(self : Schema, ty : Type) -> Type raise XdrError {
  let mut current = ty
  for depth = 0; depth <= self.limits.max_depth; depth = depth + 1 {
    match current {
      TRef(name) => current = self.lookup(name)
      _ => return current
    }
  }
  raise Invalid(0, "cyclic or excessively deep typedef")
}

///|
/// Values preserve signed/unsigned 64-bit and IEEE representations without JSON rounding.
pub(all) enum Value {
  VVoid
  VInt(Int)
  VUInt(UInt)
  VHyper(Int64)
  VUHyper(UInt64)
  VBool(Bool)
  VFloat(Float)
  VDouble(Double)
  VString(String)
  VOpaque(Array[Byte])
  VArray(Array[Value])
  VOptional(Value?)
  VEnum(Int)
  VStruct(Array[(String, Value)])
  VUnion(Int, Value)
}

///|
pub fn Schema::empty(
  limits? : Limits = Limits::default(),
) -> Schema raise XdrError {
  limits.validate()
  { types: {}, constants: {}, limits, }
}