package config

import (
	"errors"
	"fmt"
	"maps"
	"os"
	"regexp"
	"strconv"
	"strings"

	"git.sr.ht/~rjarry/aerc/lib/xdg"
	"git.sr.ht/~rockorager/vaxis"
	"github.com/emersion/go-message/mail"
	"github.com/go-ini/ini"
)

type StyleObject int32

const (
	STYLE_DEFAULT StyleObject = iota
	STYLE_ERROR
	STYLE_WARNING
	STYLE_SUCCESS

	STYLE_TITLE
	STYLE_HEADER

	STYLE_STATUSLINE_DEFAULT
	STYLE_STATUSLINE_ERROR
	STYLE_STATUSLINE_WARNING
	STYLE_STATUSLINE_SUCCESS

	STYLE_MSGLIST_DEFAULT
	STYLE_MSGLIST_UNREAD
	STYLE_MSGLIST_READ
	STYLE_MSGLIST_FLAGGED
	STYLE_MSGLIST_DELETED
	STYLE_MSGLIST_MARKED
	STYLE_MSGLIST_RESULT
	STYLE_MSGLIST_ANSWERED
	STYLE_MSGLIST_FORWARDED
	STYLE_MSGLIST_THREAD_FOLDED
	STYLE_MSGLIST_GUTTER
	STYLE_MSGLIST_PILL
	STYLE_MSGLIST_THREAD_CONTEXT
	STYLE_MSGLIST_THREAD_ORPHAN

	STYLE_DIRLIST_DEFAULT
	STYLE_DIRLIST_UNREAD
	STYLE_DIRLIST_RECENT

	STYLE_PART_SWITCHER
	STYLE_PART_FILENAME
	STYLE_PART_MIMETYPE

	STYLE_COMPLETION_DEFAULT
	STYLE_COMPLETION_DESCRIPTION
	STYLE_COMPLETION_GUTTER
	STYLE_COMPLETION_PILL

	STYLE_TAB
	STYLE_STACK
	STYLE_SPINNER
	STYLE_BORDER

	STYLE_SELECTOR_DEFAULT
	STYLE_SELECTOR_FOCUSED
	STYLE_SELECTOR_CHOOSER
)

var StyleNames = map[string]StyleObject{
	"default": STYLE_DEFAULT,
	"error":   STYLE_ERROR,
	"warning": STYLE_WARNING,
	"success": STYLE_SUCCESS,

	"title":  STYLE_TITLE,
	"header": STYLE_HEADER,

	"statusline_default": STYLE_STATUSLINE_DEFAULT,
	"statusline_error":   STYLE_STATUSLINE_ERROR,
	"statusline_warning": STYLE_STATUSLINE_WARNING,
	"statusline_success": STYLE_STATUSLINE_SUCCESS,

	"msglist_default":   STYLE_MSGLIST_DEFAULT,
	"msglist_unread":    STYLE_MSGLIST_UNREAD,
	"msglist_read":      STYLE_MSGLIST_READ,
	"msglist_flagged":   STYLE_MSGLIST_FLAGGED,
	"msglist_deleted":   STYLE_MSGLIST_DELETED,
	"msglist_marked":    STYLE_MSGLIST_MARKED,
	"msglist_result":    STYLE_MSGLIST_RESULT,
	"msglist_answered":  STYLE_MSGLIST_ANSWERED,
	"msglist_forwarded": STYLE_MSGLIST_FORWARDED,
	"msglist_gutter":    STYLE_MSGLIST_GUTTER,
	"msglist_pill":      STYLE_MSGLIST_PILL,

	"msglist_thread_folded":  STYLE_MSGLIST_THREAD_FOLDED,
	"msglist_thread_context": STYLE_MSGLIST_THREAD_CONTEXT,
	"msglist_thread_orphan":  STYLE_MSGLIST_THREAD_ORPHAN,

	"dirlist_default": STYLE_DIRLIST_DEFAULT,
	"dirlist_unread":  STYLE_DIRLIST_UNREAD,
	"dirlist_recent":  STYLE_DIRLIST_RECENT,

	"part_switcher": STYLE_PART_SWITCHER,
	"part_filename": STYLE_PART_FILENAME,
	"part_mimetype": STYLE_PART_MIMETYPE,

	"completion_default":     STYLE_COMPLETION_DEFAULT,
	"completion_description": STYLE_COMPLETION_DESCRIPTION,
	"completion_gutter":      STYLE_COMPLETION_GUTTER,
	"completion_pill":        STYLE_COMPLETION_PILL,

	"tab":     STYLE_TAB,
	"stack":   STYLE_STACK,
	"spinner": STYLE_SPINNER,
	"border":  STYLE_BORDER,

	"selector_default": STYLE_SELECTOR_DEFAULT,
	"selector_focused": STYLE_SELECTOR_FOCUSED,
	"selector_chooser": STYLE_SELECTOR_CHOOSER,
}

