// The declared-description constructors and queries (see schema_spec.mbt).

///|
pub fn FieldInfo::new(name : String, ty : TyInfo) -> FieldInfo {
  { name, ty }
}

///|
/// The runtime kind this field carries — a projection of its type, never a
/// second statement about it.
pub fn FieldInfo::kind(self : FieldInfo) -> FieldKind {
  self.ty.kind()
}

///|
pub fn FieldInfo::default(self : FieldInfo) -> Value {
  self.ty.zero()
}

///|
pub fn SchemaInfo::new(
  name? : String = "",
  fingerprint~ : String,
  fields? : Array[FieldInfo] = [],
  inputs? : Array[String] = [],
  receives? : Array[String] = [],
  bubbles? : Array[String] = [],
  responses? : Array[String] = [],
  methods? : Array[String] = [],
  ids? : Array[String] = [],
  view_names? : Array[String] = [],
  init_names? : Array[String] = [],
) -> SchemaInfo {
  {
    name,
    fingerprint,
    fields,
    inputs,
    receives,
    bubbles,
    responses,
    methods,
    ids,
    view_names,
    init_names,
  }
}

///|
/// Linear over `fields`, which is the right shape at these sizes: a component
/// has a handful of them and the array preserves declaration order, which the
/// callers below read as much as they read the entries themselves.
pub fn SchemaInfo::field(self : SchemaInfo, name : String) -> FieldInfo? {
  for f in self.fields {
    if f.name == name {
      return Some(f)
    }
  }
  None
}

///|
pub fn SchemaInfo::field_names(self : SchemaInfo) -> Array[String] {
  self.fields.map(f => f.name)
}

///|
/// A structural fingerprint of a schema that arrives at RUNTIME, for a caller
/// with no generated one to carry: the dynamic-component host, which receives a
/// guest's declared shape over the `tutuca:component` boundary and needs the
/// same thing `_schema_fingerprint` gives a compiled component — a value
/// that changes when the shape does, and not otherwise (it seeds the render
/// cache's bucket, and says when stored state can no longer be read back).
///
/// The twin of `@statedef.fingerprint`, which hashes the same shape one step
/// earlier — from the SOURCE schema, where a variant's payload types and the
/// user types are still visible. The two hash different inputs and do not
/// produce the same string for the same component; neither is compared against
/// the other, and each is stable for its own producer, which is all either one
/// is for.
pub fn SchemaInfo::shape_fingerprint(self : SchemaInfo) -> String {
  let sb = StringBuilder::new()
  sb.write_string("i:\{self.name};")
  for f in self.fields {
    sb.write_string("f:\{f.name}:\{f.ty.show_source()};")
    for m in f.ty.members() {
      sb.write_string("m:\{m},")
    }
  }
  for
    pair in [
      ("r", self.receives),
      ("b", self.bubbles),
      ("p", self.responses),
      ("c", self.methods),
      ("v", self.view_names),
    ] {
    for n in pair.1 {
      sb.write_string("\{pair.0}:\{n};")
    }
  }
  // FNV-1a, hex — not cryptographic and does not need to be: it distinguishes
  // schemas a human wrote, not ones an attacker did.
  let mut h : UInt64 = 0xcbf29ce484222325UL
  for c in sb.to_string().to_array() {
    h = (h ^ c.to_int().reinterpret_as_uint().to_uint64()) * 0x100000001b3UL
  }
  let digits = "0123456789abcdef".to_array()
  let out = StringBuilder::new()
  for i in 0..<16 {
    out.write_char(digits[((h >> ((15 - i) * 4)) & 0xfUL).to_int()])
  }
  out.to_string()
}

///|
/// The name of a field's generated mutator: `count` + `set` -> `setCount`.
///
/// The convention lives HERE, in one function, because two places have to agree
/// on it and neither can see the other. `component()` builds the mutators by
/// concatenating it, and the state editor — which holds a bare `Value` and calls
/// them by name through `Obj::obj_field` — reconstructed the same string from
/// its own copy. Two spellings of one convention, either of which could have
/// been changed alone.
pub fn mutator_name(verb : String, field : String) -> String {
  if field.is_empty() {
    return verb
  }
  verb + field[0:1].to_owned().to_upper() + field[1:].to_owned()
}