///|
/// One brace expression in the source template. Offsets are UTF-16 code units.
pub(all) struct ExpressionInfo {
  start : Int
  end : Int
  operator : String
  level : Int
  variable_count : Int
  permits_reserved : Bool
}

///|
/// One occurrence of a variable specification, including its modifier.
pub(all) struct VariableUse {
  name : String
  start : Int
  end : Int
  expression_index : Int
  operator : String
  prefix : Int?
  explode : Bool
}

///|
/// Static summary of a compiled template. It does not inspect bound values.
pub struct TemplateAnalysis {
  expressions : Array[ExpressionInfo]
  uses : Array[VariableUse]
  max_level : Int
  has_reserved_expansion : Bool
}

///|
pub fn TemplateAnalysis::expressions(
  self : TemplateAnalysis,
) -> Array[ExpressionInfo] {
  self.expressions.copy()
}

///|
pub fn TemplateAnalysis::uses(self : TemplateAnalysis) -> Array[VariableUse] {
  self.uses.copy()
}

///|
/// Highest RFC 6570 feature level required by the template (1–4).
pub fn TemplateAnalysis::max_level(self : TemplateAnalysis) -> Int {
  self.max_level
}

///|
/// True if '+' or '#' allows reserved characters from a value into the URI.
pub fn TemplateAnalysis::has_reserved_expansion(
  self : TemplateAnalysis,
) -> Bool {
  self.has_reserved_expansion
}

///|
fn Operator::symbol(self : Operator) -> String {
  match self {
    Simple => ""
    Reserved => "+"
    Fragment => "#"
    Label => "."
    Path => "/"
    PathParameter => ";"
    Query => "?"
    QueryContinuation => "&"
  }
}

///|
fn Operator::level(self : Operator) -> Int {
  match self {
    Simple => 1
    Reserved | Fragment => 2
    Label | Path | PathParameter | Query | QueryContinuation => 3
  }
}

///|
pub fn Template::analyze(self : Template) -> TemplateAnalysis {
  let expressions : Array[ExpressionInfo] = []
  let uses : Array[VariableUse] = []
  let mut max_level = 1
  let mut has_reserved = false
  for segment in self.segments {
    match segment {
      Literal(_) => ()
      Expression(op, specs, start, end) => {
        let mut level = op.level()
        let permits_reserved = operator_allows_reserved(op)
        if permits_reserved {
          has_reserved = true
        }
        let expression_index = expressions.length()
        for spec in specs {
          if spec.explode || spec.prefix is Some(_) {
            level = 4
          }
          uses.push({
            name: spec.name,
            start: spec.start,
            end: spec.end,
            expression_index,
            operator: op.symbol(),
            prefix: spec.prefix,
            explode: spec.explode,
          })
        }
        if level > max_level {
          max_level = level
        }
        expressions.push({
          start,
          end,
          operator: op.symbol(),
          level,
          variable_count: specs.length(),
          permits_reserved,
        })
      }
    }
  }
  { expressions, uses, max_level, has_reserved_expansion: has_reserved, }
}

///|
/// Count how often a variable occurs in this template.
pub fn Template::reference_count(self : Template, name : String) -> Int {
  let mut count = 0
  for segment in self.segments {
    match segment {
      Literal(_) => ()
      Expression(_, specs, _, _) =>
        for spec in specs {
          if spec.name == name {
            count += 1
          }
        }
    }
  }
  count
}