package org.schabi.newpipe.player.resolver;

import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;

import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.player.helper.PlayerDataSource;
import org.schabi.newpipe.player.helper.PlayerHelper;
import org.schabi.newpipe.util.ListHelper;

import java.util.ArrayList;
import java.util.List;

import static com.google.android.exoplayer2.C.SELECTION_FLAG_AUTOSELECT;
import static com.google.android.exoplayer2.C.TIME_UNSET;

public class VideoPlaybackResolver implements PlaybackResolver {

    public interface QualityResolver {
        int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
        int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
                                       final String playbackQuality);
    }

    @NonNull private final Context context;
    @NonNull private final PlayerDataSource dataSource;
    @NonNull private final QualityResolver qualityResolver;

    @Nullable private String playbackQuality;

    public VideoPlaybackResolver(@NonNull final Context context,
                                 @NonNull final PlayerDataSource dataSource,
                                 @NonNull final QualityResolver qualityResolver) {
        this.context = context;
        this.dataSource = dataSource;
        this.qualityResolver = qualityResolver;
    }

    @Override
    @Nullable
    public MediaSource resolve(@NonNull StreamInfo info) {
        final MediaSource liveSource = maybeBuildLiveMediaSource(dataSource, info);
        if (liveSource != null) return liveSource;

        List<MediaSource> mediaSources = new ArrayList<>();

        // Create video stream source
        final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context,
                info.getVideoStreams(), info.getVideoOnlyStreams(), false);
        final int index;
        if (videos.isEmpty()) {
            index = -1;
        } else if (playbackQuality == null) {
            index = qualityResolver.getDefaultResolutionIndex(videos);
        } else {
            index = qualityResolver.getOverrideResolutionIndex(videos, getPlaybackQuality());
        }
        final MediaSourceTag tag = new MediaSourceTag(info, videos, index);
        @Nullable final VideoStream video = tag.getSelectedVideoStream();

        if (video != null) {
            final MediaSource streamSource = buildMediaSource(dataSource, video.getUrl(),
                    PlayerHelper.cacheKeyOf(info, video),
                    MediaFormat.getSuffixById(video.getFormatId()), tag);
            mediaSources.add(streamSource);
        }

        // Create optional audio stream source
        final List<AudioStream> audioStreams = info.getAudioStreams();
        final AudioStream audio = audioStreams.isEmpty() ? null : audioStreams.get(
                ListHelper.getDefaultAudioFormat(context, audioStreams));
        // Use the audio stream if there is no video stream, or
        // Merge with audio stream in case if video does not contain audio
        if (audio != null && ((video != null && video.isVideoOnly) || video == null)) {
            final MediaSource audioSource = buildMediaSource(dataSource, audio.getUrl(),
                    PlayerHelper.cacheKeyOf(info, audio),
                    MediaFormat.getSuffixById(audio.getFormatId()), tag);
            mediaSources.add(audioSource);
        }

        // If there is no audio or video sources, then this media source cannot be played back
        if (mediaSources.isEmpty()) return null;
        // Below are auxiliary media sources

        // Create subtitle sources
        if(info.getSubtitles() != null) {
            for (final SubtitlesStream subtitle : info.getSubtitles()) {
                final String mimeType = PlayerHelper.subtitleMimeTypesOf(subtitle.getFormat());
                if (mimeType == null) continue;

                final Format textFormat = Format.createTextSampleFormat(null, mimeType,
                        SELECTION_FLAG_AUTOSELECT, PlayerHelper.captionLanguageOf(context, subtitle));
                final MediaSource textSource = dataSource.getSampleMediaSourceFactory()
                        .createMediaSource(Uri.parse(subtitle.getURL()), textFormat, TIME_UNSET);
                mediaSources.add(textSource);
            }
        }

        if (mediaSources.size() == 1) {
            return mediaSources.get(0);
        } else {
            return new MergingMediaSource(mediaSources.toArray(
                    new MediaSource[mediaSources.size()]));
        }
    }

    @Nullable
    public String getPlaybackQuality() {
        return playbackQuality;
    }

    public void setPlaybackQuality(@Nullable String playbackQuality) {
        this.playbackQuality = playbackQuality;
    }
}
