/** does the setup and login with settings credentials */
 public boolean Login() {
   // TODO: when calling login, check for existing auth. on exception reauth ONCE
   TRAKT.setApiKey(CLIENT_ID);
   try {
     TRAKT.setLoginData(Globals.settings.getTraktUsername(), Globals.settings.getTraktPassword());
   } catch (RetrofitError e) {
     handleRetrofitError(e);
     return false;
   } catch (LoginException e) {
     handleRetrofitError((RetrofitError) e.getCause());
     return false;
   } catch (UnauthorizedException e) {
     handleRetrofitError((RetrofitError) e.getCause());
     return false;
   }
   if (LOGGER.isTraceEnabled()) {
     // when we are on TRACE, show some Trakt debug settings... (need to set after login)
     TRAKT.setIsDebug(true);
   }
   return true;
 }
  /**
   * clears the whole Trakt.tv movie collection. Gets all Trakt.tv movies from your collection and
   * removes them from the collection and the watched state; a little helper to initialize the
   * collection
   */
  public void clearTraktTvShows() {
    // *****************************************************************************
    // 1) get ALL Trakt shows in collection / watched
    // *****************************************************************************
    List<BaseShow> traktCollection = new ArrayList<BaseShow>();
    List<BaseShow> traktWatched = new ArrayList<BaseShow>();
    try {
      traktCollection = TRAKT.sync().collectionShows(Extended.DEFAULT_MIN);
      traktWatched = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN);
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      // not authorized - maybe access token revoked - relogin
      if (this.Login()) {
        // ok, it worked, lets try once again :)
        try {
          traktCollection = TRAKT.sync().collectionShows(Extended.DEFAULT_MIN);
          traktWatched = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN);
        } catch (UnauthorizedException e1) {
          return;
        }
      } else {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }
    LOGGER.info("You have " + traktCollection.size() + " shows in your Trakt.tv collection");
    LOGGER.info("You have " + traktWatched.size() + " shows watched");

    // *****************************************************************************
    // 2) remove every shows from the COLLECTION state
    // *****************************************************************************
    List<SyncShow> showToRemove = new ArrayList<SyncShow>();
    for (BaseShow traktShow : traktCollection) {
      showToRemove.add(toSyncShow(traktShow));
    }
    if (!showToRemove.isEmpty()) {
      try {
        SyncItems items = new SyncItems().shows(showToRemove);
        TRAKT.sync().deleteItemsFromCollection(items);
        LOGGER.debug("removed " + showToRemove.size() + " shows from your trakt.tv collection");
      } catch (RetrofitError e) {
        handleRetrofitError(e);
        return;
      } catch (UnauthorizedException e) {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }

    // *****************************************************************************
    // 3) remove every shows from the WATCHED state
    // *****************************************************************************
    showToRemove.clear();
    for (BaseShow traktShow : traktWatched) {
      showToRemove.add(toSyncShow(traktShow));
    }
    if (!showToRemove.isEmpty()) {
      try {
        SyncItems items = new SyncItems().shows(showToRemove);
        TRAKT.sync().deleteItemsFromWatchedHistory(items);
        LOGGER.debug("removed " + showToRemove.size() + " shows from your trakt.tv watched");
      } catch (RetrofitError e) {
        handleRetrofitError(e);
        return;
      } catch (UnauthorizedException e) {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }
  }
  /**
   * Syncs Trakt.tv "seen" flag (all gives you have already marked as watched)<br>
   * Gets all watched movies from Trakt, and sets the "watched" flag on TMM movies.<br>
   * Then update the remaining TMM movies on Trakt as 'seen'.
   */
  public void syncTraktMovieWatched(List<Movie> moviesInTmm) {
    if (!isEnabled()) {
      return;
    }

    // create a local copy of the list
    List<Movie> tmmMovies = new ArrayList<Movie>(moviesInTmm);

    // *****************************************************************************
    // 1) get all Trakt watched movies and update our "watched" status
    // *****************************************************************************
    List<BaseMovie> traktMovies = new ArrayList<BaseMovie>();
    try {
      traktMovies = TRAKT.sync().watchedMovies(Extended.DEFAULT_MIN);
      // Extended.DEFAULT adds url, poster, fanart, banner, genres
      // Extended.MAX adds certs, runtime, and other stuff (useful for scraper!)
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      // not authorized - maybe access token revoked - relogin
      if (this.Login()) {
        // ok, it worked, lets try once again :)
        try {
          traktMovies = TRAKT.sync().watchedMovies(Extended.DEFAULT_MIN);
        } catch (UnauthorizedException e1) {
          return;
        }
      } else {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }
    LOGGER.info(
        "You have "
            + traktMovies.size()
            + " movies marked as 'watched' in your Trakt.tv collection");

    // loop over all watched movies on trakt
    for (BaseMovie traktWatched : traktMovies) {

      // loop over TMM movies, and check if IMDBID match
      for (Movie tmmMovie : tmmMovies) {

        if (matches(tmmMovie, traktWatched.movie.ids)) {
          // we have a movie match

          // update missing IDs (we get them for free :)
          boolean dirty = updateIDs(tmmMovie, traktWatched.movie.ids);

          if (!tmmMovie.isWatched()) {
            // save Trakt watched status
            LOGGER.info("Marking movie '" + tmmMovie.getTitle() + "' as watched");
            tmmMovie.setWatched(true);
            dirty = true;
          }
          if (traktWatched.last_watched_at != null
              && !(traktWatched.last_watched_at.toDate().equals(tmmMovie.getLastWatched()))) {
            // always set from trakt, if not matched (Trakt = master)
            LOGGER.trace(
                "Marking movie '"
                    + tmmMovie.getTitle()
                    + "' as watched on "
                    + traktWatched.last_watched_at.toDate()
                    + " (was "
                    + tmmMovie.getLastWatched()
                    + ")");
            tmmMovie.setLastWatched(traktWatched.last_watched_at.toDate());
            dirty = true;
          }

          if (dirty) {
            tmmMovie.saveToDb();
          }
        }
      }
    }

    // *****************************************************************************
    // 2) mark additionally "watched" movies as 'seen' on Trakt
    // *****************************************************************************
    // Now get all TMM watched movies...
    List<Movie> tmmWatchedMovies = new ArrayList<Movie>();
    for (Movie movie : tmmMovies) {
      if (movie.isWatched()) {
        tmmWatchedMovies.add(movie);
      }
    }
    LOGGER.info(
        "You have now "
            + tmmWatchedMovies.size()
            + " movies marked as 'watched' in your TMM database");

    // ...and subtract the already watched from Trakt
    for (int i = tmmWatchedMovies.size() - 1; i >= 0; i--) {
      for (BaseMovie traktWatched : traktMovies) {
        Movie tmmMovie = tmmWatchedMovies.get(i);
        if (matches(tmmMovie, traktWatched.movie.ids)) {
          tmmWatchedMovies.remove(i);
          break;
        }
      }
    }

    if (tmmWatchedMovies.size() == 0) {
      LOGGER.info("no new watched movies for Trakt sync found.");
      return;
    }

    LOGGER.debug("prepare " + tmmWatchedMovies.size() + " movies for Trakt.tv sync");
    List<SyncMovie> movies = new ArrayList<SyncMovie>();
    int nosync = 0;
    for (Movie tmmMovie : tmmWatchedMovies) {
      if (tmmMovie.getIdAsInt(Constants.TRAKTID) != 0
          || !tmmMovie.getIdAsString(Constants.IMDBID).isEmpty()
          || tmmMovie.getIdAsInt(Constants.TMDBID) != 0) {
        movies.add(toSyncMovie(tmmMovie, true));
      } else {
        // do not add to Trakt if we do not have at least one ID
        nosync++;
        continue;
      }
    }
    if (nosync > 0) {
      LOGGER.debug("skipping " + nosync + " movies, because they have not been scraped yet!");
    }

    if (movies.size() == 0) {
      LOGGER.info("no new watched movies for Trakt sync found.");
      return;
    }

    try {
      LOGGER.info("Marking " + movies.size() + " movies as 'watched' to Trakt.tv collection");
      SyncItems items = new SyncItems().movies(movies);
      response = TRAKT.sync().addItemsToWatchedHistory(items);
      LOGGER.info("Trakt mark-as-watched status:");
      printStatus(response);
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      handleRetrofitError((RetrofitError) e.getCause());
      return;
    }
  }
  public void syncTraktTvShowWatched(List<TvShow> tvShowsInTmm) {
    if (!isEnabled()) {
      return;
    }

    // create a local copy of the list
    List<TvShow> tvShows = new ArrayList<TvShow>(tvShowsInTmm);

    List<BaseShow> traktShows = new ArrayList<BaseShow>();
    try {
      traktShows = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN);
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      // not authorized - maybe access token revoked - relogin
      if (this.Login()) {
        // ok, it worked, lets try once again :)
        try {
          traktShows = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN);
        } catch (UnauthorizedException e1) {
          return;
        }
      } else {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }
    LOGGER.info("You have " + traktShows.size() + " TvShows marked as watched on Trakt.tv");
    for (BaseShow traktShow : traktShows) {
      for (TvShow tmmShow : tvShows) {

        if (matches(tmmShow, traktShow.show.ids)) {
          // ok, we have a show match

          // update show IDs from trakt
          boolean dirty = updateIDs(tmmShow, traktShow.show.ids);

          // update watched date from trakt (show)
          if (traktShow.last_watched_at != null
              && !(traktShow.last_watched_at.toDate().equals(tmmShow.getLastWatched()))) {
            // always set from trakt, if not matched (Trakt = master)
            LOGGER.trace(
                "Marking TvShow '"
                    + tmmShow.getTitle()
                    + "' as watched on "
                    + traktShow.last_watched_at.toDate()
                    + " (was "
                    + tmmShow.getLastWatched()
                    + ")");
            tmmShow.setLastWatched(traktShow.last_watched_at.toDate());
            dirty = true;
          }

          // update collection date from trakt (episodes)
          for (BaseSeason bs : traktShow.seasons) {
            for (BaseEpisode be : bs.episodes) {
              TvShowEpisode tmmEP = tmmShow.getEpisode(bs.number, be.number);
              // update ep IDs - NOT YET POSSIBLE
              // boolean dirty = updateIDs(tmmEP, be.ids);

              if (tmmEP != null
                  && be.last_watched_at != null
                  && !(be.last_watched_at.toDate().equals(tmmEP.getLastWatched()))) {
                tmmEP.setLastWatched(be.last_watched_at.toDate());
                tmmEP.setWatched(true);
                dirty = true;
              }
            }
          }

          if (dirty) {
            tmmShow.saveToDb();
          }
        }
      }
    }

    // *****************************************************************************
    // 2) add all our shows to Trakt watched
    // *****************************************************************************
    LOGGER.info("Adding " + tvShows.size() + " TvShows as watched on Trakt.tv");
    // send show per show; sending all together may result too often in a timeout
    for (TvShow show : tvShows) {
      // get items to sync
      SyncShow sync = toSyncShow(show, true);
      if (sync == null) {
        continue;
      }

      try {
        SyncItems items = new SyncItems().shows(sync);
        response = TRAKT.sync().addItemsToWatchedHistory(items);

        LOGGER.debug("Trakt add-to-library status: " + show.getTitle());
        printStatus(response);
      } catch (RetrofitError e) {
        handleRetrofitError(e);
      } catch (UnauthorizedException e) {
        handleRetrofitError((RetrofitError) e.getCause());
      }
    }
  }
  /**
   * Syncs Trakt.tv collection (specified movies)<br>
   * Gets all Trakt movies from collection, matches them to ours, and sends ONLY the new ones back
   * to Trakt
   */
  public void syncTraktMovieCollection(List<Movie> moviesInTmm) {
    if (!isEnabled()) {
      return;
    }

    // create a local copy of the list
    List<Movie> tmmMovies = new ArrayList<Movie>(moviesInTmm);
    // *****************************************************************************
    // 1) get diff of TMM <-> Trakt collection
    // *****************************************************************************
    LOGGER.info("got " + tmmMovies.size() + " movies for Trakt.tv collection sync");

    // get ALL Trakt movies in collection
    List<BaseMovie> traktMovies = new ArrayList<BaseMovie>();

    try {
      traktMovies = TRAKT.sync().collectionMovies(Extended.DEFAULT_MIN);
      // Extended.DEFAULT adds url, poster, fanart, banner, genres
      // Extended.MAX adds certs, runtime, and other stuff (useful for scraper!)
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      // not authorized - maybe access token revoked - relogin
      if (this.Login()) {
        // ok, it worked, lets try once again :)
        try {
          traktMovies = TRAKT.sync().collectionMovies(Extended.DEFAULT_MIN);
        } catch (UnauthorizedException e1) {
          return;
        }
      } else {
        handleRetrofitError((RetrofitError) e.getCause());
        return;
      }
    }
    LOGGER.info("You have " + traktMovies.size() + " movies in your Trakt.tv collection");

    // loop over all movies on trakt
    for (BaseMovie traktMovie : traktMovies) {
      // loop over TMM movies, and check if IMDBID match
      for (int i = tmmMovies.size() - 1; i >= 0; i--) {
        Movie tmmMovie = tmmMovies.get(i);

        if (matches(tmmMovie, traktMovie.movie.ids)) {
          // we have a movie match

          // update missing IDs (we get them for free :)
          boolean dirty = updateIDs(tmmMovie, traktMovie.movie.ids);

          if (traktMovie.collected_at != null
              && !(traktMovie.collected_at.toDate().equals(tmmMovie.getDateAdded()))) {
            // always set from trakt, if not matched (Trakt = master)
            LOGGER.trace(
                "Marking movie '"
                    + tmmMovie.getTitle()
                    + "' as collected on "
                    + traktMovie.collected_at.toDate()
                    + " (was "
                    + tmmMovie.getDateAddedAsString()
                    + ")");
            tmmMovie.setDateAdded(traktMovie.collected_at.toDate());
            dirty = true;
          }

          if (dirty) {
            tmmMovie.saveToDb();
          }

          // remove it from our list (no need to add)
          tmmMovies.remove(i);
        }
      }
    }

    if (tmmMovies.size() == 0) {
      LOGGER.info("Already up-to-date - no need to add anything :)");
      return;
    }

    // *****************************************************************************
    // 2) add remaining TMM movies to Trakt collection
    // *****************************************************************************
    LOGGER.debug("prepare " + tmmMovies.size() + " movies for Trakt.tv collection sync");

    List<SyncMovie> movies = new ArrayList<SyncMovie>();
    int nosync = 0;
    for (Movie tmmMovie : tmmMovies) {
      if (tmmMovie.getIdAsInt(Constants.TRAKTID) != 0
          || !tmmMovie.getIdAsString(Constants.IMDBID).isEmpty()
          || tmmMovie.getIdAsInt(Constants.TMDBID) != 0) {
        movies.add(toSyncMovie(tmmMovie, false));
      } else {
        // do not add to Trakt if we do not have at least one ID
        nosync++;
        continue;
      }
    }
    if (nosync > 0) {
      LOGGER.debug("skipping " + nosync + " movies, because they have not been scraped yet!");
    }

    if (movies.size() == 0) {
      LOGGER.info("no new movies for Trakt collection sync found.");
      return;
    }

    try {
      LOGGER.info("Adding " + movies.size() + " movies to Trakt.tv collection");
      SyncItems items = new SyncItems().movies(movies);
      response = TRAKT.sync().addItemsToCollection(items);
      LOGGER.info("Trakt add-to-library status:");
      printStatus(response);
    } catch (RetrofitError e) {
      handleRetrofitError(e);
      return;
    } catch (UnauthorizedException e) {
      handleRetrofitError((RetrofitError) e.getCause());
      return;
    }
  }