1package message
2
3import (
4	"fmt"
5)
6
7const (
8	// Reset resets the color
9	Reset = "\033[0m"
10
11	// Bold makes the following text bold
12	Bold = "\033[1m"
13
14	// Dim dims the following text
15	Dim = "\033[2m"
16
17	// Italic makes the following text italic
18	Italic = "\033[3m"
19
20	// Underline underlines the following text
21	Underline = "\033[4m"
22
23	// Blink blinks the following text
24	Blink = "\033[5m"
25
26	// Invert inverts the following text
27	Invert = "\033[7m"
28
29	// Newline
30	Newline = "\r\n"
31
32	// BEL
33	Bel = "\007"
34)
35
36// Interface for Styles
37type Style interface {
38	String() string
39	Format(string) string
40}
41
42// General hardcoded style, mostly used as a crutch until we flesh out the
43// framework to support backgrounds etc.
44type style string
45
46func (c style) String() string {
47	return string(c)
48}
49
50func (c style) Format(s string) string {
51	return c.String() + s + Reset
52}
53
54// 256 color type, for terminals who support it
55type Color256 uint8
56
57// String version of this color
58func (c Color256) String() string {
59	return fmt.Sprintf("38;05;%d", c)
60}
61
62// Return formatted string with this color
63func (c Color256) Format(s string) string {
64	return "\033[" + c.String() + "m" + s + Reset
65}
66
67func Color256Palette(colors ...uint8) *Palette {
68	size := len(colors)
69	p := make([]Style, 0, size)
70	for _, color := range colors {
71		p = append(p, Color256(color))
72	}
73	return &Palette{
74		colors: p,
75		size:   size,
76	}
77}
78
79// No color, used for mono theme
80type Color0 struct{}
81
82// No-op for Color0
83func (c Color0) String() string {
84	return ""
85}
86
87// No-op for Color0
88func (c Color0) Format(s string) string {
89	return s
90}
91
92// Container for a collection of colors
93type Palette struct {
94	colors []Style
95	size   int
96}
97
98// Get a color by index, overflows are looped around.
99func (p Palette) Get(i int) Style {
100	if p.size == 1 {
101		return p.colors[0]
102	}
103	return p.colors[i%(p.size-1)]
104}
105
106func (p Palette) Len() int {
107	return p.size
108}
109
110func (p Palette) String() string {
111	r := ""
112	for _, c := range p.colors {
113		r += c.Format("X")
114	}
115	return r
116}
117
118// Collection of settings for chat
119type Theme struct {
120	id        string
121	sys       Style
122	pm        Style
123	highlight Style
124	names     *Palette
125	useID     bool
126}
127
128func (theme Theme) ID() string {
129	return theme.id
130}
131
132// Colorize name string given some index
133func (theme Theme) ColorName(u *User) string {
134	var name string
135	if theme.useID {
136		name = u.ID()
137	} else {
138		name = u.Name()
139	}
140	if theme.names == nil {
141		return name
142	}
143
144	return theme.names.Get(u.colorIdx).Format(name)
145}
146
147// Colorize the PM string
148func (theme Theme) ColorPM(s string) string {
149	if theme.pm == nil {
150		return s
151	}
152
153	return theme.pm.Format(s)
154}
155
156// Colorize the Sys message
157func (theme Theme) ColorSys(s string) string {
158	if theme.sys == nil {
159		return s
160	}
161
162	return theme.sys.Format(s)
163}
164
165// Highlight a matched string, usually name
166func (theme Theme) Highlight(s string) string {
167	if theme.highlight == nil {
168		return s
169	}
170	return theme.highlight.Format(s)
171}
172
173// Timestamp colorizes the timestamp.
174func (theme Theme) Timestamp(s string) string {
175	if theme.sys == nil {
176		return s
177	}
178	return theme.sys.Format(s)
179}
180
181// List of initialzied themes
182var Themes []Theme
183
184// Default theme to use
185var DefaultTheme *Theme
186
187// MonoTheme is a simple theme without colors, useful for testing and bots.
188var MonoTheme *Theme
189
190func allColors256() *Palette {
191	colors := []uint8{}
192	var i uint8
193	for i = 0; i < 255; i++ {
194		colors = append(colors, i)
195	}
196	return Color256Palette(colors...)
197}
198
199func readableColors256() *Palette {
200	colors := []uint8{}
201	var i uint8
202	for i = 0; i < 255; i++ {
203		if i == 0 || i == 7 || i == 8 || i == 15 || i == 16 || i == 17 || i > 230 {
204			// Skip 31 Shades of Grey, and one hyperintelligent shade of blue.
205			continue
206		}
207		colors = append(colors, i)
208	}
209	return Color256Palette(colors...)
210}
211
212func init() {
213	Themes = []Theme{
214		{
215			id:        "colors",
216			names:     readableColors256(),
217			sys:       Color256(245),                              // Grey
218			pm:        Color256(7),                                // White
219			highlight: style(Bold + "\033[48;5;11m\033[38;5;16m"), // Yellow highlight
220		},
221		{
222			id:        "solarized",
223			names:     Color256Palette(1, 2, 3, 4, 5, 6, 7, 9, 13),
224			sys:       Color256(11),                              // Yellow
225			pm:        Color256(15),                              // White
226			highlight: style(Bold + "\033[48;5;3m\033[38;5;94m"), // Orange highlight
227		},
228		{
229			id:        "hacker",
230			names:     Color256Palette(82),                        // Green
231			sys:       Color256(22),                               // Another green
232			pm:        Color256(28),                               // More green, slightly lighter
233			highlight: style(Bold + "\033[48;5;22m\033[38;5;46m"), // Green on dark green
234		},
235		{
236			id:    "mono",
237			useID: true,
238		},
239	}
240
241	DefaultTheme = &Themes[0]
242	MonoTheme = &Themes[3]
243
244	/* Some debug helpers for your convenience:
245
246	// Debug for palettes
247	printPalette(allColors256())
248
249	// Debug for themes
250	for _, t := range Themes {
251		printTheme(t)
252	}
253
254	*/
255}
256
257func printTheme(t Theme) {
258	fmt.Println("Printing theme:", t.ID())
259	if t.names != nil {
260		for i, color := range t.names.colors {
261			fmt.Printf("%s ", color.Format(fmt.Sprintf("name%d", i)))
262		}
263		fmt.Println("")
264	}
265	fmt.Println(t.ColorSys("SystemMsg"))
266	fmt.Println(t.ColorPM("PrivateMsg"))
267	fmt.Println(t.Highlight("Highlight"))
268	fmt.Println("")
269}
270
271func printPalette(p *Palette) {
272	for i, color := range p.colors {
273		fmt.Printf("%d\t%s\n", i, color.Format(color.String()+" "))
274	}
275}
276