type StyleHeaderPattern struct {
	RawPattern string
	Re         *regexp.Regexp
}

type Style struct {
	Fg        vaxis.Color
	Bg        vaxis.Color
	Bold      bool
	Blink     bool
	Underline bool
	Reverse   bool
	Italic    bool
	Dim       bool
	// Only for msglist, maps header -> pattern/regexp
	// All regexps must match in order for the style to be applied
	headerPatterns map[string]*StyleHeaderPattern
}

func (s Style) Get() vaxis.Style {
	vx := vaxis.Style{
		Foreground: s.Fg,
		Background: s.Bg,
	}
	if s.Bold {
		vx.Attribute |= vaxis.AttrBold
	}
	if s.Blink {
		vx.Attribute |= vaxis.AttrBlink
	}
	if s.Underline {
		vx.UnderlineStyle |= vaxis.UnderlineSingle
	}
	if s.Reverse {
		vx.Attribute |= vaxis.AttrReverse
	}
	if s.Italic {
		vx.Attribute |= vaxis.AttrItalic
	}
	if s.Dim {
		vx.Attribute |= vaxis.AttrDim
	}
	return vx
}

func (s *Style) Normal() {
	s.Bold = false
	s.Blink = false
	s.Underline = false
	s.Reverse = false
	s.Italic = false
	s.Dim = false
}

func (s *Style) Default() *Style {
	s.Fg = 0
	s.Bg = 0
	return s
}

func (s *Style) Reset() *Style {
	s.Default()
	s.Normal()
	return s
}

func (s *Style) hasSameHeaderPatterns(other map[string]*StyleHeaderPattern) bool {
	return maps.EqualFunc(s.headerPatterns, other, func(a, b *StyleHeaderPattern) bool {
		return a.RawPattern == b.RawPattern
	})
}

func boolSwitch(val string, cur_val bool) (bool, error) {
	switch val {
	case "true":
		return true, nil
	case "false":
		return false, nil
	case "toggle":
		return !cur_val, nil
	default:
		return cur_val, errors.New(
			"Bool Switch attribute must be true, false, or toggle")
	}
}

func extractColor(val string) vaxis.Color {
	// Check if the string can be interpreted as a number, indicating a
	// reference to the color number. Otherwise retrieve the number based
	// on the name.
	if i, err := strconv.ParseUint(val, 10, 8); err == nil {
		return vaxis.IndexColor(uint8(i))
	}
	if cut, found := strings.CutPrefix(val, "#"); found {
		val = cut
		hex, err := strconv.ParseUint(val, 16, 32)
		if err != nil {
			return 0
		}
		return vaxis.HexColor(uint32(hex))
	}
	return colorNames[val]
}

