1package prog
2
3import (
4	"context"
5	"fmt"
6	"time"
7
8	"github.com/ambientsound/visp/api"
9	"github.com/ambientsound/visp/clipboard"
10	"github.com/ambientsound/visp/db"
11	"github.com/ambientsound/visp/input/keys"
12	"github.com/ambientsound/visp/list"
13	"github.com/ambientsound/visp/log"
14	"github.com/ambientsound/visp/multibar"
15	"github.com/ambientsound/visp/options"
16	"github.com/ambientsound/visp/player"
17	"github.com/ambientsound/visp/spotify/library"
18	"github.com/ambientsound/visp/spotify/proxyclient"
19	"github.com/ambientsound/visp/spotify/tracklist"
20	"github.com/ambientsound/visp/style"
21	"github.com/ambientsound/visp/topbar"
22	"github.com/zmb3/spotify"
23	"golang.org/x/oauth2"
24)
25
26func (v *Visp) Authenticate(token *oauth2.Token) error {
27	log.Infof("Configured Spotify access token, expires at %s", token.Expiry.Format(time.RFC1123))
28
29	cli := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(token))
30	scli := spotify.NewClient(cli)
31
32	v.client = &scli
33
34	next := spotify_proxyclient.TokenTTL(token)
35
36	v.tokenRefresh = time.After(next)
37
38	v.Changed(api.ChangePlayerStateInvalid, nil)
39
40	err := v.Tokencache.Write(*token)
41	if err != nil {
42		return fmt.Errorf("write Spotify token to file: %s", err)
43	}
44
45	return nil
46}
47
48func (v *Visp) Clipboards() *clipboard.List {
49	return v.clipboards
50}
51
52func (v *Visp) Db() *db.List {
53	return v.db
54}
55
56func (v *Visp) Exec(command string) error {
57	log.Debugf("Run command: %s", command)
58	return v.interpreter.Exec(command)
59}
60
61func (v *Visp) Library() *spotify_library.List {
62	return v.library
63}
64
65func (v *Visp) List() list.List {
66	return v.list
67}
68
69func (v *Visp) Changed(change api.ChangeType, data interface{}) {
70	switch change {
71	case api.ChangeList:
72		lst, ok := data.(list.List)
73		if !ok {
74			log.Debugf("BUG: list was changed, but is '%T', not 'list.List'", data)
75			return
76		}
77		v.db.Cache(lst)
78		v.clipboards.Update(lst)
79		// TODO: playlists indexes
80
81	case api.ChangeOption:
82		s, ok := data.(string)
83		if !ok {
84			log.Debugf("BUG: option '#v' changed, but is not of type 'string'", data)
85			return
86		}
87		v.optionChanged(s)
88
89	case api.ChangePlayerStateInvalid:
90		v.player.Invalidate()
91		v.ticker.Reset(changePlayerStateDelay)
92
93	case api.ChangeDevice:
94		v.player.Invalidate()
95		v.ticker.Reset(changePlayerStateDelay)
96		// TODO: refresh devices window
97
98	}
99}
100
101func (v *Visp) optionChanged(key string) {
102	switch key {
103	case options.LogFile:
104		logFile := options.GetString(options.LogFile)
105		overwrite := options.GetBool(options.LogOverwrite)
106		if len(logFile) == 0 {
107			break
108		}
109		err := log.Configure(logFile, overwrite)
110		if err != nil {
111			log.Errorf("log configuration: %s", err)
112			break
113		}
114		log.Infof("Note: log file will be backfilled with existing log")
115		log.Infof("Writing debug log to %s", logFile)
116
117	case options.Topbar:
118		config := options.GetString(options.Topbar)
119		matrix, err := topbar.Parse(v, config)
120		if err == nil {
121			v.Termui.Widgets.Topbar.SetMatrix(matrix)
122			v.Termui.Resize()
123		} else {
124			log.Errorf("topbar configuration: %s", err)
125		}
126
127	case options.ExpandColumns:
128		// Re-render columns
129		v.UI().TableWidget().SetColumns(v.UI().TableWidget().ColumnNames())
130	}
131}
132
133func (v *Visp) PlayerStatus() player.State {
134	return *v.player
135}
136
137func (v *Visp) Quit() {
138	v.quit <- new(interface{})
139}
140
141func (v *Visp) Sequencer() *keys.Sequencer {
142	return v.sequencer
143}
144
145func (v *Visp) Multibar() *multibar.Multibar {
146	return v.multibar
147}
148
149func (v *Visp) History() *spotify_tracklist.List {
150	if v.history == nil {
151		v.history = spotify_tracklist.NewHistory()
152	}
153	return v.history
154}
155
156func (v *Visp) SetList(lst list.List) {
157	if lst == nil {
158		return
159	}
160	cur := v.db.Current()
161	if cur != nil && cur != lst && cur != v.db && cur != v.clipboards {
162		log.Debugf("Setting last used list to '%s'", cur.Name())
163		v.db.SetLast(v.db.Current())
164	}
165	c := v.db.Cache(lst)
166	v.db.SetCursor(c)
167	v.list = lst
168	v.Termui.TableWidget().SetList(lst)
169}
170
171func (v *Visp) Spotify() (*spotify.Client, error) {
172	if v.client == nil {
173		return nil, fmt.Errorf("please authenticate with Spotify at: %s/authorize", options.GetString("spotifyauthserver"))
174	}
175	token, err := v.client.Token()
176	if err != nil {
177		return nil, fmt.Errorf("unable to refresh Spotify token: %s", err)
178	}
179	_ = v.Tokencache.Write(*token)
180	return v.client, nil
181}
182
183func (v *Visp) Styles() style.Stylesheet {
184	return v.stylesheet
185}
186
187func (v *Visp) Tracklist() *spotify_tracklist.List {
188	switch v := v.List().(type) {
189	case *spotify_tracklist.List:
190		return v
191	default:
192		return nil
193	}
194}
195
196func (v *Visp) UI() api.UI {
197	return v.Termui
198}
199