1 #pragma once
2 
3 #include <vector>
4 #include <set>
5 #include <deque>
6 #include <cstdlib>
7 #include <ctime>
8 #include <cassert>
9 #include <algorithm>
10 //#include <iostream>
11 
12 #define MOUS_HAS(container, var) \
13     (container.find(var) != container.end())
14 
15 #define MOUS_FIND(container, var) \
16     std::find(container.begin(), container.end(), var)
17 
18 #define MOUS_CONTAINS(container, var) \
19     (std::find(container.begin(), container.end(), var) != container.end())
20 
21 namespace mous {
22 
23 enum class PlaylistMode : uint8_t
24 {
25     Normal,
26     Repeat,
27     Shuffle,
28     ShuffleRepeat,
29     RepeatOne,
30     Top,
31 };
32 
ToString(PlaylistMode mode)33 inline static std::string ToString(PlaylistMode mode)
34 {
35     switch (mode) {
36         case PlaylistMode::Normal:
37             return "Normal";
38 
39         case PlaylistMode::Repeat:
40             return "Repeat";
41 
42         case PlaylistMode::Shuffle:
43             return "Shuffle";
44 
45         case PlaylistMode::ShuffleRepeat:
46             return "Shuffle Repeat";
47 
48         case PlaylistMode::RepeatOne:
49             return "Repeat One";
50 
51         default:
52             return "";
53     }
54 }
55 
56 template <typename item_t>
57 class PlaylistSerializer;
58 
59 /*
60  * NOTE: Playlist<int> won't be compiled!
61  */
62 template <typename item_t>
63 class Playlist
64 {
65     friend class PlaylistSerializer<item_t>;
66 
67 public:
68     Playlist() = default;
69     ~Playlist() = default;
70 
SetMode(PlaylistMode mode)71     void SetMode(PlaylistMode mode)
72     {
73         if (m_SeqIndex >= 0 && (size_t)m_SeqIndex < m_ItemQueue.size())  {
74             std::set<PlaylistMode> normalSet { PlaylistMode::Normal, PlaylistMode::Repeat, PlaylistMode::RepeatOne };
75             std::set<PlaylistMode> shuffleSet { PlaylistMode::Shuffle, PlaylistMode::ShuffleRepeat };
76 
77             // normal <=> shuffle
78             if (MOUS_HAS(normalSet, m_Mode) && MOUS_HAS(shuffleSet, mode)) {
79                 m_SeqIndex = MOUS_FIND(m_SeqShuffleQueue, m_SeqIndex) - m_SeqShuffleQueue.begin();
80             } else if (MOUS_HAS(shuffleSet, m_Mode) && MOUS_HAS(normalSet, mode)) {
81                 m_SeqIndex = m_SeqShuffleQueue[m_SeqIndex];
82             }
83         }
84 
85         m_Mode = mode;
86     }
87 
Mode()88     PlaylistMode Mode() const
89     {
90         return m_Mode;
91     }
92 
93     /* check whether there is a next item according to current play mode
94      *
95      * offset:
96      *  < 0 previous
97      *  = 0 current
98      *  > 0 next
99      *
100      * NOTE: this method should be called before NextItem()
101      *
102      */
HasNext(int offset)103     bool HasNext(int offset) const
104     {
105         return (OffsetToIndex(offset) >= 0);
106     }
107 
NextItem(int offset,bool moveTo)108     const item_t& NextItem(int offset, bool moveTo) const
109     {
110         return (const_cast<Playlist*>(this))->NextItem(offset, moveTo);
111     }
112 
NextItem(int offset,bool moveTo)113     item_t& NextItem(int offset, bool moveTo)
114     {
115         int index = OffsetToIndex(offset);
116         assert(index >= 0 && (size_t)index < m_ItemQueue.size());
117 
118         if (moveTo)
119             m_SeqIndex = index;
120 
121         switch (m_Mode) {
122             case PlaylistMode::Shuffle:
123             case PlaylistMode::ShuffleRepeat:
124                 index = m_SeqShuffleQueue[index];
125                 break;
126 
127             default:
128                 break;
129         }
130 
131         return m_ItemQueue[index];
132     }
133 
134     /* try to set index */
JumpTo(int index)135     bool JumpTo(int index)
136     {
137         if (index >= 0 && (size_t)index < m_ItemQueue.size()) {
138             switch (m_Mode) {
139                 case PlaylistMode::Normal:
140                 case PlaylistMode::Repeat:
141                 case PlaylistMode::RepeatOne:
142                     m_SeqIndex = index;
143                     break;
144 
145                 case PlaylistMode::Shuffle:
146                 case PlaylistMode::ShuffleRepeat:
147                     m_SeqIndex = MOUS_FIND(m_SeqShuffleQueue, index) - m_SeqShuffleQueue.begin();
148                     break;
149 
150                 default:
151                     break;
152             }
153             return true;
154         } else {
155             return false;
156         }
157     }
158 
159     /* current index */
CurrentIndex()160     int CurrentIndex() const
161     {
162         if (m_SeqIndex < 0)
163             return -1;
164 
165         switch (m_Mode) {
166             case PlaylistMode::Normal:
167             case PlaylistMode::Repeat:
168             case PlaylistMode::RepeatOne:
169                 return m_SeqIndex;
170 
171             case PlaylistMode::Shuffle:
172             case PlaylistMode::ShuffleRepeat:
173                 return m_SeqShuffleQueue[m_SeqIndex];
174 
175             default:
176                 return -1;
177         }
178     }
179 
180     template<class Array>
Assign(const Array & items)181     void Assign(const Array& items)
182     {
183         m_ItemQueue.assign(items.begin(), items.end());
184         AdjustSeqPosition();
185         AdjustShuffleRange(true);
186     }
187 
Move(int oldPos,int newPos)188     void Move(int oldPos, int newPos)
189     {
190         Move(std::vector<int>(1, oldPos), newPos);
191     }
192 
193     template<class Array>
Move(Array oldPos,int newPos)194     void Move(Array oldPos, int newPos)
195     {
196         if (m_ItemQueue.size() < 2)
197             return;
198 
199         std::sort(oldPos.begin(), oldPos.end());
200 
201         int realNewPos = newPos;
202         for (size_t i = 0; i < oldPos.size(); ++i) {
203             if (oldPos[i] < newPos)
204                 --realNewPos;
205         }
206 
207         // backup & remove & insert
208         std::deque<item_t> tmpList(oldPos.size());
209         for (size_t i = 0; i < oldPos.size(); ++i) {
210             tmpList[i] = std::move(m_ItemQueue[oldPos[i]]);
211         }
212         Remove(oldPos);
213         Insert(realNewPos, tmpList);
214 
215         // calc new seq index
216         int seqIndex = (m_Mode == PlaylistMode::Shuffle
217                 || m_Mode == PlaylistMode::ShuffleRepeat) ?
218             m_SeqShuffleQueue[m_SeqIndex] : m_SeqIndex;
219 
220         const auto iter = std::find(oldPos.begin(), oldPos.end(), seqIndex);
221         if (iter != oldPos.end()) {
222             seqIndex = realNewPos + (iter - oldPos.begin());
223         } else {
224             int tmp = seqIndex;
225             for (size_t i = 0; i < oldPos.size(); ++i) {
226                 if (oldPos[i] < seqIndex)
227                     --tmp;
228                 else
229                     break;
230             }
231             if (realNewPos <= tmp)
232                 tmp += oldPos.size();
233             seqIndex = tmp;
234         }
235 
236         assert(seqIndex >= 0 && (size_t)seqIndex < m_ItemQueue.size());
237 
238         m_SeqIndex = (m_Mode == PlaylistMode::Shuffle
239                 || m_Mode == PlaylistMode::ShuffleRepeat) ?
240             MOUS_FIND(m_SeqShuffleQueue, seqIndex) - m_SeqShuffleQueue.begin() : seqIndex;
241     }
242 
Insert(int index,const item_t & item)243     void Insert(int index, const item_t& item)
244     {
245         m_ItemQueue.insert(m_ItemQueue.begin()+index, item);
246         AdjustSeqPosition();
247         AdjustShuffleRange();
248     }
249 
Insert(int index,item_t && item)250     void Insert(int index, item_t&& item)
251     {
252         m_ItemQueue.insert(m_ItemQueue.begin()+index, std::move(item));
253         AdjustSeqPosition();
254         AdjustShuffleRange();
255     }
256 
257     template<class Array>
Insert(int index,const Array & array)258     void Insert(int index, const Array& array)
259     {
260         m_ItemQueue.insert(m_ItemQueue.begin()+index, array.begin(), array.end());
261         AdjustSeqPosition();
262         AdjustShuffleRange();
263     }
264 
Append(const item_t & item)265     void Append(const item_t& item)
266     {
267         m_ItemQueue.push_back(item);
268         AdjustSeqPosition();
269         AdjustShuffleRange();
270     }
271 
Append(item_t && item)272     void Append(item_t&& item)
273     {
274         m_ItemQueue.push_back(std::move(item));
275         AdjustSeqPosition();
276         AdjustShuffleRange();
277     }
278 
279     template<class Array>
Append(const Array & array)280     void Append(const Array& array)
281     {
282         m_ItemQueue.insert(m_ItemQueue.end(), array.begin(), array.end());
283         AdjustSeqPosition();
284         AdjustShuffleRange();
285     }
286 
Remove(const item_t & item)287     bool Remove(const item_t& item)
288     {
289         for (size_t i = 0; i < m_ItemQueue.size(); ++i) {
290             if (item == m_ItemQueue[i]) {
291                 m_ItemQueue.erase(m_ItemQueue.begin() + i);
292                 AdjustSeqPosition();
293                 AdjustShuffleRange();
294                 return true;
295             }
296         }
297         return false;
298     }
299 
Remove(int index)300     void Remove(int index)
301     {
302         m_ItemQueue.erase(m_ItemQueue.begin() + index);
303         AdjustSeqPosition();
304         AdjustShuffleRange();
305     }
306 
307     template<class Array>
Remove(const Array & array)308     void Remove(const Array& array)
309     {
310         for (int i = array.size()-1; i >= 0; --i) {
311             m_ItemQueue.erase(m_ItemQueue.begin() + array[i]);
312         }
313         AdjustSeqPosition();
314         AdjustShuffleRange();
315     }
316 
Clear()317     void Clear()
318     {
319         m_ItemQueue.clear();
320         m_SeqShuffleQueue.clear();
321         AdjustSeqPosition();
322     }
323 
At(int index)324     item_t& At(int index)
325     {
326         return m_ItemQueue.at(index);
327     }
328 
At(int index)329     const item_t& At(int index) const
330     {
331         return m_ItemQueue.at(index);
332     }
333 
334     item_t& operator[](int index)
335     {
336         return m_ItemQueue[index];
337     }
338 
339     const item_t& operator[](int index) const
340     {
341         return m_ItemQueue[index];
342     }
343 
Items()344     const std::deque<item_t>& Items() const
345     {
346         return m_ItemQueue;
347     }
348 
Items()349     std::deque<item_t>& Items()
350     {
351         return m_ItemQueue;
352     }
353 
Count()354     int Count() const
355     {
356         int size = m_ItemQueue.size();
357         return size;
358     }
359 
Empty()360     bool Empty() const
361     {
362         bool empty = m_ItemQueue.empty();
363         return empty;
364     }
365 
Reverse()366     void Reverse()
367     {
368         reverse(m_ItemQueue.begin(), m_ItemQueue.end());
369     }
370 
371 private:
OffsetToIndex(int offset)372     int OffsetToIndex(int offset) const
373     {
374         if (m_ItemQueue.empty())
375             return -1;
376 
377         int idx = -1;
378         switch (m_Mode) {
379             case PlaylistMode::Normal:
380             case PlaylistMode::Shuffle:
381                 idx = m_SeqIndex + offset;
382                 break;
383 
384             case PlaylistMode::Repeat:
385             case PlaylistMode::ShuffleRepeat:
386                 idx = (m_SeqIndex + offset) % m_ItemQueue.size();
387                 break;
388 
389             case PlaylistMode::RepeatOne:
390                 idx = m_SeqIndex;
391                 break;
392 
393             default:
394                 break;
395         }
396 
397         return (idx >= 0 && (size_t)idx < m_ItemQueue.size()) ? idx : -1;
398     }
399 
AdjustSeqPosition()400     void AdjustSeqPosition()
401     {
402         if (m_ItemQueue.empty()) {
403             m_SeqIndex = -1;
404         };
405     }
406 
407     void AdjustShuffleRange(bool reGenerate = false)
408     {
409         if (m_ItemQueue.empty())
410             return;
411 
412         if (reGenerate)
413             m_SeqShuffleQueue.clear();
414 
415         int need = m_ItemQueue.size() - m_SeqShuffleQueue.size();
416         if (need > 0) {
417             srandom(time(nullptr));
418             for (int i = 0; i < need; ++i) {
419                 int inspos = random() % (m_SeqShuffleQueue.size()+1);
420                 m_SeqShuffleQueue.insert(m_SeqShuffleQueue.begin()+inspos, m_SeqShuffleQueue.size());
421             }
422         } else if (need < 0){
423             int top = m_ItemQueue.size();
424             for (int i = m_SeqShuffleQueue.size()-1; i >= 0; --i) {
425                 if (m_SeqShuffleQueue[i] >= top)
426                     m_SeqShuffleQueue.erase(m_SeqShuffleQueue.begin() + i);
427             }
428         }
429 
430         assert(m_ItemQueue.size() == m_SeqShuffleQueue.size());
431 
432         // debug
433         /*
434         using namespace std;
435         cout << "shuffle:";
436         for (size_t i = 0; i < m_SeqShuffleQueue.size(); ++i) {
437             cout << m_SeqShuffleQueue[i] << ", ";
438         }
439         cout << endl;
440         */
441     }
442 
443 private:
444     PlaylistMode m_Mode = PlaylistMode::Normal;
445 
446     std::deque<item_t> m_ItemQueue;
447 
448     int m_SeqIndex = -1;
449     std::deque<int> m_SeqShuffleQueue;
450 
451 };
452 
453 }
454 
455 #undef MOUS_HAS
456 #undef MOUS_FIND
457 #undef MOUS_CONTAINS
458