1package songlist
2
3import (
4	"fmt"
5	"reflect"
6	"time"
7)
8
9// Collection holds a set of songlists, and keeps track of movement between different lists.
10type Collection struct {
11	lists   []Songlist
12	index   int
13	last    Songlist
14	current Songlist
15	updated time.Time
16}
17
18// NewCollection returns Collection.
19func NewCollection() *Collection {
20	return &Collection{
21		lists: make([]Songlist, 0),
22	}
23}
24
25// Activate activates the specified songlist. The songlist is not added to the
26// collection. If the songlist is found in the collection, the index of that
27// songlist is activated.
28func (c *Collection) Activate(s Songlist) {
29	c.index = -1
30	for i, stored := range c.lists {
31		if stored == s {
32			c.index = i
33			break
34		}
35	}
36	c.last = c.current
37	c.current = s
38	c.SetUpdated()
39}
40
41// ActivateIndex activates the songlist pointed to by the specified index.
42func (c *Collection) ActivateIndex(i int) error {
43	list, err := c.Songlist(i)
44	if err != nil {
45		return err
46	}
47	c.Activate(list)
48	return nil
49}
50
51// Add appends a songlist to the collection.
52func (c *Collection) Add(s Songlist) {
53	c.lists = append(c.lists, s)
54}
55
56// Current returns the active songlist.
57func (c *Collection) Current() Songlist {
58	return c.current
59}
60
61// Index returns the current list cursor.
62func (c *Collection) Index() (int, error) {
63	if !c.ValidIndex(c.index) {
64		return 0, fmt.Errorf("Songlist index is out of range")
65	}
66	return c.index, nil
67}
68
69// Last returns the last used songlist.
70func (c *Collection) Last() Songlist {
71	return c.last
72}
73
74// Len returns the songlists count.
75func (c *Collection) Len() int {
76	return len(c.lists)
77}
78
79// Remove removes a songlist from the collection.
80func (c *Collection) Remove(index int) error {
81	if err := c.ValidateIndex(index); err != nil {
82		return err
83	}
84	if index+1 == c.Len() {
85		c.lists = c.lists[:index]
86	} else {
87		c.lists = append(c.lists[:index], c.lists[index+1:]...)
88	}
89	return nil
90}
91
92// Replace replaces an existing songlist with its new version. Checking
93// is done on a type-level, so this function should not be used for lists where
94// several of the same type is contained within the collection.
95func (c *Collection) Replace(s Songlist) {
96	for i := range c.lists {
97		if reflect.TypeOf(c.lists[i]) != reflect.TypeOf(s) {
98			continue
99		}
100		//console.Log("Songlist UI: replacing songlist of type %T at %p with new list at %p", s, c.lists[i], s)
101		//console.Log("Songlist UI: comparing %p %p", c.lists[i], c.Songlist())
102
103		active := c.lists[i] == c.Current()
104		c.lists[i] = s
105
106		if active {
107			//console.Log("Songlist UI: replaced songlist is currently active, switching to new songlist.")
108			c.Activate(s)
109		}
110		return
111	}
112
113	//console.Log("Songlist UI: adding songlist of type %T at address %p since no similar exists", s, s)
114	c.Add(s)
115}
116
117func (c *Collection) Songlist(index int) (Songlist, error) {
118	if err := c.ValidateIndex(index); err != nil {
119		return nil, err
120	}
121	return c.lists[index], nil
122}
123
124func (c *Collection) ValidIndex(i int) bool {
125	return i >= 0 && i < c.Len()
126}
127
128func (c *Collection) ValidateIndex(i int) error {
129	if !c.ValidIndex(i) {
130		return fmt.Errorf("Index %d is out of bounds (try between 1 and %d)", i+1, c.Len())
131	}
132	return nil
133}
134
135// Updated returns the timestamp of when this collection was last updated.
136func (c *Collection) Updated() time.Time {
137	return c.updated
138}
139
140// SetUpdated sets the update timestamp of the collection.
141func (c *Collection) SetUpdated() {
142	c.updated = time.Now()
143}
144