func (s *Style) Set(attr, val string) error {
	switch attr {
	case "fg":
		s.Fg = extractColor(val)
	case "bg":
		s.Bg = extractColor(val)
	case "bold":
		if state, err := boolSwitch(val, s.Bold); err != nil {
			return err
		} else {
			s.Bold = state
		}
	case "blink":
		if state, err := boolSwitch(val, s.Blink); err != nil {
			return err
		} else {
			s.Blink = state
		}
	case "underline":
		if state, err := boolSwitch(val, s.Underline); err != nil {
			return err
		} else {
			s.Underline = state
		}
	case "reverse":
		if state, err := boolSwitch(val, s.Reverse); err != nil {
			return err
		} else {
			s.Reverse = state
		}
	case "italic":
		if state, err := boolSwitch(val, s.Italic); err != nil {
			return err
		} else {
			s.Italic = state
		}
	case "dim":
		if state, err := boolSwitch(val, s.Dim); err != nil {
			return err
		} else {
			s.Dim = state
		}
	case "default":
		s.Default()
	case "normal":
		s.Normal()
	default:
		return errors.New("Unknown style attribute: " + attr)
	}

	return nil
}

func (s Style) composeWith(styles []*Style) Style {
	newStyle := s
	for _, st := range styles {
		if st.Fg != s.Fg && st.Fg != 0 {
			newStyle.Fg = st.Fg
		}
		if st.Bg != s.Bg && st.Bg != 0 {
			newStyle.Bg = st.Bg
		}
		if st.Bold != s.Bold {
			newStyle.Bold = st.Bold
		}
		if st.Blink != s.Blink {
			newStyle.Blink = st.Blink
		}
		if st.Underline != s.Underline {
			newStyle.Underline = st.Underline
		}
		if st.Reverse != s.Reverse {
			newStyle.Reverse = st.Reverse
		}
		if st.Italic != s.Italic {
			newStyle.Italic = st.Italic
		}
		if st.Dim != s.Dim {
			newStyle.Dim = st.Dim
		}
	}
	return newStyle
}

type StyleConf struct {
	base    Style
	dynamic []Style
}

type StyleSet struct {
	objects  map[StyleObject]*StyleConf
	selected map[StyleObject]*StyleConf
	user     map[string]*Style
	paths    []string
}

const defaultStyleset string = `
*.selected.bg = 12
*.selected.fg = 15
*.selected.bold = true
statusline_*.dim = true
*warning.dim = false
*warning.bold = true
*warning.fg = 11
*success.dim = false
*success.bold = true
*success.fg = 10
*error.dim = false
*error.bold = true
*error.fg = 9
border.fg = 12
border.bold = true
title.bg = 12
title.fg = 15
title.bold = true
header.fg = 4
header.bold = true
msglist_unread.bold = true
msglist_deleted.dim = true
msglist_marked.bg = 6
msglist_marked.fg = 15
msglist_pill.bg = 12
msglist_pill.fg = 15
part_mimetype.fg = 12
selector_chooser.bold = true
selector_focused.bold = true
selector_focused.bg = 12
selector_focused.fg = 15
completion_*.bg = 8
completion_pill.bg = 12
completion_default.fg = 15
completion_description.fg = 15
completion_description.dim = true
`

func NewStyleSet() StyleSet {
	ss := StyleSet{
		objects:  make(map[StyleObject]*StyleConf),
		selected: make(map[StyleObject]*StyleConf),
		user:     make(map[string]*Style),
	}
	for _, so := range StyleNames {
		ss.objects[so] = new(StyleConf)
		ss.selected[so] = new(StyleConf)
	}
	f, err := ini.Load([]byte(defaultStyleset))
	if err == nil {
		err = ss.ParseStyleSet(f)
	}
	if err != nil {
		panic(err)
	}
	return ss
}

func (c *StyleConf) getStyle(h *mail.Header) *Style {
	if h == nil {
		return &c.base
	}
	style := &c.base

	// All dynamic styles must be iterated through, as later ones might be a
	// narrower match based due to multiple header patterns.
	for _, s := range c.dynamic {
		allMatch := true
		for header, pattern := range s.headerPatterns {
			val, _ := h.Text(header)
			allMatch = allMatch && pattern.Re.MatchString(val)
		}

		if allMatch {
			s := c.base.composeWith([]*Style{&s})
			style = &s
		}
	}
	return style
}

func (ss StyleSet) Get(so StyleObject, h *mail.Header) vaxis.Style {
	return ss.objects[so].getStyle(h).Get()
}

