001package net.filebot.web;
002
003import static java.util.Arrays.*;
004import static java.util.Collections.*;
005import static java.util.stream.Collectors.*;
006
007import java.io.Serializable;
008import java.net.URL;
009import java.util.Comparator;
010import java.util.List;
011import java.util.Locale;
012import java.util.Objects;
013import java.util.function.Predicate;
014import java.util.stream.Stream;
015
016public class Artwork implements Serializable {
017
018        protected String[] tags;
019        protected URL url;
020
021        protected String language;
022        protected Double rating;
023
024        public Artwork() {
025                // used by serializer
026        }
027
028        public Artwork(Stream<?> tags, URL url, Locale language, Double rating) {
029                this.tags = tags.filter(Objects::nonNull).map(Object::toString).toArray(String[]::new);
030                this.url = url;
031                this.language = getLanguageCode(language);
032                this.rating = rating;
033        }
034
035        public List<String> getTags() {
036                return unmodifiableList(asList(tags));
037        }
038
039        public URL getUrl() {
040                return url;
041        }
042
043        public Locale getLanguage() {
044                return language == null ? null : Locale.forLanguageTag(language);
045        }
046
047        public double getRating() {
048                return rating == null ? 0 : rating;
049        }
050
051        public boolean has(Predicate<String> predicate) {
052                return stream(tags).anyMatch(predicate);
053        }
054
055        public boolean matches(Object... tags) {
056                if (tags == null || tags.length == 0) {
057                        return true;
058                }
059
060                return stream(tags).filter(Objects::nonNull).map(Object::toString).allMatch(tag -> {
061                        return tag.equalsIgnoreCase(language) || has(tag::equalsIgnoreCase);
062                });
063        }
064
065        @Override
066        public int hashCode() {
067                return url.hashCode();
068        }
069
070        @Override
071        public boolean equals(Object other) {
072                if (other instanceof Artwork) {
073                        Artwork artwork = (Artwork) other;
074                        return url.sameFile(artwork.url);
075                }
076                return false;
077        }
078
079        @Override
080        public String toString() {
081                return Stream.of(String.join("/", tags), language, rating, url).filter(Objects::nonNull).map(Object::toString).collect(joining(", ", "[", "]"));
082        }
083
084        public static Comparator<Artwork> ratingOrder() {
085                return Comparator.comparing(Artwork::getRating, reverseOrder());
086        }
087
088        public static Comparator<Artwork> noLanguageOrder() {
089                return Comparator.comparingInt(a -> a.language == null ? 0 : 1);
090        }
091
092        public static Comparator<Artwork> languageOrder(Locale... locale) {
093                // prefer no language
094                if (locale == null || locale.length == 0) {
095                        return noLanguageOrder();
096                }
097
098                // prefer language in order
099                List<String> code = Stream.of(locale).map(Artwork::getLanguageCode).distinct().collect(toList());
100
101                Comparator<Artwork> languageOrder = Comparator.comparingInt(a -> {
102                        int index = code.indexOf(a.language);
103                        return index >= 0 ? index : Integer.MAX_VALUE;
104                });
105
106                // then prefer no language over other languages
107                return languageOrder.thenComparing(noLanguageOrder());
108        }
109
110        public static Comparator<Artwork> relevanceOrder(Locale... locale) {
111                // prefer language in order
112                Comparator<Artwork> order = languageOrder(locale);
113
114                // then prefer higher rated ones
115                return order.thenComparing(ratingOrder());
116        }
117
118        private static String getLanguageCode(Locale locale) {
119                return locale == null || locale.getLanguage().isEmpty() ? null : locale.getLanguage();
120        }
121
122}