///|
/// A model-aware value, independent of its JSON spelling. Structures hold their present members by Smithy
/// member name, a union names its selected member, and maps hold logical keys. `Object` is a document
/// object, which has no modeled members.
pub(all) enum LogicalValue {
  Null
  Boolean(Bool)
  Number(@smithy.NodeNumber)
  String(String)
  List(Array[LogicalValue])
  Structure(Array[(String, LogicalValue)])
  Union(String, LogicalValue)
  Map(Array[(String, LogicalValue)])
  Object(Array[(String, LogicalValue)])
} derive(Debug)

///|
/// A number from its exact JSON text, such as `12345678901234567890.25`.
pub fn LogicalValue::number(
  text : String,
) -> LogicalValue raise @validation.ConfigurationError {
  match @smithy.NodeNumber::parse(text) {
    Some(n) => Number(n)
    None =>
      raise @validation.ConfigurationError(
        "I expected one exact JSON number token, but found \{text.escape()}.",
      )
  }
}

///|
/// The present member `name` of a structure, the entry `name` of a map or document object.
pub fn LogicalValue::get(self : LogicalValue, name : String) -> LogicalValue? {
  match self {
    Structure(fields) | Map(fields) | Object(fields) => {
      for entry in fields {
        if entry.0 == name {
          return Some(entry.1)
        }
      }
      None
    }
    _ => None
  }
}

///|
/// The selected member of a union value.
pub fn LogicalValue::selected(self : LogicalValue) -> (String, LogicalValue)? {
  match self {
    Union(name, payload) => Some((name, payload))
    _ => None
  }
}

///|
fn sorted_entries(
  fields : Array[(String, LogicalValue)],
) -> Array[(String, LogicalValue)] {
  let out = fields.copy()
  out.sort_by((a, b) => a.0.compare(b.0))
  out
}

///|
/// Semantic equality: numbers compare by exact value; structure members, map entries and document
/// fields compare without regard to order. It uses an explicit stack, not recursion.
pub impl Eq for LogicalValue with fn equal(self, other) {
  let stack : Array[(LogicalValue, LogicalValue)] = [(self, other)]
  while stack.pop() is Some((a, b)) {
    match (a, b) {
      (Null, Null) => ()
      (Boolean(x), Boolean(y)) => if x != y { return false }
      (Number(x), Number(y)) => if x != y { return false }
      (String(x), String(y)) => if x != y { return false }
      (List(xs), List(ys)) => {
        if xs.length() != ys.length() {
          return false
        }
        for i in 0.. {
        if m != n {
          return false
        }
        stack.push((x, y))
      }
      (Structure(xs), Structure(ys))
      | (Map(xs), Map(ys))
      | (Object(xs), Object(ys)) => {
        if xs.length() != ys.length() {
          return false
        }
        let xs = sorted_entries(xs)
        let ys = sorted_entries(ys)
        for i in 0.. return false
    }
  }
  true
}

///|
/// The identity of a logical contract: a root shape and the fingerprint of the contract reachable from it.
/// Profiles of one model and root share it; another root or another modeled version does not.
pub struct SemanticRoot {
  shape : @smithy.ShapeId
  fingerprint : String
} derive(Eq, Debug)

///|
pub impl Show for SemanticRoot with fn output(self, logger) {
  logger.write_string(self.shape.to_string() + "@" + self.fingerprint)
}

///|
/// A logical value that a codec has checked against its semantic root. Only decoding and
/// `PreparedCodec::value` create one.
pub struct CodecValue {
  priv root : SemanticRoot
  priv value : LogicalValue
} derive(Debug)

///|
pub fn CodecValue::root(self : CodecValue) -> SemanticRoot {
  self.root
}

///|
pub fn CodecValue::logical(self : CodecValue) -> LogicalValue {
  self.value
}

///|
pub impl Eq for CodecValue with fn equal(self, other) {
  self.root == other.root && self.value == other.value
}