]> git.lizzy.rs Git - go-anidb.git/blob - misc/episodelist.go
misc: Allow adding/removing arbitrary episodes to an EpisodeList
[go-anidb.git] / misc / episodelist.go
1 package misc
2
3 import (
4         "fmt"
5         "sort"
6         "strings"
7 )
8
9 type EpisodeCount struct {
10         RegularCount int
11         SpecialCount int
12         CreditsCount int
13         OtherCount   int
14         TrailerCount int
15         ParodyCount  int
16 }
17
18 type EpisodeList []*EpisodeRange
19
20 func EpisodeToList(ep *Episode) EpisodeList {
21         return RangesToList(EpisodeToRange(ep))
22 }
23
24 func RangesToList(ranges ...*EpisodeRange) EpisodeList {
25         return EpisodeList(ranges)
26 }
27
28 func ContainerToList(ec EpisodeContainer) EpisodeList {
29         switch v := ec.(type) {
30         case *Episode:
31                 return EpisodeToList(v)
32         case *EpisodeRange:
33                 return RangesToList(v)
34         case EpisodeList:
35                 return v
36         default:
37                 panic("unimplemented")
38         }
39 }
40
41 // Converts the EpisodeList into the AniDB API list format.
42 func (el EpisodeList) String() string {
43         scales := map[EpisodeType]int{}
44
45         for _, er := range el {
46                 if er == nil {
47                         continue
48                 }
49
50                 s := er.scale()
51                 if s > scales[er.Type] {
52                         scales[er.Type] = s
53                 }
54         }
55
56         parts := make([]string, len(el))
57         for i, er := range el {
58                 parts[i] = er.Format(scales[er.Type])
59         }
60
61         return strings.Join(parts, ",")
62 }
63
64 // Formats the list according to the number of digits of
65 // the count for its type, given in the EpisodeCount.
66 func (el EpisodeList) FormatLog(ec EpisodeCount) string {
67         parts := make([]string, len(el))
68         for i, er := range el {
69                 switch er.Type {
70                 case EpisodeTypeRegular:
71                         parts[i] = er.FormatLog(ec.RegularCount)
72                 case EpisodeTypeSpecial:
73                         parts[i] = er.FormatLog(ec.SpecialCount)
74                 case EpisodeTypeCredits:
75                         parts[i] = er.FormatLog(ec.CreditsCount)
76                 case EpisodeTypeOther:
77                         parts[i] = er.FormatLog(ec.OtherCount)
78                 case EpisodeTypeTrailer:
79                         parts[i] = er.FormatLog(ec.TrailerCount)
80                 case EpisodeTypeParody:
81                         parts[i] = er.FormatLog(ec.ParodyCount)
82                 default:
83                         parts[i] = er.Format(er.scale())
84                 }
85         }
86
87         return strings.Join(parts, ",")
88 }
89
90 // Returns true if any of the contained EpisodeRanges contain the
91 // given EpisodeContainer.
92 func (el EpisodeList) ContainsEpisodes(ec EpisodeContainer) bool {
93         for _, i := range el {
94                 if i != nil && i.ContainsEpisodes(ec) {
95                         return true
96                 }
97         }
98         return false
99 }
100
101 // Parses a string in the AniDB API list format and converts into
102 // an EpisodeList.
103 //
104 //      ParseEpisodeList("01")       <=> EpisodeList{ParseEpisodeRange("01")}
105 //      ParseEpisodeList("S2-S3")    <=> EpisodeList{ParseEpisodeRange("S2-S3")}
106 //      ParseEpisodeList("T1,C1-C3") <=> EpisodeList{ParseEpisodeRange("T1"), ParseEpisodeRange("C1-C3")}
107 func ParseEpisodeList(s string) (el EpisodeList) {
108         parts := strings.Split(s, ",")
109
110         el = make(EpisodeList, len(parts))
111         for i := range parts {
112                 el[i] = ParseEpisodeRange(parts[i])
113         }
114
115         return el.Simplify()
116 }
117
118 // Returns a simplified version of the EpisodeList (removes nil ranges, merges mergeable ranges, sorts).
119 func (el EpisodeList) Simplify() EpisodeList {
120         nl := make(EpisodeList, 0, len(el))
121
122         // drop nil ranges
123         for _, er := range el {
124                 if er != nil {
125                         nl = append(nl, er)
126                 }
127         }
128
129         // merge ranges
130         for n, changed := 0, true; changed; n++ {
131                 tmp := EpisodeList{}
132                 used := map[int]bool{}
133                 changed = false
134
135                 for i, a := range nl {
136                         if used[i] {
137                                 continue
138                         }
139                         for j, b := range nl[i+1:] {
140                                 if c := a.Merge(b); c != nil {
141                                         changed = true
142                                         used[j+i+1] = true
143                                         a = c
144                                 }
145                         }
146                         tmp = append(tmp, a)
147                 }
148                 nl = tmp
149
150                 if n > len(el) {
151                         panic(fmt.Sprintf("Too many iterations (%d) when simplifing %s!", n, el))
152                 }
153         }
154         sort.Sort(nl)
155         return nl
156 }
157
158 func (el EpisodeList) CountEpisodes() (ec EpisodeCount) {
159         for _, er := range el {
160                 var c *int
161                 switch er.Type {
162                 case EpisodeTypeRegular:
163                         c = &ec.RegularCount
164                 case EpisodeTypeSpecial:
165                         c = &ec.SpecialCount
166                 case EpisodeTypeCredits:
167                         c = &ec.CreditsCount
168                 case EpisodeTypeOther:
169                         c = &ec.OtherCount
170                 case EpisodeTypeTrailer:
171                         c = &ec.TrailerCount
172                 case EpisodeTypeParody:
173                         c = &ec.ParodyCount
174                 default:
175                         continue
176                 }
177                 if *c < 0 {
178                         continue
179                 }
180                 if er.End == nil {
181                         *c = -1
182                         continue
183                 }
184                 *c += er.End.Number - er.Start.Number
185         }
186         return
187 }
188
189 func (el EpisodeList) Len() int {
190         return len(el)
191 }
192
193 func (el EpisodeList) Less(i, j int) bool {
194         switch {
195         case el[i] == nil:
196                 return true
197         case el[j] == nil:
198                 return false
199         case el[i].Type < el[j].Type:
200                 return true
201         case el[i].Type > el[j].Type:
202                 return false
203         case el[i].Start.Number < el[j].Start.Number:
204                 return true
205         }
206         return false
207 }
208
209 func (el EpisodeList) Swap(i, j int) {
210         el[i], el[j] = el[j], el[i]
211 }
212
213 func (el *EpisodeList) Add(ec EpisodeContainer) {
214         *el = append(*el, ContainerToList(ec)...)
215         *el = el.Simplify()
216 }
217
218 func (el *EpisodeList) Sub(ep *Episode) {
219         el2 := make(EpisodeList, 0, len(*el))
220         for _, r := range *el {
221                 el2 = append(el2, r.Split(ep)...)
222         }
223         *el = append(*el, el2.Simplify()...)
224 }