package cascadia

import (
	
	
	

	
)

// Matcher is the interface for basic selector functionality.
// Match returns whether a selector matches n.
type Matcher interface {
	Match(n *html.Node) bool
}

// Sel is the interface for all the functionality provided by selectors.
type Sel interface {
	Matcher
	Specificity() Specificity

	// Returns a CSS input compiling to this selector.
	String() string

	// Returns a pseudo-element, or an empty string.
	PseudoElement() string
}

// Parse parses a selector. Use `ParseWithPseudoElement`
// if you need support for pseudo-elements.
func ( string) (Sel, error) {
	 := &parser{s: }
	,  := .parseSelector()
	if  != nil {
		return nil, 
	}

	if .i < len() {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", , len()-.i)
	}

	return , nil
}

// ParseWithPseudoElement parses a single selector,
// with support for pseudo-element.
func ( string) (Sel, error) {
	 := &parser{s: , acceptPseudoElements: true}
	,  := .parseSelector()
	if  != nil {
		return nil, 
	}

	if .i < len() {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", , len()-.i)
	}

	return , nil
}

// ParseGroup parses a selector, or a group of selectors separated by commas.
// Use `ParseGroupWithPseudoElements`
// if you need support for pseudo-elements.
func ( string) (SelectorGroup, error) {
	 := &parser{s: }
	,  := .parseSelectorGroup()
	if  != nil {
		return nil, 
	}

	if .i < len() {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", , len()-.i)
	}

	return , nil
}

// ParseGroupWithPseudoElements parses a selector, or a group of selectors separated by commas.
// It supports pseudo-elements.
func ( string) (SelectorGroup, error) {
	 := &parser{s: , acceptPseudoElements: true}
	,  := .parseSelectorGroup()
	if  != nil {
		return nil, 
	}

	if .i < len() {
		return nil, fmt.Errorf("parsing %q: %d bytes left over", , len()-.i)
	}

	return , nil
}

// A Selector is a function which tells whether a node matches or not.
//
// This type is maintained for compatibility; I recommend using the newer and
// more idiomatic interfaces Sel and Matcher.
type Selector func(*html.Node) bool

// Compile parses a selector and returns, if successful, a Selector object
// that can be used to match against html.Node objects.
func ( string) (Selector, error) {
	,  := ParseGroup()
	if  != nil {
		return nil, 
	}

	return Selector(.Match), nil
}

// MustCompile is like Compile, but panics instead of returning an error.
func ( string) Selector {
	,  := Compile()
	if  != nil {
		panic()
	}
	return 
}

// MatchAll returns a slice of the nodes that match the selector,
// from n and its children.
func ( Selector) ( *html.Node) []*html.Node {
	return .matchAllInto(, nil)
}

func ( Selector) ( *html.Node,  []*html.Node) []*html.Node {
	if () {
		 = append(, )
	}

	for  := .FirstChild;  != nil;  = .NextSibling {
		 = .(, )
	}

	return 
}

func queryInto( *html.Node,  Matcher,  []*html.Node) []*html.Node {
	for  := .FirstChild;  != nil;  = .NextSibling {
		if .Match() {
			 = append(, )
		}
		 = (, , )
	}

	return 
}

// QueryAll returns a slice of all the nodes that match m, from the descendants
// of n.
func ( *html.Node,  Matcher) []*html.Node {
	return queryInto(, , nil)
}

// Match returns true if the node matches the selector.
func ( Selector) ( *html.Node) bool {
	return ()
}

// MatchFirst returns the first node that matches s, from n and its children.
func ( Selector) ( *html.Node) *html.Node {
	if .Match() {
		return 
	}

	for  := .FirstChild;  != nil;  = .NextSibling {
		 := .()
		if  != nil {
			return 
		}
	}
	return nil
}

// Query returns the first node that matches m, from the descendants of n.
// If none matches, it returns nil.
func ( *html.Node,  Matcher) *html.Node {
	for  := .FirstChild;  != nil;  = .NextSibling {
		if .Match() {
			return 
		}
		if  := (, );  != nil {
			return 
		}
	}

	return nil
}

// Filter returns the nodes in nodes that match the selector.
func ( Selector) ( []*html.Node) ( []*html.Node) {
	for ,  := range  {
		if () {
			 = append(, )
		}
	}
	return 
}

// Filter returns the nodes that match m.
func ( []*html.Node,  Matcher) ( []*html.Node) {
	for ,  := range  {
		if .Match() {
			 = append(, )
		}
	}
	return 
}

