///|
// Delimiter-aware splitting is shared by selectors, mixin parameters and calls.
fn split_top(text : String, delimiter : Char) -> Array[String] raise ParseError {
  let chars = text.to_array()
  let out = []
  let stack : Array[Char] = []
  let mut quote = '\u0000'
  let mut start = 0
  let mut i = 0
  while i < chars.length() {
    let c = chars[i]
    if c == '\\' {
      i += 2
      continue
    }
    if quote != '\u0000' {
      if c == quote {
        quote = '\u0000'
      }
    } else if c == '"' || c == '\'' {
      quote = c
    } else if c == '(' || c == '[' {
      stack.push(c)
    } else if c == ')' || c == ']' {
      if stack.is_empty() ||
        stack.pop() != Some(if c == ')' { '(' } else { '[' }) {
        raise Invalid("mismatched delimiters")
      }
    } else if c == delimiter && stack.is_empty() {
      out.push(String::from_array(chars[start:i]).trim().to_owned())
      start = i + 1
    }
    i += 1
  }
  if quote != '\u0000' || !stack.is_empty() {
    raise Invalid("unterminated delimiter")
  }
  out.push(String::from_array(chars[start:]).trim().to_owned())
  out
}

///|
fn has_parent(text : String) -> Bool {
  let chars = text.to_array()
  let mut quote = '\u0000'
  let mut bracket = 0
  let mut i = 0
  while i < chars.length() {
    let c = chars[i]
    if c == '\\' {
      i += 2
      continue
    }
    if quote != '\u0000' {
      if c == quote {
        quote = '\u0000'
      }
    } else if c == '"' || c == '\'' {
      quote = c
    } else if c == '[' {
      bracket += 1
    } else if c == ']' {
      bracket -= 1
    } else if c == '&' && bracket == 0 {
      return true
    }
    i += 1
  }
  false
}

///|
fn prepare_selector(
  parent : Array[String],
  text : String,
  depth : Int,
) -> (Array[String], Bool) raise ParseError {
  if depth > 32 {
    raise Invalid("selector nesting limit")
  }
  let chars = text.to_array()
  let segments = []
  let mut current = ""
  let mut quote = '\u0000'
  let mut bracket = 0
  let mut nested = false
  let mut i = 0
  while i < chars.length() {
    let c = chars[i]
    if c == '\\' {
      current += c.to_string()
      i += 1
      if i < chars.length() {
        current += chars[i].to_string()
        i += 1
      }
      continue
    }
    if quote != '\u0000' {
      current += c.to_string()
      if c == quote {
        quote = '\u0000'
      }
      i += 1
      continue
    }
    if c == '"' || c == '\'' {
      quote = c
    }
    if c == '{' || c == '}' || c == ';' {
      raise Invalid("invalid selector punctuation")
    }
    if c == '[' {
      bracket += 1
    }
    if c == ']' {
      bracket -= 1
    }
    if c == '(' && bracket == 0 {
      let start = i + 1
      let mut j = start
      let mut level = 1
      let mut q = '\u0000'
      while j < chars.length() && level > 0 {
        let x = chars[j]
        if x == '\\' {
          j += 2
          continue
        }
        if q != '\u0000' {
          if x == q {
            q = '\u0000'
          }
        } else if x == '"' || x == '\'' {
          q = x
        } else if x == '(' {
          level += 1
        } else if x == ')' {
          level -= 1
        }
        j += 1
      }
      if level != 0 {
        raise Invalid("unterminated selector function")
      }
      let inner = String::from_array(chars[start:j - 1])
      if has_parent(inner) {
        let expanded = resolve_selectors(parent, inner, false, depth + 1)
        current += "(" + expanded.join(", ") + ")"
        nested = true
      } else {
        current += "(" + inner + ")"
      }
      i = j
      continue
    }
    if c == '&' && bracket == 0 {
      if i > 0 &&
        ![' ', '\t', '\n', '\r', '>', '+', '~', '|'].contains(chars[i - 1]) {
        raise Invalid("parent selector must start a compound")
      }
      segments.push(current)
      current = ""
      i += 1
      continue
    }
    current += c.to_string()
    i += 1
  }
  segments.push(current)
  (segments, nested)
}

///|
fn resolve_selectors(
  parent : Array[String],
  child : String,
  implicit : Bool,
  depth : Int,
) -> Array[String] raise ParseError {
  let children = split_top(child, ',')
  let prepared = []
  for c in children {
    if c.is_empty() || c.has_prefix("@") {
      raise Invalid("empty or invalid selector")
    }
    if parent.is_empty() && has_parent(c) {
      raise Invalid("parent selector at root")
    }
    prepared.push((c, prepare_selector(parent, c, depth)))
  }
  let out : Array[String] = []
  let parents = if parent.is_empty() { [""] } else { parent }
  for p in parents {
    for (original, (parts, nested)) in prepared {
      if parts.length() == 1 {
        let value = if implicit && !nested && !p.is_empty() {
          p + " " + parts[0]
        } else {
          parts[0]
        }
        if (!nested && implicit) || !out.contains(value) {
          out.push(value)
        }
      } else {
        let mut values = [parts[0]]
        for i in 1.. 256 {
                raise Invalid("selector expansion limit")
              }
            }
          }
          values = next
        }
        for value in values {
          out.push(value)
        }
      }
      ignore(original)
      if out.length() > 256 {
        raise Invalid("selector expansion limit")
      }
    }
  }
  out
}

///|
fn selectors(
  parent : Array[String],
  child : String,
) -> Array[String] raise ParseError {
  resolve_selectors(parent, child, true, 0)
}