/*
 * ***********************************************************************
 *
 *  ADOBE CONFIDENTIAL
 *  ___________________
 *
 *  Copyright 2013 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 *  NOTICE:  All information contained herein is, and remains
 *  the property of Adobe Systems Incorporated and its suppliers,
 *  if any.  The intellectual and technical concepts contained
 *  herein are proprietary to Adobe Systems Incorporated and its
 *  suppliers and are protected by trade secret or copyright law.
 *  Dissemination of this information or reproduction of this material
 *  is strictly forbidden unless prior written permission is obtained
 *  from Adobe Systems Incorporated.
 *
 * ***********************************************************************
 */

package com.adobe.primetime.reference.manager.test;

import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import com.adobe.mediacore.PSDKErrorCode;
import com.adobe.mediacore.MediaPlayer;
import com.adobe.mediacore.metadata.*;
import com.adobe.mediacore.timeline.advertising.AdvertisingMetadata;
import org.mockito.ArgumentCaptor;

import android.test.AndroidTestCase;

import com.adobe.mediacore.ABRControlParameters.ABRPolicy;
import com.adobe.mediacore.timeline.Timeline;
import com.adobe.mediacore.utils.TimeRange;
import com.adobe.primetime.reference.config.IAdConfig;
import com.adobe.primetime.reference.config.IPlaybackConfig;
import com.adobe.primetime.reference.manager.AdsManagerOn;
import com.adobe.primetime.reference.manager.PlaybackManager;
import com.adobe.primetime.reference.manager.PlaybackManager.PlaybackManagerEventListener;

public class PlaybackManagerTest extends AndroidTestCase {

	private MockMediaPlayer mediaPlayer = null;
	private PlaybackManager playbackManager = null;
	private PlaybackManagerEventListener playbackManagerEventListener;

	// Internal tracking variables
	private boolean isPrepared = false;
	private long seekPos = 0;
	private boolean isPlaying = false;
	private boolean isComplete = false;

	// Media player setup metadata
	private String url = "testUrl";
	private String zoneId = "61135";
	private String domain = "auditude.com";
	private String mediaId = "vast_comedy";

	private long duration = 30000;
	private boolean isLive = false;
	private long width = 640;
	private long height = 480;

	// Playback config
	private final int INIT_BITRATE = 2000;
	private final int MIN_BITRATE = 1000;
	private final int MAX_BITRATE = 3000;
	private final ABRPolicy ABR_POLICY = ABRPolicy.ABR_AGGRESSIVE;

	private AdConfig adConfig;
	private AdsManagerOn adsManager;

	@Override
	protected void setUp() throws Exception {
		super.setUp();

		mediaPlayer = new MockMediaPlayer(getContext(), duration, isLive, width, height);

		PlaybackConfig playbackConfig = new PlaybackConfig();
		playbackManager = new PlaybackManager(playbackConfig, mediaPlayer);
		playbackManagerEventListener = spy(new PlaybackManagerEventListenerImpl());
		playbackManager.addEventListener(playbackManagerEventListener);

		adConfig = new AdConfig(domain, zoneId, mediaId);
		adsManager = new AdsManagerOn(adConfig, mediaPlayer);

	}

	@Override
	protected void tearDown() throws Exception {
		super.tearDown();
		mediaPlayer = null;
		playbackManager = null;
		adsManager = null;
		isPrepared = false;
		isPlaying = false;
		isComplete = false;
		seekPos = 0;
	}

	/**
	 * Test setUp video function by verifying URL, stream type and Auditude
	 * metadata are ingested into media player correctly
	 */
	public void testSetupVideo() {
		playbackManager.setupVideo(url, adsManager);
		assertEquals(url, mediaPlayer.getCurrentItem().getResource().getUrl());
		assertEquals(adConfig.getMetadata(), mediaPlayer.getCurrentItem()
				.getResource().getMetadata());
	}