func (ss StyleSet) Selected(so StyleObject, h *mail.Header) vaxis.Style {
	return ss.selected[so].getStyle(h).Get()
}

func (ss StyleSet) UserStyle(name string) vaxis.Style {
	if style, found := ss.user[name]; found {
		return style.Get()
	}
	return vaxis.Style{}
}

func (ss StyleSet) Compose(
	so StyleObject, sos []StyleObject, h *mail.Header,
) vaxis.Style {
	base := *ss.objects[so].getStyle(h)
	styles := make([]*Style, len(sos))
	for i, so := range sos {
		styles[i] = ss.objects[so].getStyle(h)
	}

	return base.composeWith(styles).Get()
}

func (ss StyleSet) ComposeSelected(
	so StyleObject, sos []StyleObject, h *mail.Header,
) vaxis.Style {
	base := *ss.selected[so].getStyle(h)
	styles := make([]*Style, len(sos))
	for i, so := range sos {
		styles[i] = ss.selected[so].getStyle(h)
	}

	return base.composeWith(styles).Get()
}

func findStyleSet(stylesetName string, stylesetsDir []string) (string, error) {
	for _, dir := range stylesetsDir {
		stylesetPath := xdg.ExpandHome(dir, stylesetName)
		if _, err := os.Stat(stylesetPath); os.IsNotExist(err) {
			continue
		}

		return stylesetPath, nil
	}

	return "", fmt.Errorf(
		"Can't find styleset %q in any of %v", stylesetName, stylesetsDir)
}

func (ss *StyleSet) ParseStyleSet(file *ini.File) error {
	defaultSection, err := file.GetSection(ini.DefaultSection)
	if err != nil {
		return err
	}

	// parse non-selected items first
	for _, key := range defaultSection.Keys() {
		err = ss.parseKey(key, false)
		if err != nil {
			return err
		}
	}
	// override with selected items afterwards
	for _, key := range defaultSection.Keys() {
		err = ss.parseKey(key, true)
		if err != nil {
			return err
		}
	}

	user, err := file.GetSection("user")
	if err != nil {
		// This errors if the section doesn't exist, which is ok
		return nil
	}
	for _, key := range user.KeyStrings() {
		tokens := strings.Split(key, ".")
		var styleName, attr string
		switch len(tokens) {
		case 2:
			styleName, attr = tokens[0], tokens[1]
		default:
			return errors.New("Style parsing error: " + key)
		}
		val := user.KeysHash()[key]
		s, ok := ss.user[styleName]
		if !ok {
			// Haven't seen this name before, add it to the map
			s = &Style{}
			ss.user[styleName] = s
		}
		if err := s.Set(attr, val); err != nil {
			return fmt.Errorf("[user].%s=%s: %w", key, val, err)
		}
	}

	return nil
}

var (
	styleObjRe            = regexp.MustCompile(`^([\w\*\?]+)(\.(?:[\w-]+,.+?)+?)?(\.selected)?\.(\w+)$`)
	styleHeaderPatternsRe = regexp.MustCompile(`([\w-]+),(~/(?:.+?)/|(?:.+?))\.`)
)

func (ss *StyleSet) parseKey(key *ini.Key, selected bool) error {
	groups := styleObjRe.FindStringSubmatch(key.Name())
	if groups == nil {
		return errors.New("invalid style syntax: " + key.Name())
	}
	if (groups[3] == ".selected") != selected {
		return nil
	}
	obj, attr := groups[1], groups[4]

	// As there can be multiple header patterns, match them separately, one
	// by one
	headerMatches := styleHeaderPatternsRe.FindAllStringSubmatch(groups[2]+".", -1)
	headerPatterns := make(map[string]*StyleHeaderPattern)
	for _, match := range headerMatches {
		headerPatterns[match[1]] = &StyleHeaderPattern{
			RawPattern: match[2],
		}
	}

	objRe, err := fnmatchToRegex(obj)
	if err != nil {
		return err
	}
	num := 0
	for sn, so := range StyleNames {
		if !objRe.MatchString(sn) {
			continue
		}
		if !selected {
			err = ss.objects[so].update(headerPatterns, attr, key.Value())
			if err != nil {
				return fmt.Errorf("%s=%s: %w", key.Name(), key.Value(), err)
			}
		}
		err = ss.selected[so].update(headerPatterns, attr, key.Value())
		if err != nil {
			return fmt.Errorf("%s=%s: %w", key.Name(), key.Value(), err)
		}
		num++
	}
	if num == 0 {
		return errors.New("unknown style object: " + obj)
	}
	return nil
}

