001package org.intellimate.izou.sdk.frameworks.music.player;
002
003import org.intellimate.izou.resource.ResourceModel;
004
005import java.util.Arrays;
006import java.util.HashMap;
007import java.util.Optional;
008import java.util.function.BiPredicate;
009
010/**
011 * This class represents the Information about one track, this class is immutable
012 * @author LeanderK
013 * @version 1.0
014 */
015@SuppressWarnings("unused")
016public class TrackInfo {
017    public static final String nameDescriptor = "izou.music.trackinfo.name";
018    private final String name;
019    public static final String artistDescriptor = "izou.music.trackinfo.artist";
020    private final String artist;
021    public static final String albumDescriptor = "izou.music.trackinfo.album";
022    private final String album;
023    public static final String albumCoverDescriptor = "izou.music.trackinfo.albumCover";
024    private final byte[] albumCover;
025    public static final String albumCoverFormatDescriptor = "izou.music.trackinfo.albumCoverFormat";
026    private final String albumCoverFormat;
027    public static final String dataDescriptor = "izou.music.trackinfo.data";
028    private final String data;
029    public static final String yearDescriptor = "izou.music.trackinfo.year";
030    private final String year;
031    public static final String genreDescriptor = "izou.music.trackinfo.genre";
032    private final String genre;
033    public static final String bmpDescriptor = "izou.music.trackinfo.bmp";
034    private final String bmp;
035    public static final String durationDescriptor = "izou.music.trackinfo.duration";
036    private final long duration;
037
038    public TrackInfo(String name) {
039        this.name = name;
040        this.artist = null;
041        this.album = null;
042        albumCover = null;
043        albumCoverFormat = null;
044        data = null;
045        year = null;
046        genre = null;
047        bmp = null;
048        duration = -1;
049    }
050
051    public TrackInfo(String name, String artist, String album) {
052        this.name = name;
053        this.artist = artist;
054        this.album = album;
055        albumCover = null;
056        albumCoverFormat = null;
057        data = null;
058        year = null;
059        genre = null;
060        bmp = null;
061        duration = -1;
062    }
063
064    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat) {
065        this.name = name;
066        this.artist = artist;
067        this.album = album;
068        this.albumCover = albumCover;
069        this.albumCoverFormat = albumCoverFormat;
070        data = null;
071        year = null;
072        genre = null;
073        bmp = null;
074        duration = -1;
075    }
076
077    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String data) {
078        this.name = name;
079        this.artist = artist;
080        this.album = album;
081        this.albumCover = albumCover;
082        this.albumCoverFormat = albumCoverFormat;
083        this.data = data;
084        year = null;
085        genre = null;
086        bmp = null;
087        duration = -1;
088    }
089
090    public TrackInfo(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String data, String year, String genre, String bmp, long duration) {
091        this.name = name;
092        this.artist = artist;
093        this.album = album;
094        this.albumCover = albumCover;
095        this.albumCoverFormat = albumCoverFormat;
096        this.data = data;
097        this.year = year;
098        this.genre = genre;
099        this.bmp = bmp;
100        this.duration = duration;
101    }
102
103    /**
104     * returns a String containing the Name of the Track
105     * @return the optional Name
106     */
107    public Optional<String> getName() {
108        return getOptionalOrEmtpyIfNull(name);
109    }
110
111    /**
112     * returns a String containing the Name of the Artist
113     * @return the optional Artist
114     */
115    public Optional<String> getArtist() {
116        return getOptionalOrEmtpyIfNull(artist);
117    }
118
119    /**
120     * returns a String containing the Name of the Album
121     * @return the optional Artist
122     */
123    public Optional<String> getAlbum() {
124        return getOptionalOrEmtpyIfNull(album);
125    }
126
127    /**
128     * returns the album cover as bytes, format specified by: {@link #getAlbumCoverFormat()}.
129     * @return the optional Album Cover
130     */
131    public Optional<byte[]> getAlbumCover() {
132        return getOptionalOrEmtpyIfNull(albumCover);
133    }
134
135    /**
136     * returns the format of the album cover, which can be obtained by calling {@link #getAlbumCover()}.
137     * @return the optional Format
138     */
139    public Optional<String> getAlbumCoverFormat() {
140        return getOptionalOrEmtpyIfNull(albumCoverFormat);
141    }
142
143    /**
144     * returns the data of the TrackInfo.
145     * <p>
146     * this field is mostly internal, can be an url etc. should be used only if obtained from a player
147     * </p>
148     * @return the optional data
149     */
150    public Optional<String> getData() {
151        return getOptionalOrEmtpyIfNull(data);
152    }
153
154    /**
155     * returns a String containing the Year of the release
156     * @return the optional Year of the release
157     */
158    public Optional<String> getYear() {
159        return getOptionalOrEmtpyIfNull(year);
160    }
161
162    /**
163     * returns a String containing the Genre
164     * @return the optional Genre
165     */
166    public Optional<String> getGenre() {
167        return getOptionalOrEmtpyIfNull(genre);
168    }
169
170    /**
171     * returns a String containing the BMP
172     * @return the optional BMP
173     */
174    public Optional<String> getBmp() {
175        return getOptionalOrEmtpyIfNull(bmp);
176    }
177
178    /**
179     * returns a long containing the Duration in milliseconds
180     * @return the optional Duration
181     */
182    public Optional<Long> getDuration() {
183        if (duration < 0) {
184            return Optional.empty();
185        } else {
186            return Optional.of(duration);
187        }
188    }
189
190    private <T> Optional<T> getOptionalOrEmtpyIfNull(T t) {
191        if (t == null) {
192            return Optional.empty();
193        } else {
194            return Optional.of(t);
195        }
196    }
197
198    /**
199     * retruns true if any of these arguments is already existing and would be overwritten, otherwise retruns false.
200     * The method also returns true if everything is null except the albumCover-Format (maybe known in advance).
201     * @param trackInfo the trackInfo to compare against
202     * @return true if new
203     */
204    public boolean isNew(TrackInfo trackInfo) {
205        return isNew(trackInfo.name,
206                trackInfo.artist,
207                trackInfo.album,
208                trackInfo.albumCover,
209                trackInfo.albumCoverFormat,
210                trackInfo.data,
211                trackInfo.year,
212                trackInfo.genre,
213                trackInfo.bmp,
214                trackInfo.duration);
215    }
216
217    /**
218     * retruns true if any of these arguments is already existing and would be overwritten, otherwise retruns false.
219     * The method also returns true if everything is null except the albumCover-Format (maybe known in advance).
220     * @param name the name
221     * @param artist the artist
222     * @param album the album
223     * @param albumCover the album cover
224     * @param albumCoverFormat the album cover format
225     * @param id the id of the track
226     * @param year the year
227     * @param genre the genre
228     * @param duration the duration
229     * @param bmp the bmp
230     * @return true if new
231     */
232    public boolean isNew(String name, String artist, String album, byte[] albumCover, String albumCoverFormat, String id, String year, String genre, String bmp, long duration) {
233        if (name == null && artist == null && album == null && albumCover == null && id == null)
234            return true;
235        BiPredicate<String, String> compareStrings = (newString, oldString) ->
236                oldString != null && newString != null && !oldString.equals(newString);
237        if (compareStrings.test(name, this.name)) {
238            return true;
239        }
240        if (compareStrings.test(artist, this.artist)) {
241            return true;
242        }
243        if (compareStrings.test(album, this.album)) {
244            return true;
245        }
246        if (compareStrings.test(id, this.data)) {
247            return true;
248        }
249        if (compareStrings.test(albumCoverFormat, this.albumCoverFormat)) {
250            return true;
251        }
252        if (compareStrings.test(year, this.year)) {
253            return true;
254        }
255        if (compareStrings.test(genre, this.genre)) {
256            return true;
257        }
258        if (this.duration > 0 && duration > 0 && this.duration == duration) {
259            return true;
260        }
261        if (compareStrings.test(bmp, this.bmp)) {
262            return true;
263        }
264        if (albumCover != null) {
265            if (this.albumCover == null || !Arrays.equals(albumCover, this.albumCover))
266                return true;
267        }
268        return false;
269    }
270
271    /**
272     * returns a trackinfo if some information was added and not overwritten
273     * @param trackInfo the data to compare
274     * @return a trackinfo if some information was updated
275     */
276    public Optional<TrackInfo> update(TrackInfo trackInfo) {
277        return update(trackInfo.name,
278                trackInfo.artist,
279                trackInfo.album,
280                trackInfo.albumCover,
281                trackInfo.albumCoverFormat,
282                trackInfo.data,
283                trackInfo.year,
284                trackInfo.genre,
285                trackInfo.bmp,
286                trackInfo.duration);
287    }
288
289    /**
290     * returns a trackinfo if some information was added and not overwritten (see isNew) AND a change occurred.
291     * @param name the name
292     * @param artist the artist
293     * @param album the album
294     * @param albumCover the album cover
295     * @param coverFormat the album cover format
296     * @param data the data
297     * @param year the year
298     * @param genre the genre
299     * @param duration the duration
300     * @param bmp the bmp
301     * @return a trackInfo if some information was updated, and an empty optional if: 1. information would be ovewritten
302     * or 2. no change occured
303     */
304    public Optional<TrackInfo> update(String name, String artist, String album, byte[] albumCover, String coverFormat, String data, String year, String genre, String bmp, long duration) {
305        if (isNew(name, artist, album, albumCover, albumCoverFormat, data, year, genre, bmp, duration))
306            return Optional.empty();
307        boolean change = false;
308        if (name != null && !name.equals(this.name)) {
309            change = true;
310        }
311        if (artist != null && !artist.equals(this.artist)) {
312            change = true;
313        }
314        if (album != null && !album.equals(this.album)) {
315            change = true;
316        }
317        if (name != null && !name.equals(this.name)) {
318            change = true;
319        }
320        if (albumCoverFormat != null && !albumCoverFormat.equals(this.albumCoverFormat)) {
321            change = true;
322        }
323        if (data != null && !data.equals(this.data)) {
324            change = true;
325        }
326        if (year != null && !year.equals(this.year)) {
327            change = true;
328        }
329        if (genre != null && !genre.equals(this.genre)) {
330            change = true;
331        }
332        if (duration > 0 && duration != this.duration) {
333            change = true;
334        }
335        if (bmp != null && !bmp.equals(this.bmp)) {
336            change = true;
337        }
338        if (!change)
339            return Optional.empty();
340        return Optional.of(new TrackInfo(
341                this.name == null? name : this.name,
342                this.artist == null? artist : this.artist,
343                this.album == null? album : this.album,
344                this.albumCover == null? albumCover : this.albumCover,
345                this.albumCoverFormat == null? albumCoverFormat : this.albumCoverFormat,
346                this.data == null? data : this.data,
347                this.year == null? year : this.year,
348                this.genre == null? genre : this.genre,
349                this.bmp == null? bmp : this.bmp,
350                this.duration < 0 ? duration : this.duration
351        ));
352    }
353
354    /**
355     * exports the TrackInfo to a Hashmap
356     * @return a HashMap
357     */
358    public HashMap<String, Object> export() {
359        HashMap<String, Object> data = new HashMap<>();
360        data.put(nameDescriptor, name);
361        data.put(artistDescriptor, artist);
362        data.put(albumDescriptor, albumDescriptor);
363        data.put(albumCoverDescriptor, albumCover);
364        data.put(albumCoverFormatDescriptor, albumCoverFormatDescriptor);
365        data.put(dataDescriptor, this.data);
366        data.put(yearDescriptor, this.year);
367        data.put(genreDescriptor, this.genre);
368        data.put(durationDescriptor, this.duration);
369        data.put(bmpDescriptor, this.bmp);
370        return data;
371    }
372
373    /**
374     * returns the optional TrackInfo if the HashMap contains no malformed data
375     * @param hashMap the data to import from
376     * @return an optional
377     */
378    public static Optional<TrackInfo> importFromHashMap(HashMap<String, Object> hashMap) {
379        try {
380            String name = (String) hashMap.get(nameDescriptor);
381            String album = (String) hashMap.get(albumDescriptor);
382            String artist = (String) hashMap.get(artistDescriptor);
383            byte[] albumCover = (byte[]) hashMap.get(albumCoverDescriptor);
384            String albumCoverFormat = (String) hashMap.get(albumCoverFormatDescriptor);
385            String data = (String) hashMap.get(dataDescriptor);
386            String year = (String) hashMap.get(yearDescriptor);
387            String genre = (String) hashMap.get(genreDescriptor);
388            long duration = (Long) hashMap.get(durationDescriptor);
389            String bmp = (String) hashMap.get(bmpDescriptor);
390            return Optional.of(new TrackInfo(name, artist, album, albumCover, albumCoverFormat, data, year, genre, bmp, duration));
391        } catch (ClassCastException e) {
392            return Optional.empty();
393        }
394    }
395
396    /**
397     * creates a TrackInfo from the resourceModel
398     * @param resourceModel the resourceModel
399     * @return the optional TrackInfo
400     */
401    public static Optional<TrackInfo> importFromResource(ResourceModel resourceModel) {
402        Object resource = resourceModel.getResource();
403        try {
404            @SuppressWarnings("unchecked")
405            HashMap<String, Object> hashMap = (HashMap<String, Object>) resource;
406            return importFromHashMap(hashMap);
407        } catch (ClassCastException e) {
408            return Optional.empty();
409        }
410    }
411
412    @Override
413    public boolean equals(Object o) {
414        if (this == o) return true;
415        if (!(o instanceof TrackInfo)) return false;
416
417        TrackInfo trackInfo = (TrackInfo) o;
418
419        if (name != null ? !name.equals(trackInfo.name) : trackInfo.name != null) return false;
420        if (artist != null ? !artist.equals(trackInfo.artist) : trackInfo.artist != null) return false;
421        if (album != null ? !album.equals(trackInfo.album) : trackInfo.album != null) return false;
422        if (year != null ? !year.equals(trackInfo.year) : trackInfo.year != null) return false;
423        if (genre != null ? !genre.equals(trackInfo.genre) : trackInfo.genre != null) return false;
424        if (bmp != null ? !bmp.equals(trackInfo.bmp) : trackInfo.bmp != null) return false;
425        return !(duration > 0l ? duration != trackInfo.duration : trackInfo.duration < 0);
426
427    }
428
429    @Override
430    public int hashCode() {
431        int result = name != null ? name.hashCode() : 0;
432        result = 31 * result + (artist != null ? artist.hashCode() : 0);
433        result = 31 * result + (album != null ? album.hashCode() : 0);
434        result = 31 * result + (year != null ? year.hashCode() : 0);
435        result = 31 * result + (genre != null ? genre.hashCode() : 0);
436        result = 31 * result + (bmp != null ? bmp.hashCode() : 0);
437        result = (int) (31 * result + (duration > 0 ? duration : 0));
438        return result;
439    }
440}