///|
fn syntax_error(offset : Int, message : String) -> UriTemplateError {
  SyntaxError(offset~, message~)
}

///|
fn validate_literal_at(
  source : String,
  offset : Int,
) -> Unit raise UriTemplateError {
  let ch = source[offset]
  if ch == '%' {
    if offset + 2 >= source.length() ||
      !@internal.is_hex_digit(source[offset + 1]) ||
      !@internal.is_hex_digit(source[offset + 2]) {
      raise syntax_error(offset, "invalid percent triplet in literal text")
    }
    return
  }
  let forbidden = ch <= 0x20 ||
    (ch >= 0x7F && ch <= 0x9F) ||
    ch == '"' ||
    ch == '<' ||
    ch == '>' ||
    ch == '\\' ||
    ch == '^' ||
    ch == '`' ||
    ch == '|'
  if forbidden {
    raise syntax_error(offset, "character is not allowed in URI Template text")
  }
}

///|
fn parse_variable_spec(
  source : String,
  start : Int,
  end : Int,
) -> VariableSpec raise UriTemplateError {
  if start >= end {
    raise syntax_error(start, "empty variable specification")
  }
  let mut i = start
  let mut last_was_dot = false
  for ; i < end; {
    let ch = source[i]
    if ch == '*' || ch == ':' {
      break
    }
    if @internal.is_ascii_alpha(ch) || @internal.is_ascii_digit(ch) || ch == '_' {
      last_was_dot = false
      i += 1
      continue
    }
    if ch == '.' {
      if i == start || last_was_dot {
        raise syntax_error(i, "variable name contains an empty dot segment")
      }
      last_was_dot = true
      i += 1
      continue
    }
    if ch == '%' {
      if i + 2 >= end ||
        !@internal.is_hex_digit(source[i + 1]) ||
        !@internal.is_hex_digit(source[i + 2]) {
        raise syntax_error(i, "invalid percent triplet in variable name")
      }
      last_was_dot = false
      i += 3
      continue
    }
    raise syntax_error(i, "invalid character in variable name")
  }
  if i == start || last_was_dot {
    raise syntax_error(i, "invalid variable name")
  }
  let name = @internal.owned_slice(source, start, i)
  let modifier = if i == end {
    NoModifier
  } else if source[i] == '*' {
    if i + 1 != end {
      raise syntax_error(i + 1, "explode modifier must end the variable")
    }
    Explode
  } else {
    let digit_start = i + 1
    if digit_start >= end ||
      source[digit_start] < '1' ||
      source[digit_start] > '9' {
      raise syntax_error(digit_start, "prefix length must start with 1-9")
    }
    if end - digit_start > 4 {
      raise syntax_error(digit_start, "prefix length must use at most 4 digits")
    }
    let mut length = 0
    for j in digit_start.. (Operator, Array[VariableSpec], Int) raise UriTemplateError {
  if start >= end {
    raise syntax_error(start, "empty expression")
  }
  let mut i = start
  let operator = match operator_from_char(source[i]) {
    Some(op) => {
      i += 1
      op
    }
    None =>
      if source[i] == '=' ||
        source[i] == ',' ||
        source[i] == '!' ||
        source[i] == '@' ||
        source[i] == '|' {
        raise syntax_error(i, "reserved operator is not defined by RFC 6570")
      } else {
        Simple
      }
  }
  if i >= end {
    raise syntax_error(i, "expression must contain at least one variable")
  }
  let variables : Array[VariableSpec] = []
  let mut item_start = i
  for ; i <= end; {
    if i == end || source[i] == ',' {
      if variables.length() >= max_variables {
        raise ParseLimitExceeded(
          kind="variables per expression",
          limit=max_variables,
        )
      }
      variables.push(parse_variable_spec(source, item_start, i))
      item_start = i + 1
    }
    i += 1
  }
  let mut level = operator.level()
  for variable in variables {
    match variable.modifier {
      Prefix(_) | Explode => level = 4
      NoModifier => ()
    }
  }
  (operator, variables, level)
}

///|
/// Default maximum accepted template length, measured in UTF-16 code units.
pub const DEFAULT_MAX_TEMPLATE_LENGTH : Int = 1048576

///|
/// Default maximum number of expressions in one template.
pub const DEFAULT_MAX_EXPRESSIONS : Int = 4096

///|
/// Default maximum number of variables in one expression.
pub const DEFAULT_MAX_VARIABLES_PER_EXPRESSION : Int = 256

///|
/// Parse and validate an RFC 6570 URI Template with explicit resource limits.
pub fn UriTemplate::parse_with_limits(
  source : StringView,
  max_template_length~ : Int,
  max_expressions~ : Int,
  max_variables_per_expression~ : Int,
) -> UriTemplate raise UriTemplateError {
  let source = source.to_owned()
  if source.length() > max_template_length {
    raise ParseLimitExceeded(kind="template length", limit=max_template_length)
  }
  let parts : Array[TemplatePart] = []
  let variable_names : Array[String] = []
  let mut level = 1
  let mut literal_start = 0
  let mut expression_count = 0
  let mut i = 0
  for ; i < source.length(); {
    let ch = source[i]
    if ch == '}' {
      raise syntax_error(i, "unexpected closing brace")
    }
    if ch != '{' {
      validate_literal_at(source, i)
      i += 1
      continue
    }
    if literal_start < i {
      parts.push(Literal(@internal.owned_slice(source, literal_start, i)))
    }
    expression_count += 1
    if expression_count > max_expressions {
      raise ParseLimitExceeded(kind="expression count", limit=max_expressions)
    }
    let expression_start = i + 1
    let mut close = expression_start
    for ; close < source.length() && source[close] != '}'; {
      if source[close] == '{' {
        raise syntax_error(close, "nested opening brace")
      }
      close += 1
    }
    if close >= source.length() {
      raise syntax_error(i, "unclosed expression")
    }
    let (operator, variables, expression_level) = parse_expression(
      source,
      expression_start,
      close,
      max_variables=max_variables_per_expression,
    )
    if expression_level > level {
      level = expression_level
    }
    for variable in variables {
      if !variable_names.contains(variable.name) {
        variable_names.push(variable.name)
      }
    }
    parts.push(Expression(operator, variables))
    i = close + 1
    literal_start = i
  }
  if literal_start < source.length() {
    parts.push(
      Literal(@internal.owned_slice(source, literal_start, source.length())),
    )
  }
  { source, parts, variables: variable_names, level }
}

///|
/// Parse and validate an RFC 6570 URI Template using safe default limits.
pub fn UriTemplate::parse(
  source : StringView,
) -> UriTemplate raise UriTemplateError {
  UriTemplate::parse_with_limits(
    source,
    max_template_length=DEFAULT_MAX_TEMPLATE_LENGTH,
    max_expressions=DEFAULT_MAX_EXPRESSIONS,
    max_variables_per_expression=DEFAULT_MAX_VARIABLES_PER_EXPRESSION,
  )
}

///|
/// Return whether a string is a syntactically valid URI Template.
pub fn is_valid(source : StringView) -> Bool {
  try UriTemplate::parse(source) catch {
    _ => false
  } noraise {
    _ => true
  }
}

///|
/// Return variable names in first-appearance order without duplicates.
pub fn UriTemplate::variables(self : UriTemplate) -> Array[String] {
  self.variables.copy()
}

///|
/// Return the lowest RFC 6570 feature level required by this template.
pub fn UriTemplate::level(self : UriTemplate) -> Int {
  self.level
}

///|
/// Return the original template source.
pub fn UriTemplate::source(self : UriTemplate) -> String {
  self.source
}