	/**
	 * Test play and pause functions by verifying player state after playing the
	 * video and pause This also verifies the isPrepared(), isComplete(),
	 * isPlaying() and isPaused()
	 */
	public void testPlayPause() {

		playbackManager.setupVideo(url, adsManager);

		// Verify isPrepared(), isComplete() isPlaying() and isPaused()
		assertTrue(playbackManager.isPrepared());
		assertFalse(playbackManager.isPlaying());
		assertFalse(playbackManager.isPaused());
		assertFalse(playbackManager.isCompleted());

		// Verify play()
		playbackManager.play();
		assertEquals(MediaPlayer.PlayerStatus.PLAYING, mediaPlayer.getStatus());

		// Verify play() in invalid state
		try {
			playbackManager.play();
		} catch (IllegalStateException e) {
			fail();
		}

		// Verify isPrepared(), isComplete() isPlaying() and isPaused()
		assertTrue(playbackManager.isPrepared());
		assertTrue(playbackManager.isPlaying());
		assertFalse(playbackManager.isPaused());
		assertFalse(playbackManager.isCompleted());

		// Verify pause()
		playbackManager.pause();
		assertEquals(MediaPlayer.PlayerStatus.PAUSED, mediaPlayer.getStatus());

		// Verify pause() in invalid state
		try {
			playbackManager.pause();
		} catch (IllegalStateException e) {
			fail();
		}

		// Verify isPrepared(), isComplete() isPlaying() and isPaused()
		assertTrue(playbackManager.isPrepared());
		assertFalse(playbackManager.isPlaying());
		assertTrue(playbackManager.isPaused());
		assertFalse(playbackManager.isCompleted());
	}

	/**
	 * Test seek functionality by verifying the player current time has change
	 * to the new seek position This also verifies the isComplete() function
	 * from seeking to the end of the playback range
	 */
	public void testSeek() {

		playbackManager.setupVideo(url, adsManager);
		long position = 10000;
		playbackManager.seek(position);

		// Verify the current time of player
		assertEquals(position, mediaPlayer.getCurrentTime());

		playbackManager.seek(mediaPlayer.getPlaybackRange().getEnd());
		assertTrue(playbackManager.isCompleted());
	}

	/**
	 * Test set ABR control parameters. This test does not require loading any
	 * video resourse.
	 */
	public void testSetABRParams() {

		// Default init, min and max bit rate are all 0
		assertEquals(0, mediaPlayer.getABRControlParameters()
				.getInitialBitRate());
		assertEquals(0, mediaPlayer.getABRControlParameters().getMinBitRate());
		assertEquals(0, mediaPlayer.getABRControlParameters().getMaxBitRate());

		// Default policy is MODERATE
		assertEquals(ABRPolicy.ABR_MODERATE, mediaPlayer
				.getABRControlParameters().getABRPolicy());

		// Set ABR param based on config
		playbackManager.setAbrControlParams();

		// Verify new params
		assertEquals(INIT_BITRATE, mediaPlayer.getABRControlParameters()
				.getInitialBitRate());
		assertEquals(MIN_BITRATE, mediaPlayer.getABRControlParameters()
				.getMinBitRate());
		assertEquals(MAX_BITRATE, mediaPlayer.getABRControlParameters()
				.getMaxBitRate());
		assertEquals(ABR_POLICY, mediaPlayer.getABRControlParameters()
				.getABRPolicy());

	}

	/**
	 * Test the manager handling playback event on prepared correctly
	 */
	public void testOnPrepared() {

		assertFalse(isPrepared);
		playbackManager.setupVideo(url, adsManager);

		verify(playbackManagerEventListener).onPrepared();
		assertTrue(isPrepared);
	}

	/**
	 * Test the manager handling qos event on seek complete correctly
	 */
	public void testOnSeekStartComplete() {

		playbackManager.setupVideo(url, adsManager);

		long adjustedTime = 10000;
		mediaPlayer.seek(adjustedTime);

		verify(playbackManagerEventListener).onSeekCompleted(adjustedTime);
		assertEquals(seekPos, adjustedTime);
	}

	/**
	 * Test the manager handling qos event buffer start and complete correctly
	 */
	public void testOnBuffer() {

		mediaPlayer.buffer();
		verify(playbackManagerEventListener).onBufferStart();
		verify(playbackManagerEventListener).onBufferComplete();
	}

	/**
	 * Test the manager handling playback event on playback start correctly
	 * 
	 */
	public void testOnPlayStart() {
		playbackManager.setupVideo(url, adsManager);

		assertFalse(isPlaying);
		mediaPlayer.play();

		verify(playbackManagerEventListener).onPlaying();
		assertTrue(isPlaying);
	}

	/**
	 * Test the manager handling playback event on playback complete correctly
	 */
	public void testOnPlayComplete() {
		playbackManager.setupVideo(url, adsManager);

		assertFalse(isComplete);

		mediaPlayer.seek(mediaPlayer.getPlaybackRange().getEnd());
		verify(playbackManagerEventListener).onComplete();
		assertTrue(isComplete);
	}

