/*
 * ************************************************************************
 *
 * ADOBE SYSTEMS INCORPORATED
 * Copyright 2013 Adobe Systems Incorporated
 * All Rights Reserved.
 * NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the 
 * terms of the Adobe license agreement accompanying it.  If you have received this file from a 
 * source other than Adobe, then your use, modification, or distribution of it requires the prior 
 * written permission of Adobe.
 *
 **************************************************************************
 */

package com.adobe.primetime.reference.manager;

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

import com.adobe.mediacore.*;
import com.adobe.mediacore.info.AudioTrack;
import com.adobe.mediacore.metadata.Metadata;
import com.adobe.mediacore.metadata.Notification;
import com.adobe.primetime.reference.PrimetimeReference;
import com.adobe.primetime.reference.config.IAAConfig;

/**
 * 
 * The AAManager handles the Alternate Audio functionality
 * 
 */
public class AAManagerOn extends AAManager {

	@SuppressWarnings("unused")
	private IAAConfig aaConfig;
	private MediaPlayer mediaPlayer;

	private List<Integer> failedAlternateAudioTracks = new ArrayList<Integer>();

	private final String LOG_TAG = PrimetimeReference.LOG_APP_NAME
			+ AAManagerOn.class.getSimpleName();

	/**
	 * 
	 * Class constructor. This method begins the AAManagerOn's initialization
	 * process.
	 * 
	 * @param mediaPlayer
	 *            the PSDK's implementation of MediaPlayer which the manager
	 *            wraps
	 * @param aaConfig
	 *            which is used to configure alternate audio settings for the
	 *            manager
	 * 
	 */
	public AAManagerOn(IAAConfig aaConfig, MediaPlayer mediaPlayer) {
		this.aaConfig = aaConfig;
		this.mediaPlayer = mediaPlayer;

		// add event listener for failed audio tracks
		mediaPlayer.addEventListener(MediaPlayerEvent.OPERATION_FAILED, failedAudioTrackHandler);
	}

	/**
	 * Destroy manager by removing PSDK listeners
	 */
	@Override
	public void destroy() {

		mediaPlayer.removeEventListener(MediaPlayerEvent.OPERATION_FAILED, failedAudioTrackHandler);
	}

	/**
	 * Determines if alternate audio is available
	 * 
	 * @return true if alterate audio is available
	 */
	@Override
	public boolean hasAlternateAudio() {
		return mediaPlayer.getCurrentItem().hasAlternateAudio();
	}

	/**
	 * Select the audio track from the given index
	 * 
	 * @param index
	 *            the index of the available audio track to be chosen
	 * @return true if audio track is successfully selected, false otherwise or
	 *         the audio track was failed to switch to before
	 */
	@Override
	public boolean selectAlternateAudioTrack(int index) {
		if (failedAlternateAudioTracks.contains(index)) {
			PrimetimeReference.logger
					.i(LOG_TAG + "#selectAlternateAudio",
							"Selection to the current audio track has previously failed. Select a different track.");
			return false;
		}

		AudioTrack selectedAudioTrack = getSelectedAudioTrack();
		if (index >= 0) {
			selectedAudioTrack = mediaPlayer.getCurrentItem().getAudioTracks()
					.get(index);
			PrimetimeReference.logger.i(LOG_TAG + "#selectAlternateAudio", "Audio track "
					+ selectedAudioTrack + " selected.");

		}

        try {
            mediaPlayer.getCurrentItem().selectAudioTrack(selectedAudioTrack);
        } catch(MediaPlayerException e)
        {
            PrimetimeReference.logger.i(LOG_TAG + "#selectAlternateAudio", "Audio track "
                    + selectedAudioTrack + " selection failed. ErrorCode - " + e.getErrorCode());
        }

		return true;
	}

	/**
	 * Get the currently selected audio track object
	 * 
	 * @return AudioTrack the audio track object that's currently selected
	 */
	@Override
	public AudioTrack getSelectedAudioTrack() {
		return mediaPlayer.getCurrentItem().getSelectedAudioTrack();
	}

	/**
	 * Get the index of the currently selected audio track in the list of
	 * available tracks
	 * 
	 * @return index of the selected audio track in the list. If none of the
	 *         track matches, return -1
	 */
	@Override
	public int getSelectedAudioTrackIndex() {
		MediaPlayerItem item = mediaPlayer.getCurrentItem();
		if (item == null) {
			return INVALID_AUDIO_TRACK;
		}
		List<AudioTrack> audioTracks = item.getAudioTracks();
		AudioTrack selectedAudioTrack = item.getSelectedAudioTrack();

		for (int i = 0; i < audioTracks.size(); i++) {
			if (audioTracks.get(i).equals(selectedAudioTrack))
				return i;
		}

		return INVALID_AUDIO_TRACK;
	}

	/**
	 * Converts the alternate audio tracks to a string array.
	 * 
	 * @return array of AA track names
	 */
	@Override
	public String[] getAudioTracks() {

		List<String> audioTracksAsStrings = new ArrayList<String>();

		MediaPlayerItem currentItem = mediaPlayer.getCurrentItem();
		if (currentItem != null) {
			int index = 0;
			List<AudioTrack> audioTracks = currentItem.getAudioTracks();

			for (AudioTrack audioTrack : audioTracks) {
				String name = audioTrack.getName() + "[ " + audioTrack.getLanguage() + "]";
				if (failedAlternateAudioTracks.contains(index)) {
					name += " *broken track*";
				}
				audioTracksAsStrings.add(name);
				index++;
			}
		}

		return audioTracksAsStrings.toArray(new String[audioTracksAsStrings.size()]);
	}

	/**
	 * Register audio track index when audio switching operation failure.
	 * 
	 * @param notification
	 *            TVSDK notification created from operation failed event
	 */
	private void registerFailedAudioTrackIndex(Notification notification)
	{
		if (notification.getCode() == PSDKErrorCode.AUDIO_TRACK_ERROR)
		{
			Metadata metadata = notification.getMetadata();
			if (metadata != null)
			{
				String name = metadata.getValue("AUDIO_TRACK_NAME");
				String language = metadata.getValue("AUDIO_TRACK_LANGUAGE");
				MediaPlayerItem currentItem = mediaPlayer.getCurrentItem();

				PrimetimeReference.logger.w(LOG_TAG, "Failed Audio Track notification for track " + name + ":" + language);

				if (currentItem != null)
				{
					List<AudioTrack> tracks = currentItem.getAudioTracks();
					for (AudioTrack track : tracks)
					{
						if (track.getName().equals(name) && track.getLanguage().equals(language))
						{
							// Keep the track ID in a list. Next time the user selects this track
							// a warning is displayed.
							failedAlternateAudioTracks.add(tracks.indexOf(track));
							break;
						}
					}
				}
			}
		}
		else if (notification.getInnerNotification() != null)
		{
			registerFailedAudioTrackIndex(notification.getInnerNotification());
		}
	}

	private final NotificationEventListener failedAudioTrackHandler = new NotificationEventListener() {
		@Override
		public void onNotification(NotificationEvent notificationEvent) {
			if (notificationEvent.getNotification() != null)
			{
				registerFailedAudioTrackIndex(notificationEvent.getNotification());
			}
		}
	};

}