func (c *StyleConf) update(headerPatterns map[string]*StyleHeaderPattern, attr, val string) error {
	if len(headerPatterns) == 0 {
		return (&c.base).Set(attr, val)
	}

	// Check existing entries and overwrite ones with same header/pattern
	for i := range c.dynamic {
		s := &c.dynamic[i]
		if s.hasSameHeaderPatterns(headerPatterns) {
			return s.Set(attr, val)
		}
	}

	s := Style{}
	err := (&s).Set(attr, val)
	if err != nil {
		return err
	}

	for _, p := range headerPatterns {
		var pattern string
		switch {
		case strings.HasPrefix(p.RawPattern, "~/"):
			pattern = p.RawPattern[2 : len(p.RawPattern)-1]
		case strings.HasPrefix(p.RawPattern, "~"):
			pattern = p.RawPattern[1:]
		default:
			pattern = "^" + regexp.QuoteMeta(p.RawPattern) + "$"
		}

		re, err := regexp.Compile(pattern)
		if err != nil {
			return err
		}
		p.Re = re
	}

	s.headerPatterns = headerPatterns
	c.dynamic = append(c.dynamic, s)
	return nil
}

func (ss *StyleSet) LoadStyleSet(stylesetName string, stylesetDirs []string) error {
	parts := strings.Split(stylesetName, ",")
	filepaths := []string{}
	for _, name := range parts {
		filepath, err := findStyleSet(strings.TrimSpace(name), stylesetDirs)
		if err != nil {
			return err
		}
		filepaths = append(filepaths, filepath)
	}

	var options ini.LoadOptions
	options.SpaceBeforeInlineComment = true

	ifilepaths := make([]any, len(filepaths))
	for i, v := range filepaths {
		ifilepaths[i] = v
	}
	file, err := ini.LoadSources(options, ifilepaths[0], ifilepaths[1:]...)
	if err != nil {
		return err
	}

	ss.paths = filepaths

	return ss.ParseStyleSet(file)
}

func fnmatchToRegex(pattern string) (*regexp.Regexp, error) {
	p := regexp.QuoteMeta(pattern)
	p = strings.ReplaceAll(p, `\*`, `.*`)
	return regexp.Compile(strings.ReplaceAll(p, `\?`, `.`))
}

