package chroma

import (
	
	
	
	
	
)

// Trilean value for StyleEntry value inheritance.
type Trilean uint8

// Trilean states.
const (
	Pass Trilean = iota
	Yes
	No
)

func ( Trilean) () string {
	switch  {
	case Yes:
		return "Yes"
	case No:
		return "No"
	default:
		return "Pass"
	}
}

// Prefix returns s with "no" as a prefix if Trilean is no.
func ( Trilean) ( string) string {
	if  == Yes {
		return 
	} else if  == No {
		return "no" + 
	}
	return ""
}

// A StyleEntry in the Style map.
type StyleEntry struct {
	// Hex colours.
	Colour     Colour
	Background Colour
	Border     Colour

	Bold      Trilean
	Italic    Trilean
	Underline Trilean
	NoInherit bool
}

func ( StyleEntry) () ([]byte, error) {
	return []byte(.String()), nil
}

func ( StyleEntry) () string {
	 := []string{}
	if .Bold != Pass {
		 = append(, .Bold.Prefix("bold"))
	}
	if .Italic != Pass {
		 = append(, .Italic.Prefix("italic"))
	}
	if .Underline != Pass {
		 = append(, .Underline.Prefix("underline"))
	}
	if .NoInherit {
		 = append(, "noinherit")
	}
	if .Colour.IsSet() {
		 = append(, .Colour.String())
	}
	if .Background.IsSet() {
		 = append(, "bg:"+.Background.String())
	}
	if .Border.IsSet() {
		 = append(, "border:"+.Border.String())
	}
	return strings.Join(, " ")
}

// Sub subtracts e from s where elements match.
func ( StyleEntry) ( StyleEntry) StyleEntry {
	 := StyleEntry{}
	if .Colour != .Colour {
		.Colour = .Colour
	}
	if .Background != .Background {
		.Background = .Background
	}
	if .Bold != .Bold {
		.Bold = .Bold
	}
	if .Italic != .Italic {
		.Italic = .Italic
	}
	if .Underline != .Underline {
		.Underline = .Underline
	}
	if .Border != .Border {
		.Border = .Border
	}
	return 
}

// Inherit styles from ancestors.
//
// Ancestors should be provided from oldest to newest.
func ( StyleEntry) ( ...StyleEntry) StyleEntry {
	 := 
	for  := len() - 1;  >= 0; -- {
		if .NoInherit {
			return 
		}
		 := []
		if !.Colour.IsSet() {
			.Colour = .Colour
		}
		if !.Background.IsSet() {
			.Background = .Background
		}
		if !.Border.IsSet() {
			.Border = .Border
		}
		if .Bold == Pass {
			.Bold = .Bold
		}
		if .Italic == Pass {
			.Italic = .Italic
		}
		if .Underline == Pass {
			.Underline = .Underline
		}
	}
	return 
}

func ( StyleEntry) () bool {
	return .Colour == 0 && .Background == 0 && .Border == 0 && .Bold == Pass && .Italic == Pass &&
		.Underline == Pass && !.NoInherit
}

// A StyleBuilder is a mutable structure for building styles.
//
// Once built, a Style is immutable.
type StyleBuilder struct {
	entries map[TokenType]string
	name    string
	parent  *Style
}

func ( string) *StyleBuilder {
	return &StyleBuilder{name: , entries: map[TokenType]string{}}
}

func ( *StyleBuilder) ( StyleEntries) *StyleBuilder {
	for ,  := range  {
		.entries[] = 
	}
	return 
}

func ( *StyleBuilder) ( TokenType) StyleEntry {
	// This is less than ideal, but it's the price for not having to check errors on each Add().
	,  := ParseStyleEntry(.entries[])
	if .parent != nil {
		 = .Inherit(.parent.Get())
	}
	return 
}

// Add an entry to the Style map.
//
// See http://pygments.org/docs/styles/#style-rules for details.
func ( *StyleBuilder) ( TokenType,  string) *StyleBuilder { // nolint: gocyclo
	.entries[] = 
	return 
}

