/*
 * ***********************************************************************
 *
 *  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.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Notification;
import android.content.Context;
import android.test.AndroidTestCase;
import com.adobe.mediacore.DRMMetadataInfoEvent;
import com.adobe.mediacore.Event;
import com.adobe.mediacore.PSDKErrorCode;
import com.adobe.mediacore.MediaPlayerStatusChangeEvent;
import com.adobe.mediacore.drm.*;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import android.content.res.Resources;
import android.test.InstrumentationTestCase;

import com.adobe.mediacore.metadata.Metadata;
import com.adobe.primetime.reference.manager.DrmManager;
import com.adobe.primetime.reference.manager.DrmManager.DrmManagerEventListener;

/**
 * Tests for the DRMErrorHandler class, using Mockito to stub out the PSDK DRM
 * classes, and using a stub MediaPlayer to send events to the class under test,
 * and to receive callbacks from it. Events sent are DRM errors that require
 * calls into the DRM reload the license or clear the license cache. These are
 * verified using mockito. The DRMErrorHandler sends notifications back to the
 * player using DRMErrorHanderListener, which is mocked to verify that the calls
 * are made.
 * 
 */
public class DrmManagerTest extends AndroidTestCase {

	MockDRMMediaPlayer mPlayer;
	DrmManagerEventListener mListener;
	DrmManager mObjectToTest;
	Resources mTargetResources;

	@Override
	protected void setUp() throws Exception {

		System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());

		mPlayer = new MockDRMMediaPlayer(getContext());
		mListener = mock(DrmManagerEventListener.class);

