///|
/// How serious a report is. Ordered `Help < Note < Warning < Error`.
#derive(Eq, Compare, Debug)
pub(all) enum Severity {
  Help
  Note
  Warning
  Error
}

///|
/// The lower-case name used in rendered headers: `"error"`, `"warning"`,
/// `"note"` or `"help"`.
pub fn Severity::name(self : Severity) -> String {
  match self {
    Help => "help"
    Note => "note"
    Warning => "warning"
    Error => "error"
  }
}

///|
/// Whether a mark is the main place of the report (drawn with `^`) or a
/// related place (drawn with `-`).
#derive(Eq, Debug)
pub(all) enum Style {
  Primary
  Secondary
}

///|
/// A half-open interval `[start, end)` of positions in a sequence: element
/// indices for an array sequence, Unicode scalar indices for a string
/// sequence. Valid by construction: `0 <= start <= end`.
#derive(Eq, Debug)
pub struct Span {
  start : Int32
  end : Int32
}

///|
/// The span `[start, end)`. A negative end point counts as 0, and an `end`
/// before `start` counts as `start`.
pub fn Span::new(start : Int32, end : Int32) -> Span {
  let start = if start < 0 { 0 } else { start }
  let end = if end < start { start } else { end }
  { start, end, }
}

///|
/// The insertion point `[at, at)`, drawn as one marker: "something is missing
/// here".
pub fn Span::point(at : Int32) -> Span {
  Span::new(at, at)
}

///|
/// A place to mark: a span, a style, an optional message shown next to or
/// under the marker, and a priority that decides overlaps (higher wins).
#derive(Eq, Debug)
pub struct Mark {
  span : Span
  style : Style
  message : String?
  priority : Int32
}

///|
/// A primary mark (`^`) on `span`, with an optional message.
pub fn Mark::primary(span : Span, message? : String) -> Mark {
  { span, style: Primary, message, priority: 0, }
}

///|
/// A secondary mark (`-`) on `span`, with an optional message.
pub fn Mark::secondary(span : Span, message? : String) -> Mark {
  { span, style: Secondary, message, priority: 0, }
}

///|
/// The same mark with another priority.
pub fn Mark::with_priority(self : Mark, priority : Int32) -> Mark {
  { ..self, priority, }
}

///|
/// An error report: a severity, an optional code, a message, marks on the
/// sequence and notes printed after the header.
#derive(Eq, Debug)
pub struct Report {
  severity : Severity
  code : String?
  message : String
  marks : Array[Mark]
  notes : Array[String]
}

///|
/// A report with no code, marks or notes.
pub fn Report::new(severity : Severity, message : String) -> Report {
  { severity, code: None, message, marks: [], notes: [], }
}

///|
/// The same report with a code, shown as `error[CODE]`.
pub fn Report::with_code(self : Report, code : String) -> Report {
  { ..self, code: Some(code), }
}

///|
/// The same report with one more mark.
pub fn Report::with_mark(self : Report, mark : Mark) -> Report {
  { ..self, marks: [..self.marks, mark], }
}

///|
/// The same report with one more note.
pub fn Report::with_note(self : Report, note : String) -> Report {
  { ..self, notes: [..self.notes, note], }
}

///|
/// A command-line argument vector as a sequence: elements joined by a space,
/// quoted when needed so the line can be split back into arguments.
pub fn Sequence::from_args(args : Array[String]) -> Sequence[String] {
  Sequence::from_array(args)
}

///|
/// The Unicode scalar index of UTF-16 offset `offset` in `text`: the number of
/// scalars that start before it. An offset inside a surrogate pair counts
/// that pair as not yet passed. Offsets are clamped to `[0, text.length()]`.
pub fn utf16_to_scalar(text : String, offset : Int32) -> Int32 {
  let mut units = 0
  let mut scalars = 0
  for c in text {
    if units >= offset {
      break
    }
    let w = if c.to_int() > 0xFFFF { 2 } else { 1 }
    if units + w > offset {
      break
    }
    units += w
    scalars += 1
  }
  scalars
}