///|
/// A refused boundary value: the constructor name and the reason.
pub(all) suberror IdentityError {
  InvalidIdentity(String, String)
} derive(Debug)

///|
fn identifier_text(text : String) -> Bool {
  if text.length() == 0 || text.length() > 64 {
    return false
  }
  let mut previous_separator = true
  let mut first = true
  for c in text.iter() {
    let lower = c >= 'a' && c <= 'z'
    let digit = c >= '0' && c <= '9'
    let separator = c == '.' || c == '-'
    if first && !lower {
      return false
    }
    if separator && previous_separator {
      return false
    }
    if !lower && !digit && !separator {
      return false
    }
    previous_separator = separator
    first = false
  }
  !previous_separator
}

///|
/// The identifier of a declared serialization profile, such as `morphir-v4`.
/// Lower-case letters and digits, separated by single `.` or `-`, at most 64 characters.
pub struct ProfileId {
  priv text : String
} derive(Eq, Hash, Compare, Debug)

///|
pub fn ProfileId::parse(text : String) -> ProfileId raise IdentityError {
  guard identifier_text(text) else {
    raise InvalidIdentity(
      "ProfileId",
      "I expected lower-case letters and digits separated by single '.' or '-', but found \{text.escape()}.",
    )
  }
  { text, }
}

///|
pub impl Show for ProfileId with fn output(self, logger) {
  logger.write_string(self.text)
}

///|
pub fn ProfileId::to_string(self : ProfileId) -> String {
  self.text
}

///|
/// The identifier of a host-registered scalar adapter, such as `morphir.v4.name`.
pub struct AdapterId {
  priv text : String
} derive(Eq, Hash, Compare, Debug)

///|
pub fn AdapterId::parse(text : String) -> AdapterId raise IdentityError {
  guard identifier_text(text) else {
    raise InvalidIdentity(
      "AdapterId",
      "I expected lower-case letters and digits separated by single '.' or '-', but found \{text.escape()}.",
    )
  }
  { text, }
}

///|
pub impl Show for AdapterId with fn output(self, logger) {
  logger.write_string(self.text)
}

///|
pub fn AdapterId::to_string(self : AdapterId) -> String {
  self.text
}

///|
/// The identifier of a versioned document bundle, such as `morphir`.
pub struct BundleId {
  priv text : String
} derive(Eq, Hash, Compare, Debug)

///|
pub fn BundleId::parse(text : String) -> BundleId raise IdentityError {
  guard identifier_text(text) else {
    raise InvalidIdentity(
      "BundleId",
      "I expected lower-case letters and digits separated by single '.' or '-', but found \{text.escape()}.",
    )
  }
  { text, }
}

///|
pub impl Show for BundleId with fn output(self, logger) {
  logger.write_string(self.text)
}

///|
pub fn BundleId::to_string(self : BundleId) -> String {
  self.text
}

///|
/// A JSON object member name or union tag on the wire. It is not empty and has no unpaired surrogate.
pub struct WireName {
  priv text : String
} derive(Eq, Hash, Compare, Debug)

///|
pub fn WireName::parse(text : String) -> WireName raise IdentityError {
  guard text.length() > 0 && text.length() <= 1024 else {
    raise InvalidIdentity(
      "WireName", "I expected a wire name of 1..1024 characters.",
    )
  }
  for i in 0..= 0xD800 && c <= 0xDBFF {
      guard i + 1 < text.length() &&
        text.code_unit_at(i + 1).to_int() >= 0xDC00 &&
        text.code_unit_at(i + 1).to_int() <= 0xDFFF else {
        raise InvalidIdentity(
          "WireName", "I expected a wire name without an unpaired surrogate.",
        )
      }
    } else if c >= 0xDC00 && c <= 0xDFFF {
      guard i > 0 &&
        text.code_unit_at(i - 1).to_int() >= 0xD800 &&
        text.code_unit_at(i - 1).to_int() <= 0xDBFF else {
        raise InvalidIdentity(
          "WireName", "I expected a wire name without an unpaired surrogate.",
        )
      }
    }
  }
  { text, }
}

///|
pub impl Show for WireName with fn output(self, logger) {
  logger.write_string(self.text)
}

///|
pub fn WireName::to_string(self : WireName) -> String {
  self.text
}

///|
/// An exact release `major.minor.patch`. Comparison is numeric, component by component.
pub struct Release {
  major : Int32
  minor : Int32
  patch : Int32
} derive(Eq, Hash, Compare, Debug)