		mObjectToTest = new DrmManager(mPlayer);
		mObjectToTest.addEventListener(mListener);

	}

	/**
	 * Send an error (3359 NoCGSMAProtectionAvailable) that is un-recoverable
	 * and requires user notification. Verify that an appropriate error string
	 * is sent to the listener.
	 */
	public void testHandleErrorNoRetry() {
		mPlayer.postDRMMetadata(DRMAuthenticationMethod.ANONYMOUS);
		long errorCode = 3359; // NoCGSMAProtectionAvailable
		mPlayer.postStateChanged(errorCode);

		verify(mListener).onError(Matchers.eq(errorCode), Matchers.anyLong(),
				Matchers.anyString());
	}

	/**
	 * Send an error (3332 CachedLicenseExpired) that requires a an attempt to
	 * reload the license from the server. Verify that onRetry is sent to the
	 * listener.
	 */
	public void testHandleErrorWithSuccessfulReAcquireLicense() {

		mPlayer.postDRMMetadata(DRMAuthenticationMethod.ANONYMOUS);
		long errorCode = 3332; // CachedLicenseExpired

		mPlayer.stubSuccessfulAcquireLicense();
		mPlayer.postStateChanged(errorCode);

		// player should try to re-load the media after this error (fail to
		// connect to license server)
		verify(mListener).onRetry();
	}

	/**
	 * Send an error (3332 CachedLicenseExpired) that requires a an attempt to
	 * reload the license from the server. In this case, stub the callback from
	 * the DRM api to report failure. Verify that error message is sent to the
	 * listener.
	 */
	public void testHandleErrorWithReAcquireFailure() {

		mPlayer.postDRMMetadata(DRMAuthenticationMethod.ANONYMOUS);
		long errorCode = 3332; // CachedLicenseExpired

		mPlayer.stubFailedAcquireLicense(errorCode);
		mPlayer.postStateChanged(errorCode);

		verify(mListener).onError(Matchers.eq(errorCode), Matchers.anyLong(),
				Matchers.anyString());

	}

	/**
	 * Send 3323 (Corrupt GlobalStateStore) that requires clearing the local
	 * license cache. Verify that the appropriate API is called.
	 */
	public void testHandleErrorWithClearCache() {

		mPlayer.postDRMMetadata(DRMAuthenticationMethod.ANONYMOUS);
		mPlayer.postStateChanged(3323); // CorruptGlobalStateStore

		verify(mPlayer.getDRMManager()).resetDRM((DRMOperationCompleteEventListener)Matchers.any());

	}

	/**
	 * send a NON DRM event. Verify that it is ignored.
	 */
	public void testHandleNonDRMError() {
		mPlayer.postDRMMetadata(DRMAuthenticationMethod.ANONYMOUS);
		mPlayer.postStateChanged(1); // should be filtered as a non DRM error

		verify(mListener, never()).onError(Matchers.anyLong(),
				Matchers.anyLong(), Matchers.anyString());
	}

	private class MockDRMMediaPlayer extends MockMediaPlayer {

		public MockDRMMediaPlayer(Context context)
		{
			super(context);
		}


		private DRMManager mockDrmManager = mock(DRMManager.class);

		/**
		 * send an Event.DRM from the media player to the class under test.
		 */
		public void postDRMMetadata(DRMAuthenticationMethod auth) {

			// the DRMMetadata that is wrapped in the DRMetadataInfo object
			DRMMetadata mockDrmMetadata = mock(DRMMetadata.class);

			// DRMMetadataInfo is a final class and cannot be mocked, so use the
			// factory, but pass in
			// our mockDrmManager and stub its createMetadataFromBytes
			when(
					mockDrmManager.createMetadataFromBytes((byte[]) Matchers.any(),
							(DRMErrorEventListener) Matchers.any())).thenReturn(mockDrmMetadata);

			DRMMetadataInfo info = DRMMetadataInfo.createDRMMetadataInfo(mockDrmManager, null, 0, 0, false);
			Event event = DRMMetadataInfoEvent.create(info);

			eventHandlers.dispatch(MediaPlayerEventHandlerUtil.MediaPlayerEventType.kEventDRMMetadataInfoAvailable, event);

		}

		/**
		 * Send an Event.PLAYBACK from the media player to the class under test,
		 * and stub the event to give a specified native error code.
		 * 
		 * @param code
		 *            Native error code.
		 */
		public void postStateChanged(long code) {
			Notification notification = mock(Notification.class);
			Metadata mockMetadata = mock(Metadata.class);

			//when(mockMetadata.getValue("NATIVE_ERROR_CODE")).thenReturn(Long.toString(code));
			//when(notification.getMetadata()).thenReturn(mockMetadata);
			// FIXME if player state is Error, there is no way to pass a native error code.
			Event event = MediaPlayerStatusChangeEvent.create(PlayerStatus.ERROR, PSDKErrorCode.kECNativeWarning);
			eventHandlers.dispatch(MediaPlayerEventHandlerUtil.MediaPlayerEventType.kEventStatusChanged, event);

		}

		/**
		 * stubs the callback from the drmManager with successful response to
		 * acquireLicense
		 */
		public void stubSuccessfulAcquireLicense() {

			org.mockito.Mockito
					.doAnswer(new Answer<Object>() {
						public Object answer(InvocationOnMock invocation) {
							Object[] args = invocation.getArguments();
							((DRMAcquireLicenseEventListener) args[2]).onLicenseAcquired(null);
							return null;
						}
					})
					.when(mockDrmManager)
					.acquireLicense((DRMMetadata) Matchers.any(),
							(DRMAcquireLicenseSettings) Matchers.any(),
							(DRMAcquireLicenseEventListener) Matchers.any());
		}

		/**
		 * stubs the callback from the drmManager with failed response to
		 * acquireLicense
		 * 
		 * @param code
		 *            error code to pass to OperationError callback
		 */
		public void stubFailedAcquireLicense(final long code) {

			org.mockito.Mockito.doAnswer(new Answer<Object>() {
				public Object answer(InvocationOnMock invocation) {
					Object[] args = invocation.getArguments();
					((DRMAcquireLicenseEventListener) args[2]).onDRMError(new DRMError(code, 0));
					return null;
				}
			})
					.when(mockDrmManager)
					.acquireLicense((DRMMetadata) Matchers.any(),
							(DRMAcquireLicenseSettings) Matchers.any(),
							(DRMAcquireLicenseEventListener) Matchers.any());
		}

		@Override
		public DRMManager getDRMManager() {
			return mockDrmManager;
		}
	}
}
