/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer.source;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;

import android.content.Context;
import android.net.Uri;
import androidx.media3.common.AdViewProvider;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.exoplayer.source.ads.AdsLoader;
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;

/** Unit test for {@link DefaultMediaSourceFactory}. */
@RunWith(AndroidJUnit4.class)
public final class DefaultMediaSourceFactoryTest {

  private static final String URI_MEDIA = "http://exoplayer.dev/video";
  private static final String URI_TEXT = "http://exoplayer.dev/text";

  @Test
  public void createMediaSource_fromMediaItem_returnsSameMediaItemInstance() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA).build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource.getMediaItem()).isSameInstanceAs(mediaItem);
  }

  @Test
  public void createMediaSource_withoutMimeType_progressiveSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA).build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ProgressiveMediaSource.class);
  }

  @Test
  public void createMediaSource_withPath_progressiveSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA + "/file.mp3").build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ProgressiveMediaSource.class);
  }

  @Test
  public void createMediaSource_withSubtitle_isMergingMediaSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    List<MediaItem.SubtitleConfiguration> subtitleConfigurations =
        Arrays.asList(
            new MediaItem.SubtitleConfiguration.Builder(Uri.parse(URI_TEXT))
                .setMimeType(MimeTypes.APPLICATION_TTML)
                .setLanguage("en")
                .build(),
            new MediaItem.SubtitleConfiguration.Builder(Uri.parse(URI_TEXT))
                .setMimeType(MimeTypes.APPLICATION_TTML)
                .setLanguage("de")
                .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
                .build());
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setSubtitleConfigurations(subtitleConfigurations)
            .build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(MergingMediaSource.class);
  }

  @Test
  public void createMediaSource_withStartPosition_isClippingMediaSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setClippingConfiguration(
                new MediaItem.ClippingConfiguration.Builder().setStartPositionMs(1000L).build())
            .build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ClippingMediaSource.class);
  }

  @Test
  public void createMediaSource_withEndPosition_isClippingMediaSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setClippingConfiguration(
                new MediaItem.ClippingConfiguration.Builder().setEndPositionMs(1000L).build())
            .build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ClippingMediaSource.class);
  }

  @Test
  public void createMediaSource_relativeToDefaultPosition_isClippingMediaSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setClippingConfiguration(
                new MediaItem.ClippingConfiguration.Builder()
                    .setRelativeToDefaultPosition(true)
                    .build())
            .build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ClippingMediaSource.class);
  }

  @Test
  public void createMediaSource_defaultToEnd_isNotClippingMediaSource() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setClippingConfiguration(
                new MediaItem.ClippingConfiguration.Builder()
                    .setEndPositionMs(C.TIME_END_OF_SOURCE)
                    .build())
            .build();

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(ProgressiveMediaSource.class);
  }

  @Test
  public void getSupportedTypes_coreModule_onlyOther() {
    int[] supportedTypes =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
            .getSupportedTypes();

    assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER);
  }

  @SuppressWarnings("deprecation") // Testing deprecated setters.
  @Test
  public void createMediaSource_withDeprecatedAdsConfiguration_callsAdsLoader() {
    Uri adTagUri = Uri.parse(URI_MEDIA);
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setAdsConfiguration(new MediaItem.AdsConfiguration.Builder(adTagUri).build())
            .build();
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
            .setAdsLoaderProvider(ignoredAdsConfiguration -> mock(AdsLoader.class))
            .setAdViewProvider(mock(AdViewProvider.class));

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(AdsMediaSource.class);
  }

  @Test
  public void createMediaSource_withAdsConfiguration_callsAdsLoader() {
    Uri adTagUri = Uri.parse(URI_MEDIA);
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setAdsConfiguration(new MediaItem.AdsConfiguration.Builder(adTagUri).build())
            .build();
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
            .setLocalAdInsertionComponents(
                ignoredAdsConfiguration -> mock(AdsLoader.class), mock(AdViewProvider.class));

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isInstanceOf(AdsMediaSource.class);
  }

  @Test
  public void createMediaSource_withAdsConfiguration_adProvidersNotSet_playsWithoutAdNoException() {
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setAdsConfiguration(
                new MediaItem.AdsConfiguration.Builder(Uri.parse(URI_MEDIA)).build())
            .build();
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());

    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    assertThat(mediaSource).isNotInstanceOf(AdsMediaSource.class);
  }

  @Test
  public void createMediaSource_withAdsConfigurationProvidersNull_playsWithoutAdNoException() {
    Context applicationContext = ApplicationProvider.getApplicationContext();
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA)
            .setAdsConfiguration(
                new MediaItem.AdsConfiguration.Builder(Uri.parse(URI_MEDIA)).build())
            .build();

    MediaSource mediaSource =
        new DefaultMediaSourceFactory(applicationContext).createMediaSource(mediaItem);

    assertThat(mediaSource).isNotInstanceOf(AdsMediaSource.class);
  }

  @Test
  public void createMediaSource_undefinedLiveProperties_livePropertiesUnset() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
    MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA + "/file.mp4").build();
    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    MediaItem mediaItemFromSource = mediaSource.getMediaItem();

    assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(C.TIME_UNSET);
    assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(C.TIME_UNSET);
    assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(C.TIME_UNSET);
    assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(C.RATE_UNSET);
    assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(C.RATE_UNSET);
  }

  @Test
  public void createMediaSource_withoutMediaItemProperties_usesFactoryLiveProperties() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
            .setLiveTargetOffsetMs(20)
            .setLiveMinOffsetMs(2222)
            .setLiveMaxOffsetMs(4444)
            .setLiveMinSpeed(.1f)
            .setLiveMaxSpeed(2.0f);
    MediaItem mediaItem = new MediaItem.Builder().setUri(URI_MEDIA + "/file.mp4").build();
    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    MediaItem mediaItemFromSource = mediaSource.getMediaItem();

    assertThat(mediaItemFromSource.liveConfiguration.targetOffsetMs).isEqualTo(20);
    assertThat(mediaItemFromSource.liveConfiguration.minOffsetMs).isEqualTo(2222);
    assertThat(mediaItemFromSource.liveConfiguration.maxOffsetMs).isEqualTo(4444);
    assertThat(mediaItemFromSource.liveConfiguration.minPlaybackSpeed).isEqualTo(.1f);
    assertThat(mediaItemFromSource.liveConfiguration.maxPlaybackSpeed).isEqualTo(2.0f);
  }

  @Test
  public void createMediaSource_withMediaItemLiveProperties_overridesFactoryLiveProperties() {
    DefaultMediaSourceFactory defaultMediaSourceFactory =
        new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
            .setLiveTargetOffsetMs(20)
            .setLiveMinOffsetMs(2222)
            .setLiveMinOffsetMs(4444)
            .setLiveMinSpeed(.1f)
            .setLiveMaxSpeed(2.0f);
    MediaItem mediaItem =
        new MediaItem.Builder()
            .setUri(URI_MEDIA + "/file.mp4")
            .setLiveConfiguration(
                new MediaItem.LiveConfiguration.Builder()
                    .setTargetOffsetMs(10)
                    .setMinOffsetMs(1111)
                    .setMinOffsetMs(3333)
                    .setMinPlaybackSpeed(20.0f)
                    .setMaxPlaybackSpeed(20.0f)
                    .build())
            .build();
    MediaSource mediaSource = defaultMediaSourceFactory.createMediaSource(mediaItem);

    MediaItem mediaItemFromSource = mediaSource.getMediaItem();

    assertThat(mediaItemFromSource).isEqualTo(mediaItem);
  }
}
