1package cointop
2
3import (
4	"strconv"
5
6	fcolor "github.com/fatih/color"
7	gocui "github.com/miguelmota/gocui"
8	xtermcolor "github.com/tomnomnom/xtermcolor"
9)
10
11// TODO: fix hex color support
12
13// colorschemeColors is a map of color string names to Attribute types
14type colorschemeColors map[string]interface{}
15
16// ISprintf is a sprintf interface
17type ISprintf func(...interface{}) string
18
19// colorCache is a map of color string names to sprintf functions
20type colorCache map[string]ISprintf
21
22// Colorscheme is the struct for colorscheme
23type Colorscheme struct {
24	colors colorschemeColors
25	cache  colorCache
26}
27
28var fgcolorschemeColorsMap = map[string]fcolor.Attribute{
29	"black":   fcolor.FgBlack,
30	"blue":    fcolor.FgBlue,
31	"cyan":    fcolor.FgCyan,
32	"green":   fcolor.FgGreen,
33	"magenta": fcolor.FgMagenta,
34	"red":     fcolor.FgRed,
35	"white":   fcolor.FgWhite,
36	"yellow":  fcolor.FgYellow,
37}
38
39var bgcolorschemeColorsMap = map[string]fcolor.Attribute{
40	"black":   fcolor.BgBlack,
41	"blue":    fcolor.BgBlue,
42	"cyan":    fcolor.BgCyan,
43	"green":   fcolor.BgGreen,
44	"magenta": fcolor.BgMagenta,
45	"red":     fcolor.BgRed,
46	"white":   fcolor.BgWhite,
47	"yellow":  fcolor.BgYellow,
48}
49
50var gocuiColorschemeColorsMap = map[string]gocui.Attribute{
51	"black":   gocui.ColorBlack,
52	"blue":    gocui.ColorBlue,
53	"cyan":    gocui.ColorCyan,
54	"green":   gocui.ColorGreen,
55	"magenta": gocui.ColorMagenta,
56	"red":     gocui.ColorRed,
57	"white":   gocui.ColorWhite,
58	"yellow":  gocui.ColorYellow,
59}
60
61// NewColorscheme ...
62func NewColorscheme(colors colorschemeColors) *Colorscheme {
63	return &Colorscheme{
64		colors: colors,
65		cache:  make(colorCache),
66	}
67}
68
69// BaseFg ...
70func (c *Colorscheme) BaseFg() gocui.Attribute {
71	return c.gocuiFgColor("base")
72}
73
74// BaseBg ...
75func (c *Colorscheme) BaseBg() gocui.Attribute {
76	return c.gocuiBgColor("base")
77}
78
79// Chart ...
80func (c *Colorscheme) Chart(a ...interface{}) string {
81	return c.color("chart", a...)
82}
83
84// Marketbar ...
85func (c *Colorscheme) Marketbar(a ...interface{}) string {
86	return c.color("marketbar", a...)
87}
88
89// MarketbarSprintf ...
90func (c *Colorscheme) MarketbarSprintf() ISprintf {
91	return c.toSprintf("marketbar")
92}
93
94// MarketbarChangeSprintf ...
95func (c *Colorscheme) MarketbarChangeSprintf() ISprintf {
96	// NOTE: reusing table styles
97	return c.toSprintf("table_column_change")
98}
99
100// MarketbarChangeDownSprintf ...
101func (c *Colorscheme) MarketbarChangeDownSprintf() ISprintf {
102	// NOTE: reusing table styles
103	return c.toSprintf("table_column_change_down")
104}
105
106// MarketbarChangeUpSprintf ...
107func (c *Colorscheme) MarketbarChangeUpSprintf() ISprintf {
108	// NOTE: reusing table styles
109	return c.toSprintf("table_column_change_up")
110}
111
112// MarketBarLabelActive ...
113func (c *Colorscheme) MarketBarLabelActive(a ...interface{}) string {
114	return c.color("marketbar_label_active", a...)
115}
116
117// Menu ...
118func (c *Colorscheme) Menu(a ...interface{}) string {
119	return c.color("menu", a...)
120}
121
122// MenuHeader ...
123func (c *Colorscheme) MenuHeader(a ...interface{}) string {
124	return c.color("menu_header", a...)
125}
126
127// MenuLabel ...
128func (c *Colorscheme) MenuLabel(a ...interface{}) string {
129	return c.color("menu_label", a...)
130}
131
132// MenuLabelActive ...
133func (c *Colorscheme) MenuLabelActive(a ...interface{}) string {
134	return c.color("menu_label_active", a...)
135}
136
137// Searchbar ...
138func (c *Colorscheme) Searchbar(a ...interface{}) string {
139	return c.color("searchbar", a...)
140}
141
142// Statusbar ...
143func (c *Colorscheme) Statusbar(a ...interface{}) string {
144	return c.color("statusbar", a...)
145}
146
147// TableColumnPrice ...
148func (c *Colorscheme) TableColumnPrice(a ...interface{}) string {
149	return c.color("table_column_price", a...)
150}
151
152// TableColumnPriceSprintf ...
153func (c *Colorscheme) TableColumnPriceSprintf() ISprintf {
154	return c.toSprintf("table_column_price")
155}
156
157// TableColumnChange ...
158func (c *Colorscheme) TableColumnChange(a ...interface{}) string {
159	return c.color("table_column_change", a...)
160}
161
162// TableColumnChangeSprintf ...
163func (c *Colorscheme) TableColumnChangeSprintf() ISprintf {
164	return c.toSprintf("table_column_change")
165}
166
167// TableColumnChangeDown ...
168func (c *Colorscheme) TableColumnChangeDown(a ...interface{}) string {
169	return c.color("table_column_change_down", a...)
170}
171
172// TableColumnChangeDownSprintf ...
173func (c *Colorscheme) TableColumnChangeDownSprintf() ISprintf {
174	return c.toSprintf("table_column_change_down")
175}
176
177// TableColumnChangeUp ...
178func (c *Colorscheme) TableColumnChangeUp(a ...interface{}) string {
179	return c.color("table_column_change_up", a...)
180}
181
182// TableColumnChangeUpSprintf ...
183func (c *Colorscheme) TableColumnChangeUpSprintf() ISprintf {
184	return c.toSprintf("table_column_change_up")
185}
186
187// TableHeader ...
188func (c *Colorscheme) TableHeader(a ...interface{}) string {
189	return c.color("table_header", a...)
190}
191
192// TableHeaderSprintf ...
193func (c *Colorscheme) TableHeaderSprintf() ISprintf {
194	return c.toSprintf("table_header")
195}
196
197// TableHeaderColumnActive ...
198func (c *Colorscheme) TableHeaderColumnActive(a ...interface{}) string {
199	return c.color("table_header_column_active", a...)
200}
201
202// TableHeaderColumnActiveSprintf ...
203func (c *Colorscheme) TableHeaderColumnActiveSprintf() ISprintf {
204	return c.toSprintf("table_header_column_active")
205}
206
207// TableRow ...
208func (c *Colorscheme) TableRow(a ...interface{}) string {
209	return c.color("table_row", a...)
210}
211
212// TableRowSprintf ...
213func (c *Colorscheme) TableRowSprintf() ISprintf {
214	return c.toSprintf("table_row")
215}
216
217// TableRowActive ...
218func (c *Colorscheme) TableRowActive(a ...interface{}) string {
219	return c.color("table_row_active", a...)
220}
221
222// TableRowFavorite ...
223func (c *Colorscheme) TableRowFavorite(a ...interface{}) string {
224	return c.color("table_row_favorite", a...)
225}
226
227// TableRowFavoriteSprintf ...
228func (c *Colorscheme) TableRowFavoriteSprintf() ISprintf {
229	return c.toSprintf("table_row_favorite")
230}
231
232// SetViewColor ...
233func (c *Colorscheme) SetViewColor(view *gocui.View, name string) {
234	view.FgColor = c.gocuiFgColor(name)
235	view.BgColor = c.gocuiBgColor(name)
236}
237
238// SetViewActiveColor ...
239func (c *Colorscheme) SetViewActiveColor(view *gocui.View, name string) {
240	view.SelFgColor = c.gocuiFgColor(name)
241	view.SelBgColor = c.gocuiBgColor(name)
242}
243
244func (c *Colorscheme) toSprintf(name string) ISprintf {
245	if cached, ok := c.cache[name]; ok {
246		return cached
247	}
248
249	var attrs []fcolor.Attribute
250	if v, ok := c.colors[name+"_fg"].(string); ok {
251		if fg, ok := c.toFgAttr(v); ok {
252			attrs = append(attrs, fg)
253		}
254	}
255	if v, ok := c.colors[name+"_bg"].(string); ok {
256		if bg, ok := c.toBgAttr(v); ok {
257			attrs = append(attrs, bg)
258		}
259	}
260	if v, ok := c.colors[name+"_bold"].(bool); ok {
261		if bold, ok := c.toBoldAttr(v); ok {
262			attrs = append(attrs, bold)
263		}
264	}
265	if v, ok := c.colors[name+"_underline"].(bool); ok {
266		if underline, ok := c.toUnderlineAttr(v); ok {
267			attrs = append(attrs, underline)
268		}
269	}
270
271	c.cache[name] = fcolor.New(attrs...).SprintFunc()
272	return c.cache[name]
273}
274
275func (c *Colorscheme) color(name string, a ...interface{}) string {
276	return c.toSprintf(name)(a...)
277}
278
279func (c *Colorscheme) gocuiFgColor(name string) gocui.Attribute {
280	if v, ok := c.colors[name+"_fg"].(string); ok {
281		if fg, ok := c.toGocuiAttr(v); ok {
282			return fg
283		}
284	}
285
286	return gocui.ColorDefault
287}
288
289func (c *Colorscheme) gocuiBgColor(name string) gocui.Attribute {
290	if v, ok := c.colors[name+"_bg"].(string); ok {
291		if bg, ok := c.toGocuiAttr(v); ok {
292			return bg
293		}
294	}
295
296	return gocui.ColorDefault
297}
298
299func (c *Colorscheme) toFgAttr(v string) (fcolor.Attribute, bool) {
300	if attr, ok := fgcolorschemeColorsMap[v]; ok {
301		return attr, true
302	}
303
304	if code, ok := HexToAnsi(v); ok {
305		return fcolor.Attribute(code), true
306	}
307
308	return 0, false
309}
310
311func (c *Colorscheme) toBgAttr(v string) (fcolor.Attribute, bool) {
312	if attr, ok := bgcolorschemeColorsMap[v]; ok {
313		return attr, true
314	}
315
316	if code, ok := HexToAnsi(v); ok {
317		return fcolor.Attribute(code), true
318	}
319
320	return 0, false
321}
322
323// toBoldAttr converts a boolean to an Attribute type
324func (c *Colorscheme) toBoldAttr(v bool) (fcolor.Attribute, bool) {
325	return fcolor.Bold, v
326}
327
328// toUnderlineAttr converts a boolean to an Attribute type
329func (c *Colorscheme) toUnderlineAttr(v bool) (fcolor.Attribute, bool) {
330	return fcolor.Underline, v
331}
332
333// toGocuiAttr converts a color string name to a gocui Attribute type
334func (c *Colorscheme) toGocuiAttr(v string) (gocui.Attribute, bool) {
335	if attr, ok := gocuiColorschemeColorsMap[v]; ok {
336		return attr, true
337	}
338
339	if code, ok := HexToAnsi(v); ok {
340		return gocui.Attribute(code), true
341	}
342
343	return 0, false
344}
345
346// HexToAnsi converts a hex color string to a uint8 ansi code
347func HexToAnsi(h string) (uint8, bool) {
348	if h == "" {
349		return 0, false
350	}
351
352	n, err := strconv.Atoi(h)
353	if err == nil {
354		if n <= 255 {
355			return uint8(n), true
356		}
357	}
358
359	code, err := xtermcolor.FromHexStr(h)
360	if err != nil {
361		return 0, false
362	}
363
364	return code, true
365}
366
367// gocui can use xterm colors
368