func ( *StyleBuilder) ( TokenType,  StyleEntry) *StyleBuilder {
	.entries[] = .String()
	return 
}

// Transform passes each style entry currently defined in the builder to the supplied
// function and saves the returned value. This can be used to adjust a style's colours;
// see Colour's ClampBrightness function, for example.
func ( *StyleBuilder) ( func(StyleEntry) StyleEntry) *StyleBuilder {
	 := make(map[TokenType]struct{})
	for  := range .entries {
		[] = struct{}{}
	}
	if .parent != nil {
		for ,  := range .parent.Types() {
			[] = struct{}{}
		}
	}
	for  := range  {
		.AddEntry(, (.Get()))
	}
	return 
}

func ( *StyleBuilder) () (*Style, error) {
	 := &Style{
		Name:    .name,
		entries: map[TokenType]StyleEntry{},
		parent:  .parent,
	}
	for ,  := range .entries {
		,  := ParseStyleEntry()
		if  != nil {
			return nil, fmt.Errorf("invalid entry for %s: %s", , )
		}
		.entries[] = 
	}
	return , nil
}

// StyleEntries mapping TokenType to colour definition.
type StyleEntries map[TokenType]string

// NewXMLStyle parses an XML style definition.
func ( io.Reader) (*Style, error) {
	 := xml.NewDecoder()
	 := &Style{}
	return , .Decode()
}

// MustNewXMLStyle is like NewXMLStyle but panics on error.
func ( io.Reader) *Style {
	,  := NewXMLStyle()
	if  != nil {
		panic()
	}
	return 
}

// NewStyle creates a new style definition.
func ( string,  StyleEntries) (*Style, error) {
	return NewStyleBuilder().AddAll().Build()
}

// MustNewStyle creates a new style or panics.
func ( string,  StyleEntries) *Style {
	,  := NewStyle(, )
	if  != nil {
		panic()
	}
	return 
}

// A Style definition.
//
// See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical.
type Style struct {
	Name    string
	entries map[TokenType]StyleEntry
	parent  *Style
}

func ( *Style) ( *xml.Encoder,  xml.StartElement) error {
	if .parent != nil {
		return fmt.Errorf("cannot marshal style with parent")
	}
	.Name = xml.Name{Local: "style"}
	.Attr = []xml.Attr{{Name: xml.Name{Local: "name"}, Value: .Name}}
	if  := .EncodeToken();  != nil {
		return 
	}
	 := make([]TokenType, 0, len(.entries))
	for  := range .entries {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool { return [] < [] })
	for ,  := range  {
		 := .entries[]
		 := xml.StartElement{Name: xml.Name{Local: "entry"}}
		.Attr = []xml.Attr{
			{Name: xml.Name{Local: "type"}, Value: .String()},
			{Name: xml.Name{Local: "style"}, Value: .String()},
		}
		if  := .EncodeToken();  != nil {
			return 
		}
		if  := .EncodeToken(xml.EndElement{Name: .Name});  != nil {
			return 
		}
	}
	return .EncodeToken(xml.EndElement{Name: .Name})
}

func ( *Style) ( *xml.Decoder,  xml.StartElement) error {
	for ,  := range .Attr {
		if .Name.Local == "name" {
			.Name = .Value
		} else {
			return fmt.Errorf("unexpected attribute %s", .Name.Local)
		}
	}
	if .Name == "" {
		return fmt.Errorf("missing style name attribute")
	}
	.entries = map[TokenType]StyleEntry{}
	for {
		,  := .Token()
		if  != nil {
			return 
		}
		switch el := .(type) {
		case xml.StartElement:
			if .Name.Local != "entry" {
				return fmt.Errorf("unexpected element %s", .Name.Local)
			}
			var  TokenType
			var  StyleEntry
			for ,  := range .Attr {
				switch .Name.Local {
				case "type":
					,  = TokenTypeString(.Value)
					if  != nil {
						return 
					}

				case "style":
					,  = ParseStyleEntry(.Value)
					if  != nil {
						return 
					}

				default:
					return fmt.Errorf("unexpected attribute %s", .Name.Local)
				}
			}
			.entries[] = 

		case xml.EndElement:
			if .Name.Local == .Name.Local {
				return nil
			}
		}
	}
}

