/* Copyright (C) 2015 Robert Lehmann <lehmrob@gmail.com>
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
package feedmeyoutubecrawler.crawler;

import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Channel;
import com.google.api.services.youtube.model.PlaylistItem;
import com.google.api.services.youtube.model.PlaylistItemListResponse;
import com.google.common.collect.Lists;
import feedmeyoutubecore.AppConfig;
import feedmeyoutubecore.obj.YouTubeVideo;
import org.aeonbits.owner.ConfigFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Crawler for all youtube videos. This class is responsible for crawling all
 * youtube videos from the configured channel.
 *
 * @author Robert Lehmann
 * @since 1.0
 */
public class VideoCrawler {
    private YouTubeConnection _connection;
    private Channel _myChannel;
    private final YouTube.PlaylistItems.List _videosRequest;
    private String _nextToken = "";

    private static final String APP_NAME = "feedmeyoutube";
    private static final Logger LOG = LoggerFactory.getLogger(Crawler.class);
    private static final AppConfig CFG = ConfigFactory.create(AppConfig.class);

    private final static List<String> scopes = Lists.newArrayList(
            "https://www.googleapis.com/auth/youtube.readonly");

    /**
     * Constructor.
     *
     * @param connection instance of {@link YouTubeConnection}
     * @throws IOException Error occured during the authorisation
     * @since 1.0
     */
    public VideoCrawler(final YouTubeConnection connection) throws IOException {
        init(connection);
        _videosRequest = createPlaylistItemsRequest(_myChannel.
                getContentDetails().getRelatedPlaylists().getUploads());
    }

    /**
     * Constructor for a custom playlist.
     *
     * @param connection {@link YouTubeConnection} instance which stands for
     * the connection to the youtube servers<
     *
     * @since 1.0
     */
    public VideoCrawler(final YouTubeConnection connection, final String playlistId)
        throws IOException {
        init(connection);
        _videosRequest = createPlaylistItemsRequest(playlistId);
    }

    /**
     * Get the next videos in the uploaded playlist
     *
     * @return {@link List} with crawled youtube videos.
     *
     * @throws IOException Can't connect to the youtube servers
     * @since 1.0
     */
    public List<YouTubeVideo> getNextVideos() throws IOException {
        final List<PlaylistItem> playlistItems = new ArrayList<>();

        _videosRequest.setPageToken(_nextToken);
        PlaylistItemListResponse playlistResponse
                = _videosRequest.execute();
        playlistItems.addAll(playlistResponse.getItems());
        _nextToken = playlistResponse.getNextPageToken();

        return mapVideos(playlistItems);
    }

    /**
     * Check if there is an next page availible
     *
     * @return {@code true} if more videos are availible
     *
     * @since 1.0
     */
    public boolean hasNext() {
        return _nextToken != null;
    }

    /**
     * Init the local attributes
     *
     * @param connection Abstranction for the connection to the youtube servers
     *
     * @since 1.0
     */
    private void init(final YouTubeConnection connection) throws IOException {
        _connection = connection;
        _myChannel = CrawlerUtils.getMyChannel(_connection);
        LOG.debug("My channel {}", _myChannel.toPrettyString());
    }

    /**
     * Map an {@link List} of {@link PlaylistItem}s to an {@link List} of
     * {@link YouTubeVideo}s
     *
     * @param playlistItems {@link List} of {@link PlaylistItem}s which will be
     *                      mapped
     * @return {@link List} of mapped {@link YouTubeVideo}
     *
     * @since 1.0
     */
    private static List<YouTubeVideo> mapVideos(
            final List<PlaylistItem> playlistItems) {
        final List<YouTubeVideo> mappedVids = new ArrayList<>();

        playlistItems.stream().forEach((item) -> mappedVids.add(mapVideo(item)));

        return mappedVids;
    }

    /**
     * Map an {@link PlaylistItem} to an {@link YouTubeVideo}
     *
     * @param item {@link PlaylistItem} which will be mapped
     * @return {@link YouTubeVideo} which will be the result of the mapping
     *
     * @since 1.0
     */
    private static YouTubeVideo mapVideo(final PlaylistItem item) {
        return new YouTubeVideo(item.getContentDetails().getVideoId(), item.
                getSnippet().getTitle(), item.getSnippet().getDescription(),
                null);
    }