var colorNames = map[string]vaxis.Color{
	"black":                vaxis.IndexColor(0),
	"maroon":               vaxis.IndexColor(1),
	"green":                vaxis.IndexColor(2),
	"olive":                vaxis.IndexColor(3),
	"navy":                 vaxis.IndexColor(4),
	"purple":               vaxis.IndexColor(5),
	"teal":                 vaxis.IndexColor(6),
	"silver":               vaxis.IndexColor(7),
	"gray":                 vaxis.IndexColor(8),
	"red":                  vaxis.IndexColor(9),
	"lime":                 vaxis.IndexColor(10),
	"yellow":               vaxis.IndexColor(11),
	"blue":                 vaxis.IndexColor(12),
	"fuchsia":              vaxis.IndexColor(13),
	"aqua":                 vaxis.IndexColor(14),
	"white":                vaxis.IndexColor(15),
	"aliceblue":            vaxis.HexColor(0xF0F8FF),
	"antiquewhite":         vaxis.HexColor(0xFAEBD7),
	"aquamarine":           vaxis.HexColor(0x7FFFD4),
	"azure":                vaxis.HexColor(0xF0FFFF),
	"beige":                vaxis.HexColor(0xF5F5DC),
	"bisque":               vaxis.HexColor(0xFFE4C4),
	"blanchedalmond":       vaxis.HexColor(0xFFEBCD),
	"blueviolet":           vaxis.HexColor(0x8A2BE2),
	"brown":                vaxis.HexColor(0xA52A2A),
	"burlywood":            vaxis.HexColor(0xDEB887),
	"cadetblue":            vaxis.HexColor(0x5F9EA0),
	"chartreuse":           vaxis.HexColor(0x7FFF00),
	"chocolate":            vaxis.HexColor(0xD2691E),
	"coral":                vaxis.HexColor(0xFF7F50),
	"cornflowerblue":       vaxis.HexColor(0x6495ED),
	"cornsilk":             vaxis.HexColor(0xFFF8DC),
	"crimson":              vaxis.HexColor(0xDC143C),
	"darkblue":             vaxis.HexColor(0x00008B),
	"darkcyan":             vaxis.HexColor(0x008B8B),
	"darkgoldenrod":        vaxis.HexColor(0xB8860B),
	"darkgray":             vaxis.HexColor(0xA9A9A9),
	"darkgreen":            vaxis.HexColor(0x006400),
	"darkkhaki":            vaxis.HexColor(0xBDB76B),
	"darkmagenta":          vaxis.HexColor(0x8B008B),
	"darkolivegreen":       vaxis.HexColor(0x556B2F),
	"darkorange":           vaxis.HexColor(0xFF8C00),
	"darkorchid":           vaxis.HexColor(0x9932CC),
	"darkred":              vaxis.HexColor(0x8B0000),
	"darksalmon":           vaxis.HexColor(0xE9967A),
	"darkseagreen":         vaxis.HexColor(0x8FBC8F),
	"darkslateblue":        vaxis.HexColor(0x483D8B),
	"darkslategray":        vaxis.HexColor(0x2F4F4F),
	"darkturquoise":        vaxis.HexColor(0x00CED1),
	"darkviolet":           vaxis.HexColor(0x9400D3),
	"deeppink":             vaxis.HexColor(0xFF1493),
	"deepskyblue":          vaxis.HexColor(0x00BFFF),
	"dimgray":              vaxis.HexColor(0x696969),
	"dodgerblue":           vaxis.HexColor(0x1E90FF),
	"firebrick":            vaxis.HexColor(0xB22222),
	"floralwhite":          vaxis.HexColor(0xFFFAF0),
	"forestgreen":          vaxis.HexColor(0x228B22),
	"gainsboro":            vaxis.HexColor(0xDCDCDC),
	"ghostwhite":           vaxis.HexColor(0xF8F8FF),
	"gold":                 vaxis.HexColor(0xFFD700),
	"goldenrod":            vaxis.HexColor(0xDAA520),
	"greenyellow":          vaxis.HexColor(0xADFF2F),
	"honeydew":             vaxis.HexColor(0xF0FFF0),
	"hotpink":              vaxis.HexColor(0xFF69B4),
	"indianred":            vaxis.HexColor(0xCD5C5C),
	"indigo":               vaxis.HexColor(0x4B0082),
	"ivory":                vaxis.HexColor(0xFFFFF0),
	"khaki":                vaxis.HexColor(0xF0E68C),
	"lavender":             vaxis.HexColor(0xE6E6FA),
	"lavenderblush":        vaxis.HexColor(0xFFF0F5),
	"lawngreen":            vaxis.HexColor(0x7CFC00),
	"lemonchiffon":         vaxis.HexColor(0xFFFACD),
	"lightblue":            vaxis.HexColor(0xADD8E6),
	"lightcoral":           vaxis.HexColor(0xF08080),
	"lightcyan":            vaxis.HexColor(0xE0FFFF),
	"lightgoldenrodyellow": vaxis.HexColor(0xFAFAD2),
	"lightgray":            vaxis.HexColor(0xD3D3D3),
	"lightgreen":           vaxis.HexColor(0x90EE90),
	"lightpink":            vaxis.HexColor(0xFFB6C1),
	"lightsalmon":          vaxis.HexColor(0xFFA07A),
	"lightseagreen":        vaxis.HexColor(0x20B2AA),
	"lightskyblue":         vaxis.HexColor(0x87CEFA),
	"lightslategray":       vaxis.HexColor(0x778899),
	"lightsteelblue":       vaxis.HexColor(0xB0C4DE),
	"lightyellow":          vaxis.HexColor(0xFFFFE0),
	"limegreen":            vaxis.HexColor(0x32CD32),
	"linen":                vaxis.HexColor(0xFAF0E6),
	"mediumaquamarine":     vaxis.HexColor(0x66CDAA),
	"mediumblue":           vaxis.HexColor(0x0000CD),
	"mediumorchid":         vaxis.HexColor(0xBA55D3),
	"mediumpurple":         vaxis.HexColor(0x9370DB),
	"mediumseagreen":       vaxis.HexColor(0x3CB371),
	"mediumslateblue":      vaxis.HexColor(0x7B68EE),
	"mediumspringgreen":    vaxis.HexColor(0x00FA9A),
	"mediumturquoise":      vaxis.HexColor(0x48D1CC),
	"mediumvioletred":      vaxis.HexColor(0xC71585),
	"midnightblue":         vaxis.HexColor(0x191970),
	"mintcream":            vaxis.HexColor(0xF5FFFA),
	"mistyrose":            vaxis.HexColor(0xFFE4E1),
	"moccasin":             vaxis.HexColor(0xFFE4B5),
	"navajowhite":          vaxis.HexColor(0xFFDEAD),
	"oldlace":              vaxis.HexColor(0xFDF5E6),
	"olivedrab":            vaxis.HexColor(0x6B8E23),
	"orange":               vaxis.HexColor(0xFFA500),
	"orangered":            vaxis.HexColor(0xFF4500),
	"orchid":               vaxis.HexColor(0xDA70D6),
	"palegoldenrod":        vaxis.HexColor(0xEEE8AA),
	"palegreen":            vaxis.HexColor(0x98FB98),
	"paleturquoise":        vaxis.HexColor(0xAFEEEE),
	"palevioletred":        vaxis.HexColor(0xDB7093),
	"papayawhip":           vaxis.HexColor(0xFFEFD5),
	"peachpuff":            vaxis.HexColor(0xFFDAB9),
	"peru":                 vaxis.HexColor(0xCD853F),
	"pink":                 vaxis.HexColor(0xFFC0CB),
	"plum":                 vaxis.HexColor(0xDDA0DD),
	"powderblue":           vaxis.HexColor(0xB0E0E6),
	"rebeccapurple":        vaxis.HexColor(0x663399),
	"rosybrown":            vaxis.HexColor(0xBC8F8F),
	"royalblue":            vaxis.HexColor(0x4169E1),
	"saddlebrown":          vaxis.HexColor(0x8B4513),
	"salmon":               vaxis.HexColor(0xFA8072),
	"sandybrown":           vaxis.HexColor(0xF4A460),
	"seagreen":             vaxis.HexColor(0x2E8B57),
	"seashell":             vaxis.HexColor(0xFFF5EE),
	"sienna":               vaxis.HexColor(0xA0522D),
	"skyblue":              vaxis.HexColor(0x87CEEB),
	"slateblue":            vaxis.HexColor(0x6A5ACD),
	"slategray":            vaxis.HexColor(0x708090),
	"snow":                 vaxis.HexColor(0xFFFAFA),
	"springgreen":          vaxis.HexColor(0x00FF7F),
	"steelblue":            vaxis.HexColor(0x4682B4),
	"tan":                  vaxis.HexColor(0xD2B48C),
	"thistle":              vaxis.HexColor(0xD8BFD8),
	"tomato":               vaxis.HexColor(0xFF6347),
	"turquoise":            vaxis.HexColor(0x40E0D0),
	"violet":               vaxis.HexColor(0xEE82EE),
	"wheat":                vaxis.HexColor(0xF5DEB3),
	"whitesmoke":           vaxis.HexColor(0xF5F5F5),
	"yellowgreen":          vaxis.HexColor(0x9ACD32),
}
