///|
fn mail_cfws(
  a : Array[Char],
  start : Int,
  path : String,
) -> Int raise ParseError {
  let mut pos = start
  while pos < a.length() {
    if a[pos] == ' ' || a[pos] == '\t' || a[pos] == '\r' || a[pos] == '\n' {
      pos += 1
      continue
    }
    if a[pos] != '(' {
      break
    }
    let mut depth = 1
    pos += 1
    while pos < a.length() && depth > 0 {
      if a[pos] == '\\' {
        pos += 1
        if pos >= a.length() {
          raise InvalidRequiredField(path, "unfinished email comment escape")
        }
      } else if a[pos] == '(' {
        depth += 1
      } else if a[pos] == ')' {
        depth -= 1
      }
      if depth > 32 {
        raise InvalidRequiredField(path, "email comment nesting exceeds 32")
      }
      pos += 1
    }
    if depth != 0 {
      raise InvalidRequiredField(path, "unclosed email comment")
    }
  }
  pos
}

///|
fn mail_atom(c : Char) -> Bool {
  ascii_alpha(c) ||
  ascii_digit(c) ||
  "!#$%&'*+-/=?^_`{|}~".contains(c.to_string())
}

///|
fn mail_word(
  a : Array[Char],
  start : Int,
  quoted : Bool,
  path : String,
) -> Int raise ParseError {
  let mut pos = mail_cfws(a, start, path)
  if quoted && pos < a.length() && a[pos] == '"' {
    pos += 1
    while pos < a.length() {
      if a[pos] == '"' {
        return mail_cfws(a, pos + 1, path)
      }
      if a[pos] == '\\' {
        pos += 1
        if pos >= a.length() {
          raise InvalidRequiredField(path, "unfinished quoted email escape")
        }
      }
      pos += 1
    }
    raise InvalidRequiredField(path, "unclosed quoted email local part")
  }
  let begin = pos
  while pos < a.length() && mail_atom(a[pos]) {
    pos += 1
  }
  if pos == begin {
    raise InvalidRequiredField(path, "missing email atom")
  }
  mail_cfws(a, pos, path)
}

///|
/// Addr-spec parser with dot atoms, quoted words, comments and domain literals.
/// Obsolete control-character/FWS productions remain outside the validated subset.
fn validate_email(value : String, path : String) -> Unit raise ParseError {
  let a = value.to_array()
  header_characters(a, path)
  for c in a {
    if c.to_int() > 0x7F ||
      (c.to_int() < 0x20 && c != '\t' && c != '\r' && c != '\n') {
      raise InvalidRequiredField(
        path, "email must use RFC 2822 ASCII addr-spec",
      )
    }
  }
  let mut pos = mail_word(a, 0, true, path)
  while pos < a.length() && a[pos] == '.' {
    pos = mail_word(a, pos + 1, true, path)
  }
  if pos >= a.length() || a[pos] != '@' {
    raise InvalidRequiredField(path, "email requires one @ separator")
  }
  pos = mail_cfws(a, pos + 1, path)
  if pos < a.length() && a[pos] == '[' {
    pos += 1
    let mut closed = false
    while pos < a.length() {
      if a[pos] == ']' {
        pos += 1
        closed = true
        break
      }
      if a[pos] == '[' {
        raise InvalidRequiredField(path, "nested email domain literal")
      }
      if a[pos] == '\\' {
        pos += 1
      }
      if pos >= a.length() {
        raise InvalidRequiredField(path, "unfinished email domain literal")
      }
      pos += 1
    }
    if !closed {
      raise InvalidRequiredField(path, "unclosed email domain literal")
    }
    pos = mail_cfws(a, pos, path)
  } else {
    pos = mail_word(a, pos, false, path)
    while pos < a.length() && a[pos] == '.' {
      pos = mail_word(a, pos + 1, false, path)
    }
  }
  if pos != a.length() {
    raise InvalidRequiredField(path, "unexpected data after email addr-spec")
  }
}