	/**
	 * Test the manager handling playback event on size available correctly
	 */
	public void testOnSizeAvailable() {
		playbackManager.setupVideo(url, adsManager);

		verify(playbackManagerEventListener).onDimensionsChange(height, width);
	}

	/**
	 * Test the manager handling playback error correctly
	 * 
	 */
	public void testOnError() {
		playbackManager.setupVideo(url, adsManager);

		mediaPlayer.error(PSDKErrorCode.kECNativeWarning);

		ArgumentCaptor<PSDKErrorCode> argument = ArgumentCaptor.forClass(PSDKErrorCode.class);
		verify(playbackManagerEventListener).onError(argument.capture());
		assertEquals(PSDKErrorCode.kECNativeWarning, argument.getValue());

	}

	/**
	 * Customized playback config for testing purpose
	 * 
	 */
	private class PlaybackConfig implements IPlaybackConfig {

		@Override
		public boolean isABRControlEnabled() {
			return true;
		}

		@Override
		public int getABRInitialBitRate() {
			return INIT_BITRATE;
		}

		@Override
		public int getABRMinBitRate() {
			return MIN_BITRATE;
		}

		@Override
		public int getABRMaxBitRate() {
			return MAX_BITRATE;
		}

		@Override
		public ABRPolicy getABRPolicy() {
			return ABR_POLICY;
		}

		@Override
		public long getInitBufferTime() {
			return PlaybackManager.DEFAULT_INIT_BUFFER;
		}

		@Override
		public long getBufferTime() {
			return PlaybackManager.DEFAULT_BUFFER_TIME;
		}

		@Override
		public boolean isCustomPositionPrefEnabled() {
			return false;
		}

		@Override
		public long retrieveStartTimePref() {
			return 0;
		}

		@Override
		public boolean isSeekingAfterTimeJumpPrefEnabled() {
			return false;
		}

		@Override
		public boolean isSeekingAtClientLivePointPrefEnabled() {
			return false;
		}

		@Override
		public long retrieveSeekingOffsetPref() {
			return 0;
		}

		@Override
		public boolean forceOpenMAXAL() {
			return false;
		}

	}

	/**
	 * Customized Ad config for testing purpose
	 * 
	 */
	private class AdConfig implements IAdConfig {

		private AuditudeSettings metadata;

		public AdConfig(String domain, String zoneId, String mediaId) {
			metadata = new AuditudeSettings();
			metadata.setZoneId(zoneId);
			metadata.setDomain(domain);
			metadata.setMediaId(mediaId);

		}

		@Override
		public String getCustomAdFactoryType() {
			return AdsManager.DEFAULT_CUSTOM_AD_CLIENT_FACTORY;
		}

		@Override
		public boolean isClickableAdsEnabled() {
			return false;
		}

		@Override
		public AdvertisingMetadata getMetadata() {

			return metadata;
		}

		@Override
		public String[] getAdTags() {

			return null;
		}

		@Override
		public boolean isAdvertisingWorkflowEnabled() {

			return false;
		}

		@Override
		public AdSignalingMode getDefaultAdSignalingMode() {

			return null;
		}

		@Override
		public boolean useDelayAdLoading() {
			return false;
		}

	}

	public class PlaybackManagerEventListenerImpl implements
			PlaybackManager.PlaybackManagerEventListener {

		@Override
		public void onBufferStart() {
			assertTrue(playbackManager.isBuffering());
		}

		@Override
		public void onBufferComplete() {
			assertFalse(playbackManager.isBuffering());
		}

		@Override
		public void onSeekCompleted(long adjustedTime) {
			seekPos = adjustedTime;
		}

		@Override
		public void onPrepared() {
			isPrepared = true;
		}

		@Override
		public void onPlaying() {
			isPlaying = true;
		}

		@Override
		public void onComplete() {
			isComplete = true;
		}

		@Override
		public void onDimensionsChange(float height, float width) {

		}

		@Override
		public void onStateChange(MediaPlayer.PlayerStatus status) {

		}

		@Override
		public void onError(PSDKErrorCode error) {

		}

		@Override
		public void onUpdate(TimeRange playbackRange, Timeline<?> timeline) {
		}

		@Override
		public void onSeeking() {
		}

	}

}
