///|
#warnings("-unused_field")
priv struct AttributeSelector {
  attribute : String
  operator : String?
  quote : String?
  value : String?
  sensitivity : String?
}

///|
fn is_attribute_whitespace(character : UInt16) -> Bool {
  character == ' ' ||
  character == '\t' ||
  character == '\n' ||
  character == '\r'
}

///|
fn is_attribute_name_character(character : UInt16) -> Bool {
  (character >= 'a' && character <= 'z') ||
  (character >= 'A' && character <= 'Z') ||
  (character >= '0' && character <= '9') ||
  character == '-' ||
  character == '_' ||
  character.to_int() >= 128
}

///|
fn parse_attribute_selector(input : String) -> AttributeSelector? {
  if input.length() < 3 || input[0] != '[' || input[input.length() - 1] != ']' {
    return None
  }
  let end = input.length() - 1
  let mut index = 1
  while index < end && is_attribute_whitespace(input[index]) {
    index += 1
  }
  let name_start = index
  while index < end {
    if input[index] == '\\' {
      if index + 1 >= end {
        return None
      }
      index += 2
    } else if is_attribute_name_character(input[index]) {
      index += 1
    } else {
      break
    }
  }
  if index == name_start {
    return None
  }
  let attribute = input[name_start:index].to_owned()
  while index < end && is_attribute_whitespace(input[index]) {
    index += 1
  }
  if index == end {
    return Some({
      attribute,
      operator: None,
      quote: None,
      value: None,
      sensitivity: None,
    })
  }
  let operator = if input[index] == '=' {
    index += 1
    "="
  } else if index + 1 < end &&
    (
      input[index] == '~' ||
      input[index] == '|' ||
      input[index] == '^' ||
      input[index] == '$' ||
      input[index] == '*'
    ) &&
    input[index + 1] == '=' {
    let result = input[index:index + 2].to_owned()
    index += 2
    result
  } else {
    return None
  }
  while index < end && is_attribute_whitespace(input[index]) {
    index += 1
  }
  if index == end {
    return None
  }
  let mut quote : String? = None
  let mut value = ""
  if input[index] == '\'' || input[index] == '"' {
    let expected = input[index]
    quote = Some(input[index:index + 1].to_owned())
    index += 1
    let value_start = index
    let mut closing = -1
    while index < end {
      if input[index] == '\\' {
        index += 2
      } else if input[index] == expected {
        closing = index
        break
      } else {
        index += 1
      }
    }
    if closing < 0 {
      return None
    }
    value = input[value_start:closing].to_owned()
    index = closing + 1
  } else {
    let value_start = index
    while index < end && !is_attribute_whitespace(input[index]) {
      index += 1
    }
    value = input[value_start:index].to_owned()
  }
  if value == "" {
    return None
  }
  while index < end && is_attribute_whitespace(input[index]) {
    index += 1
  }
  let mut sensitivity : String? = None
  if index < end {
    let flag = input[index]
    if flag == 'i' || flag == 'I' {
      sensitivity = Some("i")
    } else if flag == 's' || flag == 'S' {
      sensitivity = Some("s")
    } else {
      return None
    }
    index += 1
    while index < end && is_attribute_whitespace(input[index]) {
      index += 1
    }
  }
  if index != end {
    return None
  }
  Some({
    attribute,
    operator: Some(operator),
    quote,
    value: Some(value),
    sensitivity,
  })
}