///|
/// The calendar system a date is interpreted in.
///
/// This port implements the ISO 8601 calendar. The type exists as a distinct
/// value rather than being elided because calendar identity is observable
/// through parsing, `toString` annotations, and the cross-calendar mismatch
/// checks that arithmetic and comparison perform.
///
/// Reference: 
pub(all) enum Calendar {
  /// The proleptic Gregorian calendar, as ISO 8601 defines it.
  ISO
} derive(Eq, Debug)

///|
pub impl Default for Calendar with fn default() {
  ISO
}

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

///|
/// Returns the canonical calendar identifier.
pub fn Calendar::identifier(self : Calendar) -> String {
  match self {
    ISO => "iso8601"
  }
}

///|
/// Parses a calendar identifier.
///
/// Identifiers are matched case-insensitively, as the Temporal specification
/// requires. Calendars other than `iso8601` are rejected: they are recognised
/// as valid Temporal calendars but are not implemented here.
///
/// ```mbt check
/// test {
///   inspect(@temporal.Calendar::of_string("ISO8601"), content="iso8601")
/// }
/// ```
pub fn Calendar::of_string(s : String) -> Calendar raise TemporalError {
  match ascii_lowercase(s) {
    "iso8601" => ISO
    other =>
      if is_known_calendar_identifier(other) {
        raise RangeError(
          "calendar '\{s}' is a valid Temporal calendar but is not implemented by this port",
        )
      } else {
        raise RangeError("'\{s}' is not a recognized calendar identifier")
      }
  }
}

///|
/// Returns whether the identifier names a calendar the Temporal specification
/// recognises, whether or not this port implements it.
///
/// Distinguishing the two lets an unimplemented-but-valid calendar produce a
/// clearer message than an outright typo.
fn is_known_calendar_identifier(lowercase : String) -> Bool {
  lowercase
  is ("buddhist"
  | "chinese"
  | "coptic"
  | "dangi"
  | "ethioaa"
  | "ethiopic"
  | "ethiopic-amete-alem"
  | "gregory"
  | "hebrew"
  | "indian"
  | "islamic"
  | "islamic-civil"
  | "islamic-rgsa"
  | "islamic-tbla"
  | "islamic-umalqura"
  | "islamicc"
  | "japanese"
  | "persian"
  | "roc")
}

///|
/// Lowercases the ASCII letters in a string, leaving other characters alone.
fn ascii_lowercase(s : String) -> String {
  let buf = StringBuilder::new()
  for c in s {
    if c >= 'A' && c <= 'Z' {
      buf.write_char(Int::unsafe_to_char(c.to_int() + 32))
    } else {
      buf.write_char(c)
    }
  }
  buf.to_string()
}