///|
pub fn Release::new(
  major : Int32,
  minor : Int32,
  patch : Int32,
) -> Release raise IdentityError {
  guard major >= 0 && minor >= 0 && patch >= 0 else {
    raise InvalidIdentity(
      "Release", "I expected nonnegative release components.",
    )
  }
  { major, minor, patch, }
}

///|
/// Parse a strict `X.Y.Z` release: three decimal components without signs or leading zeros.
pub fn Release::parse(text : String) -> Release raise IdentityError {
  let parts = text.split(".").map(p => p.to_owned()).to_array()
  guard parts.length() == 3 else {
    raise InvalidIdentity(
      "Release",
      "I expected a release of three components X.Y.Z, but found \{text.escape()}.",
    )
  }
  let values : Array[Int32] = []
  for part in parts {
    guard part.length() > 0 &&
      part.length() <= 9 &&
      (part.length() == 1 || part.code_unit_at(0) != '0') &&
      part.iter().all(c => c >= '0' && c <= '9') else {
      raise InvalidIdentity(
        "Release",
        "I expected decimal release components without leading zeros, but found \{text.escape()}.",
      )
    }
    let mut value = 0
    for c in part.iter() {
      value = value * 10 + (c.to_int() - '0'.to_int())
    }
    values.push(value)
  }
  { major: values[0], minor: values[1], patch: values[2], }
}

///|
pub fn Release::major(self : Release) -> Int32 {
  self.major
}

///|
pub fn Release::minor(self : Release) -> Int32 {
  self.minor
}

///|
pub fn Release::patch(self : Release) -> Int32 {
  self.patch
}

///|
pub impl Show for Release with fn output(self, logger) {
  logger.write_string("\{self.major}.\{self.minor}.\{self.patch}")
}

///|
pub fn Release::to_string(self : Release) -> String {
  "\{self.major}.\{self.minor}.\{self.patch}"
}

///|
/// A half-open release interval `[since, until)`.
pub struct ReleaseInterval {
  since : Release
  until : Release
} derive(Eq, Debug)

///|
pub fn ReleaseInterval::new(
  since : Release,
  until : Release,
) -> ReleaseInterval raise IdentityError {
  guard since < until else {
    raise InvalidIdentity(
      "ReleaseInterval",
      "I expected the interval start \{since} to precede its end \{until}.",
    )
  }
  { since, until, }
}

///|
pub fn ReleaseInterval::since(self : ReleaseInterval) -> Release {
  self.since
}

///|
pub fn ReleaseInterval::until(self : ReleaseInterval) -> Release {
  self.until
}

///|
pub fn ReleaseInterval::contains(
  self : ReleaseInterval,
  release : Release,
) -> Bool {
  self.since <= release && release < self.until
}

///|
pub fn ReleaseInterval::overlaps(
  self : ReleaseInterval,
  other : ReleaseInterval,
) -> Bool {
  self.since < other.until && other.since < self.until
}

///|
pub impl Show for ReleaseInterval with fn output(self, logger) {
  logger.write_string("[\{self.since}, \{self.until})")
}

///|
/// The kind of one JSON value.
pub(all) enum JsonKind {
  NullKind
  BooleanKind
  NumberKind
  StringKind
  ArrayKind
  ObjectKind
} derive(Eq, Hash, Compare, Debug)

///|
pub fn JsonKind::parse(text : String) -> JsonKind? {
  match text {
    "null" => Some(NullKind)
    "boolean" => Some(BooleanKind)
    "number" => Some(NumberKind)
    "string" => Some(StringKind)
    "array" => Some(ArrayKind)
    "object" => Some(ObjectKind)
    _ => None
  }
}

///|
pub fn JsonKind::name(self : JsonKind) -> String {
  match self {
    NullKind => "null"
    BooleanKind => "boolean"
    NumberKind => "number"
    StringKind => "string"
    ArrayKind => "array"
    ObjectKind => "object"
  }
}

///|
pub impl Show for JsonKind with fn output(self, logger) {
  logger.write_string(self.name())
}

///|
pub fn JsonKind::of(value : Json) -> JsonKind {
  match value {
    Null => NullKind
    True | False => BooleanKind
    Number(_, ..) => NumberKind
    String(_) => StringKind
    Array(_) => ArrayKind
    Object(_) => ObjectKind
  }
}