type tagSelector struct {
	tag string
}

// Matches elements with a given tag name.
func ( tagSelector) ( *html.Node) bool {
	return .Type == html.ElementNode && .Data == .tag
}

func ( tagSelector) () Specificity {
	return Specificity{0, 0, 1}
}

func ( tagSelector) () string {
	return ""
}

type classSelector struct {
	class string
}

// Matches elements by class attribute.
func ( classSelector) ( *html.Node) bool {
	return matchAttribute(, "class", func( string) bool {
		return matchInclude(.class, , false)
	})
}

func ( classSelector) () Specificity {
	return Specificity{0, 1, 0}
}

func ( classSelector) () string {
	return ""
}

type idSelector struct {
	id string
}

// Matches elements by id attribute.
func ( idSelector) ( *html.Node) bool {
	return matchAttribute(, "id", func( string) bool {
		return  == .id
	})
}

func ( idSelector) () Specificity {
	return Specificity{1, 0, 0}
}

func ( idSelector) () string {
	return ""
}

type attrSelector struct {
	key, val, operation string
	regexp              *regexp.Regexp
	insensitive         bool
}

// Matches elements by attribute value.
func ( attrSelector) ( *html.Node) bool {
	switch .operation {
	case "":
		return matchAttribute(, .key, func(string) bool { return true })
	case "=":
		return matchAttribute(, .key, func( string) bool { return matchInsensitiveValue(, .val, .insensitive) })
	case "!=":
		return attributeNotEqualMatch(.key, .val, , .insensitive)
	case "~=":
		// matches elements where the attribute named key is a whitespace-separated list that includes val.
		return matchAttribute(, .key, func( string) bool { return matchInclude(.val, , .insensitive) })
	case "|=":
		return attributeDashMatch(.key, .val, , .insensitive)
	case "^=":
		return attributePrefixMatch(.key, .val, , .insensitive)
	case "$=":
		return attributeSuffixMatch(.key, .val, , .insensitive)
	case "*=":
		return attributeSubstringMatch(.key, .val, , .insensitive)
	case "#=":
		return attributeRegexMatch(.key, .regexp, )
	default:
		panic(fmt.Sprintf("unsuported operation : %s", .operation))
	}
}

// matches elements where we ignore (or not) the case of the attribute value
// the user attribute is the value set by the user to match elements
// the real attribute is the attribute value found in the code parsed
func matchInsensitiveValue( string,  string,  bool) bool {
	if  {
		return strings.EqualFold(, )
	}
	return  == 

}

// matches elements where the attribute named key satisifes the function f.
func matchAttribute( *html.Node,  string,  func(string) bool) bool {
	if .Type != html.ElementNode {
		return false
	}
	for ,  := range .Attr {
		if .Key ==  && (.Val) {
			return true
		}
	}
	return false
}

// attributeNotEqualMatch matches elements where
// the attribute named key does not have the value val.
func attributeNotEqualMatch(,  string,  *html.Node,  bool) bool {
	if .Type != html.ElementNode {
		return false
	}
	for ,  := range .Attr {
		if .Key ==  && matchInsensitiveValue(.Val, , ) {
			return false
		}
	}
	return true
}

// returns true if s is a whitespace-separated list that includes val.
func matchInclude( string,  string,  bool) bool {
	for  != "" {
		 := strings.IndexAny(, " \t\r\n\f")
		if  == -1 {
			return matchInsensitiveValue(, , )
		}
		if matchInsensitiveValue([:], , ) {
			return true
		}
		 = [+1:]
	}
	return false
}

//  matches elements where the attribute named key equals val or starts with val plus a hyphen.
func attributeDashMatch(,  string,  *html.Node,  bool) bool {
	return matchAttribute(, ,
		func( string) bool {
			if matchInsensitiveValue(, , ) {
				return true
			}
			if len() <= len() {
				return false
			}
			if matchInsensitiveValue([:len()], , ) && [len()] == '-' {
				return true
			}
			return false
		})
}

// attributePrefixMatch returns a Selector that matches elements where
// the attribute named key starts with val.
func attributePrefixMatch(,  string,  *html.Node,  bool) bool {
	return matchAttribute(, ,
		func( string) bool {
			if strings.TrimSpace() == "" {
				return false
			}
			if  {
				return strings.HasPrefix(strings.ToLower(), strings.ToLower())
			}
			return strings.HasPrefix(, )
		})
}

