content: package allen.town.podcast.model.feed;
  
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Locale;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  public class FeedFilter implements Serializable {
      private final String includeFilter;
      private final String excludeFilter;
      private final int minimalDuration;
  
      public FeedFilter() {
          this("", "", -1);
      }
  
      public FeedFilter(String includeFilter, String excludeFilter, int minimalDuration) {
          // We're storing the strings and not the parsed terms because
          // 1. It's easier to show the user exactly what they typed in this way
          //    (we don't have to recreate it)
          // 2. We don't know if we'll actually be asked to parse anything anyways.
          this.includeFilter = includeFilter;
          this.excludeFilter = excludeFilter;
          this.minimalDuration = minimalDuration;
      }
  
      public FeedFilter(String includeFilter, String excludeFilter) {
          this(includeFilter, excludeFilter, -1);
      }
  
      /**
       * Parses the text in to a list of single words or quoted strings.
       * Example: "One "Two Three"" returns ["One", "Two Three"]
       * @param filter string to parse in to terms
       * @return list of terms
       */
      private List<String> parseTerms(String filter) {
          // from http://stackoverflow.com/questions/7804335/split-string-on-spaces-in-java-except-if-between-quotes-i-e-treat-hello-wor
          List<String> list = new ArrayList<>();
          Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(filter);
          while (m.find()) {
              list.add(m.group(1).replace("\"", ""));
          }
          return list;
      }
  
      /**
       * @param item
       * @return true if the item should be downloaded
       */
      public boolean shouldAutoDownload(FeedItem item) {
  
          List<String> includeTerms = parseTerms(includeFilter);
          List<String> excludeTerms = parseTerms(excludeFilter);
  
          if (includeTerms.size() == 0 && excludeTerms.size() == 0 && minimalDuration <= -1) {
              // nothing has been specified, so include everything
              return true;
          }
  
          // Check if the episode is long enough if minimal duration filter is on
          if (hasMinimalDurationFilter() && item.getMedia() != null) {
              int durationInMs = item.getMedia().getDuration();
              // Minimal Duration is stored in seconds
              if (durationInMs > 0 && durationInMs / 1000 < minimalDuration) {
                  return false;
              }
          }
  
          // check using lowercase so the users don't have to worry about case.
          String title = item.getTitle().toLowerCase(Locale.getDefault());
  
          // if it's explicitly excluded, it shouldn't be autodownloaded
          // even if it has include terms
          for (String term : excludeTerms) {
              if (title.contains(term.trim().toLowerCase(Locale.getDefault()))) {
                  return false;
              }
          }
  
          for (String term : includeTerms) {
              if (title.contains(term.trim().toLowerCase(Locale.getDefault()))) {
                  return true;
              }
          }
  
          // now's the tricky bit
          // if they haven't set an include filter, but they have set an exclude filter
          // default to including, but if they've set both, then exclude
          if (!hasIncludeFilter() && hasExcludeFilter()) {
              return true;
          }
  
          // if they only set minimal duration filter and arrived here, autodownload
          // should happen
          if (hasMinimalDurationFilter()) {
              return true;
          }
  
          return false;
      }
  
      public String getIncludeFilterRaw() {
          return includeFilter;
      }
  
      public String getExcludeFilterRaw() {
          return excludeFilter;
      }
  
      public List<String> getIncludeFilter() {
          return includeFilter == null ? new ArrayList<>() : parseTerms(includeFilter);
      }
  
      public List<String> getExcludeFilter() {
          return excludeFilter == null ? new ArrayList<>() : parseTerms(excludeFilter);
      }
  
      public int getMinimalDurationFilter() {
          return minimalDuration;
      }
  
      /**
       * @return true if only include is set
       */
      public boolean includeOnly() {
          return hasIncludeFilter() && !hasExcludeFilter();
      }
  
      /**
       * @return true if only exclude is set
       */
      public boolean excludeOnly() {
          return hasExcludeFilter() && !hasIncludeFilter();
      }
  
      public boolean hasIncludeFilter() {
          return includeFilter.length() > 0;
      }
  
      public boolean hasExcludeFilter() {
          return excludeFilter.length() > 0;
      }
  
      public boolean hasMinimalDurationFilter() {
          return minimalDuration > -1;
      }
  }
  