package ui

import (
	
)

// Styling specifies how to change a Style. It can also be applied to a Segment
// or Text.
type Styling interface{ transform(*Style) }

// StyleText returns a new Text with the given Styling's applied. It does not
// modify the given Text.
func ( Text,  ...Styling) Text {
	 := make(Text, len())
	for ,  := range  {
		[] = StyleSegment(, ...)
	}
	return 
}

// StyleSegment returns a new Segment with the given Styling's applied. It does
// not modify the given Segment.
func ( *Segment,  ...Styling) *Segment {
	return &Segment{Text: .Text, Style: ApplyStyling(.Style, ...)}
}

// ApplyStyling returns a new Style with the given Styling's applied.
func ( Style,  ...Styling) Style {
	for ,  := range  {
		if  != nil {
			.transform(&)
		}
	}
	return 
}

// Stylings joins several transformers into one.
func ( ...Styling) Styling { return jointStyling() }

// Common stylings.
var (
	Reset Styling = reset{}

	FgDefault Styling = setForeground{nil}

	FgBlack   Styling = setForeground{Black}
	FgRed     Styling = setForeground{Red}
	FgGreen   Styling = setForeground{Green}
	FgYellow  Styling = setForeground{Yellow}
	FgBlue    Styling = setForeground{Blue}
	FgMagenta Styling = setForeground{Magenta}
	FgCyan    Styling = setForeground{Cyan}
	FgWhite   Styling = setForeground{White}

	FgBrightBlack   Styling = setForeground{BrightBlack}
	FgBrightRed     Styling = setForeground{BrightRed}
	FgBrightGreen   Styling = setForeground{BrightGreen}
	FgBrightYellow  Styling = setForeground{BrightYellow}
	FgBrightBlue    Styling = setForeground{BrightBlue}
	FgBrightMagenta Styling = setForeground{BrightMagenta}
	FgBrightCyan    Styling = setForeground{BrightCyan}
	FgBrightWhite   Styling = setForeground{BrightWhite}

	BgDefault Styling = setBackground{nil}

	BgBlack   Styling = setBackground{Black}
	BgRed     Styling = setBackground{Red}
	BgGreen   Styling = setBackground{Green}
	BgYellow  Styling = setBackground{Yellow}
	BgBlue    Styling = setBackground{Blue}
	BgMagenta Styling = setBackground{Magenta}
	BgCyan    Styling = setBackground{Cyan}
	BgWhite   Styling = setBackground{White}

	BgBrightBlack   Styling = setBackground{BrightBlack}
	BgBrightRed     Styling = setBackground{BrightRed}
	BgBrightGreen   Styling = setBackground{BrightGreen}
	BgBrightYellow  Styling = setBackground{BrightYellow}
	BgBrightBlue    Styling = setBackground{BrightBlue}
	BgBrightMagenta Styling = setBackground{BrightMagenta}
	BgBrightCyan    Styling = setBackground{BrightCyan}
	BgBrightWhite   Styling = setBackground{BrightWhite}

	Bold       Styling = boolOn{boldField{}}
	Dim        Styling = boolOn{dimField{}}
	Italic     Styling = boolOn{italicField{}}
	Underlined Styling = boolOn{underlinedField{}}
	Blink      Styling = boolOn{blinkField{}}
	Inverse    Styling = boolOn{inverseField{}}

	NoBold       Styling = boolOff{boldField{}}
	NoDim        Styling = boolOff{dimField{}}
	NoItalic     Styling = boolOff{italicField{}}
	NoUnderlined Styling = boolOff{underlinedField{}}
	NoBlink      Styling = boolOff{blinkField{}}
	NoInverse    Styling = boolOff{inverseField{}}

	ToggleBold       Styling = boolToggle{boldField{}}
	ToggleDim        Styling = boolToggle{dimField{}}
	ToggleItalic     Styling = boolToggle{italicField{}}
	ToggleUnderlined Styling = boolToggle{underlinedField{}}
	ToggleBlink      Styling = boolToggle{blinkField{}}
	ToggleInverse    Styling = boolToggle{inverseField{}}
)

// Fg returns a Styling that sets the foreground color.
func ( Color) Styling { return setForeground{} }

// Bg returns a Styling that sets the background color.
func ( Color) Styling { return setBackground{} }

type reset struct{}
type setForeground struct{ c Color }
type setBackground struct{ c Color }
type boolOn struct{ f boolField }
type boolOff struct{ f boolField }
type boolToggle struct{ f boolField }

func (reset) ( *Style)           { * = Style{} }
func ( setForeground) ( *Style) { .Foreground = .c }
func ( setBackground) ( *Style) { .Background = .c }
func ( boolOn) ( *Style)        { *.f.get() = true }
func ( boolOff) ( *Style)       { *.f.get() = false }
func ( boolToggle) ( *Style)    {  := .f.get(); * = !* }

type boolField interface{ get(*Style) *bool }

type boldField struct{}
type dimField struct{}
type italicField struct{}
type underlinedField struct{}
type blinkField struct{}
type inverseField struct{}

func (boldField) ( *Style) *bool       { return &.Bold }
func (dimField) ( *Style) *bool        { return &.Dim }
func (italicField) ( *Style) *bool     { return &.Italic }
func (underlinedField) ( *Style) *bool { return &.Underlined }
func (blinkField) ( *Style) *bool      { return &.Blink }
func (inverseField) ( *Style) *bool    { return &.Inverse }

type jointStyling []Styling

func ( jointStyling) ( *Style) {
	for ,  := range  {
		.transform()
	}
}

// ParseStyling parses a text representation of Styling, which are kebab
// case counterparts to the names of the builtin Styling's. For example,
// ToggleInverse is expressed as "toggle-inverse".
//
// Multiple stylings can be joined by spaces, which is equivalent to calling
// Stylings.
//
// If the given string is invalid, ParseStyling returns nil.
func ( string) Styling {
	if !strings.ContainsRune(, ' ') {
		return parseOneStyling()
	}
	var  jointStyling
	for ,  := range strings.Split(, " ") {
		 := parseOneStyling()
		if  == nil {
			return nil
		}
		 = append(, parseOneStyling())
	}
	return 
}

var boolFields = map[string]boolField{
	"bold":       boldField{},
	"dim":        dimField{},
	"italic":     italicField{},
	"underlined": underlinedField{},
	"blink":      blinkField{},
	"inverse":    inverseField{},
}

func ( string) Styling {
	switch {
	case  == "default" ||  == "fg-default":
		return FgDefault
	case strings.HasPrefix(, "fg-"):
		if  := parseColor([len("fg-"):]);  != nil {
			return setForeground{}
		}
	case  == "bg-default":
		return BgDefault
	case strings.HasPrefix(, "bg-"):
		if  := parseColor([len("bg-"):]);  != nil {
			return setBackground{}
		}
	case strings.HasPrefix(, "no-"):
		if ,  := boolFields[[len("no-"):]];  {
			return boolOff{}
		}
	case strings.HasPrefix(, "toggle-"):
		if ,  := boolFields[[len("toggle-"):]];  {
			return boolToggle{}
		}
	default:
		if ,  := boolFields[];  {
			return boolOn{}
		}
		if  := parseColor();  != nil {
			return setForeground{}
		}
	}
	return nil
}