    /**
     * Creates an request to get {@link PlaylistItem}s for the playlist id. If
     * the playlist id is the id of the uploaded videos than the methode creates
     * an request to get all uploaded videos.
     *
     * @param playlistId {@link String} with the id of the correspondending
     *                   playlist.
     * @return {@link PlaylistItem} request to get all elements of the playlist.
     *
     * @throws IOException An error occurs while creating the request.
     *
     * @since 1.0
     */
    private YouTube.PlaylistItems.List createPlaylistItemsRequest(
            final String playlistId) throws IOException {
        final YouTube.PlaylistItems.List request = _connection.getYouTube()
                .playlistItems().list("id,contentDetails,snippet");
        request.setMaxResults(5L);
        request.setPlaylistId(playlistId);
        request.setFields("items(contentDetails/videoId,snippet/title, "
                + "snippet/description, snippet/publishedAt),"
                + "nextPageToken, pageInfo");
        return request;
    }

    /**
     * Gets the ID of the upload playlist. Reads the id of the uploaded video
     * playlist from {@link Channel}.
     *
     * @return ID of the uploaded video playlist
     *
     * @since 1.0
     */
//    private String getUploadPlaylistId() {
//        return _myChannel.getContentDetails().getRelatedPlaylists().getUploads();
//    }
}

@@ -462,159 +462,42 @@
 ort 
-com.google.common.collect.Lists;%0Aimport feedmeyoutubecore.AppConfig;%0Aimport feedmeyoutubecore.obj.YouTubeVideo;%0Aimport org.aeonbits.owner.ConfigFactory
+feedmeyoutubecore.obj.YouTubeVideo
 ;%0Aim
@@ -833,16 +833,49 @@
 oCrawler
+ implements Crawler%3CYouTubeVideo%3E
  %7B%0A    p
@@ -1046,68 +1046,8 @@
 %22;%0A%0A
-    private static final String APP_NAME = %22feedmeyoutube%22;%0A
     
@@ -1123,221 +1123,8 @@
 ss);
-%0A    private static final AppConfig CFG = ConfigFactory.create(AppConfig.class);%0A%0A    private final static List%3CString%3E scopes = Lists.newArrayList(%0A            %22https://www.googleapis.com/auth/youtube.readonly%22);
 %0A%0A  
@@ -1998,221 +1998,151 @@
  /**
-%0A     * Get the next videos in the uploaded playlist%0A     *
+ %7B@inheritDoc%7D */%0A    @Override%0A    public boolean hasNext() %7B
 %0A     
-* @
+   
 return 
-%7B@link List%7D with crawled youtube videos.%0A 
+_nextToken != null;%0A
     
-*%0A 
+%7D%0A%0A
     
-* @throws IOException Can't connect to the youtube servers%0A     * @since 1.0%0A     */
+/** %7B@inheritDoc%7D */%0A    @Override
 %0A   
@@ -2175,22 +2175,16 @@
  getNext
-Videos
 () throw
@@ -2581,234 +2581,8 @@
  %7D%0A%0A
-    /**%0A     * Check if there is an next page availible%0A     *%0A     * @return %7B@code true%7D if more videos are availible%0A     *%0A     * @since 1.0%0A     */%0A    public boolean hasNext() %7B%0A        return _nextToken != null;%0A    %7D%0A%0A
     
@@ -5180,358 +5180,7 @@
    %7D
-%0A%0A    /**%0A     * Gets the ID of the upload playlist. Reads the id of the uploaded video%0A     * playlist from %7B@link Channel%7D.%0A     *%0A     * @return ID of the uploaded video playlist%0A     *%0A     * @since 1.0%0A     */%0A//    private String getUploadPlaylistId() %7B%0A//        return _myChannel.getContentDetails().getRelatedPlaylists().getUploads();%0A//    %7D
 %0A%7D%0A

Add interface implementation