// Types that are styled.
func ( *Style) () []TokenType {
	 := map[TokenType]bool{}
	for  := range .entries {
		[] = true
	}
	if .parent != nil {
		for ,  := range .parent.() {
			[] = true
		}
	}
	 := make([]TokenType, 0, len())
	for  := range  {
		 = append(, )
	}
	return 
}

// Builder creates a mutable builder from this Style.
//
// The builder can then be safely modified. This is a cheap operation.
func ( *Style) () *StyleBuilder {
	return &StyleBuilder{
		name:    .Name,
		entries: map[TokenType]string{},
		parent:  ,
	}
}

// Has checks if an exact style entry match exists for a token type.
//
// This is distinct from Get() which will merge parent tokens.
func ( *Style) ( TokenType) bool {
	return !.get().IsZero() || .synthesisable()
}

// Get a style entry. Will try sub-category or category if an exact match is not found, and
// finally return the Background.
func ( *Style) ( TokenType) StyleEntry {
	return .get().Inherit(
		.get(Background),
		.get(Text),
		.get(.Category()),
		.get(.SubCategory()))
}

func ( *Style) ( TokenType) StyleEntry {
	 := .entries[]
	if .IsZero() && .parent != nil {
		return .parent.()
	}
	if .IsZero() && .synthesisable() {
		 = .synthesise()
	}
	return 
}

func ( *Style) ( TokenType) StyleEntry {
	 := .get(Background)
	 := StyleEntry{Colour: .Colour}
	.Colour = .Colour.BrightenOrDarken(0.5)

	switch  {
	// If we don't have a line highlight colour, make one that is 10% brighter/darker than the background.
	case LineHighlight:
		return StyleEntry{Background: .Background.BrightenOrDarken(0.1)}

	// If we don't have line numbers, use the text colour but 20% brighter/darker
	case LineNumbers, LineNumbersTable:
		return 

	default:
		return StyleEntry{}
	}
}

func ( *Style) ( TokenType) bool {
	return  == LineHighlight ||  == LineNumbers ||  == LineNumbersTable
}

// MustParseStyleEntry parses a Pygments style entry or panics.
func ( string) StyleEntry {
	,  := ParseStyleEntry()
	if  != nil {
		panic()
	}
	return 
}

// ParseStyleEntry parses a Pygments style entry.
func ( string) (StyleEntry, error) { // nolint: gocyclo
	 := StyleEntry{}
	 := strings.Fields()
	for ,  := range  {
		switch {
		case  == "italic":
			.Italic = Yes
		case  == "noitalic":
			.Italic = No
		case  == "bold":
			.Bold = Yes
		case  == "nobold":
			.Bold = No
		case  == "underline":
			.Underline = Yes
		case  == "nounderline":
			.Underline = No
		case  == "inherit":
			.NoInherit = false
		case  == "noinherit":
			.NoInherit = true
		case  == "bg:":
			.Background = 0
		case strings.HasPrefix(, "bg:#"):
			.Background = ParseColour([3:])
			if !.Background.IsSet() {
				return StyleEntry{}, fmt.Errorf("invalid background colour %q", )
			}
		case strings.HasPrefix(, "border:#"):
			.Border = ParseColour([7:])
			if !.Border.IsSet() {
				return StyleEntry{}, fmt.Errorf("invalid border colour %q", )
			}
		case strings.HasPrefix(, "#"):
			.Colour = ParseColour()
			if !.Colour.IsSet() {
				return StyleEntry{}, fmt.Errorf("invalid colour %q", )
			}
		default:
			return StyleEntry{}, fmt.Errorf("unknown style element %q", )
		}
	}
	return , nil
}