// attributeSuffixMatch matches elements where
// the attribute named key ends with val.
func attributeSuffixMatch(,  string,  *html.Node,  bool) bool {
	return matchAttribute(, ,
		func( string) bool {
			if strings.TrimSpace() == "" {
				return false
			}
			if  {
				return strings.HasSuffix(strings.ToLower(), strings.ToLower())
			}
			return strings.HasSuffix(, )
		})
}

// attributeSubstringMatch matches nodes where
// the attribute named key contains val.
func attributeSubstringMatch(,  string,  *html.Node,  bool) bool {
	return matchAttribute(, ,
		func( string) bool {
			if strings.TrimSpace() == "" {
				return false
			}
			if  {
				return strings.Contains(strings.ToLower(), strings.ToLower())
			}
			return strings.Contains(, )
		})
}

// attributeRegexMatch  matches nodes where
// the attribute named key matches the regular expression rx
func attributeRegexMatch( string,  *regexp.Regexp,  *html.Node) bool {
	return matchAttribute(, ,
		func( string) bool {
			return .MatchString()
		})
}

func ( attrSelector) () Specificity {
	return Specificity{0, 1, 0}
}

func ( attrSelector) () string {
	return ""
}

// see pseudo_classes.go for pseudo classes selectors

// on a static context, some selectors can't match anything
type neverMatchSelector struct {
	value string
}

func ( neverMatchSelector) ( *html.Node) bool {
	return false
}

func ( neverMatchSelector) () Specificity {
	return Specificity{0, 0, 0}
}

func ( neverMatchSelector) () string {
	return ""
}

type compoundSelector struct {
	selectors     []Sel
	pseudoElement string
}

// Matches elements if each sub-selectors matches.
func ( compoundSelector) ( *html.Node) bool {
	if len(.selectors) == 0 {
		return .Type == html.ElementNode
	}

	for ,  := range .selectors {
		if !.Match() {
			return false
		}
	}
	return true
}

func ( compoundSelector) () Specificity {
	var  Specificity
	for ,  := range .selectors {
		 = .Add(.Specificity())
	}
	if .pseudoElement != "" {
		// https://drafts.csswg.org/selectors-3/#specificity
		 = .Add(Specificity{0, 0, 1})
	}
	return 
}

func ( compoundSelector) () string {
	return .pseudoElement
}

type combinedSelector struct {
	first      Sel
	combinator byte
	second     Sel
}

func ( combinedSelector) ( *html.Node) bool {
	if .first == nil {
		return false // maybe we should panic
	}
	switch .combinator {
	case 0:
		return .first.Match()
	case ' ':
		return descendantMatch(.first, .second, )
	case '>':
		return childMatch(.first, .second, )
	case '+':
		return siblingMatch(.first, .second, true, )
	case '~':
		return siblingMatch(.first, .second, false, )
	default:
		panic("unknown combinator")
	}
}

// matches an element if it matches d and has an ancestor that matches a.
func descendantMatch(,  Matcher,  *html.Node) bool {
	if !.Match() {
		return false
	}

	for  := .Parent;  != nil;  = .Parent {
		if .Match() {
			return true
		}
	}

	return false
}

// matches an element if it matches d and its parent matches a.
func childMatch(,  Matcher,  *html.Node) bool {
	return .Match() && .Parent != nil && .Match(.Parent)
}

// matches an element if it matches s2 and is preceded by an element that matches s1.
// If adjacent is true, the sibling must be immediately before the element.
func siblingMatch(,  Matcher,  bool,  *html.Node) bool {
	if !.Match() {
		return false
	}

	if  {
		for  = .PrevSibling;  != nil;  = .PrevSibling {
			if .Type == html.TextNode || .Type == html.CommentNode {
				continue
			}
			return .Match()
		}
		return false
	}

	// Walk backwards looking for element that matches s1
	for  := .PrevSibling;  != nil;  = .PrevSibling {
		if .Match() {
			return true
		}
	}

	return false
}

func ( combinedSelector) () Specificity {
	 := .first.Specificity()
	if .second != nil {
		 = .Add(.second.Specificity())
	}
	return 
}

// on combinedSelector, a pseudo-element only makes sens on the last
// selector, although others increase specificity.
func ( combinedSelector) () string {
	if .second == nil {
		return ""
	}
	return .second.PseudoElement()
}

// A SelectorGroup is a list of selectors, which matches if any of the
// individual selectors matches.
type SelectorGroup []Sel

// Match returns true if the node matches one of the single selectors.
func ( SelectorGroup) ( *html.Node) bool {
	for ,  := range  {
		if .Match() {
			return true
		}
	}
	return false
}