/*
 * ************************************************************************
 *
 * 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.CaptionsUpdatedEventListener;
import com.adobe.mediacore.MediaPlayer;
import com.adobe.mediacore.MediaPlayer.Visibility;
import com.adobe.mediacore.MediaPlayerEvent;
import com.adobe.mediacore.MediaPlayerStatus;
import com.adobe.mediacore.MediaPlayerItemEvent;
import com.adobe.mediacore.MediaPlayerStatusChangeEvent;
import com.adobe.mediacore.MediaPlayerItem;
import com.adobe.mediacore.StatusChangeEventListener;
import com.adobe.mediacore.TextFormat;
import com.adobe.mediacore.TextFormatBuilder;
import com.adobe.mediacore.info.ClosedCaptionsTrack;
import com.adobe.mediacore.MediaPlayerException;
import com.adobe.primetime.reference.PrimetimeReference;
import com.adobe.primetime.reference.config.ICCConfig;

/**
 * 
 * The CCManager handles the Closed Caption functionality
 * 
 */
public class CCManagerOn extends CCManager {

	private ICCConfig ccConfig;
	private MediaPlayer mediaPlayer;
	private int selectedClosedCaptionsIndex = -1;
	private boolean isPrepared;
	private MediaPlayerStatus lastKnownStatus;

	private final ArrayList<CCManagerEventListener> eventListeners = new ArrayList<CCManagerEventListener>();

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

	/**
	 * 
	 * Class constructor. This method begins the CCManagerOn's initialisation
	 * process and configures the initial CC style.
	 * 
	 * @param mediaPlayer
	 *            the PSDK's implementation of MediaPlayer which the manager
	 *            wraps
	 * @param ccConfig
	 *            which is used to configure closed captioning settings for the
	 *            manager
	 * 
	 */
	public CCManagerOn(ICCConfig ccConfig, MediaPlayer mediaPlayer) {
		this.ccConfig = ccConfig;
		this.mediaPlayer = mediaPlayer;

		this.mediaPlayer.addEventListener(MediaPlayerEvent.CAPTIONS_UPDATED, ccUpdatedEventListener);
		this.mediaPlayer.addEventListener(MediaPlayerEvent.STATUS_CHANGED, statusChangeEventListener);
	}

	/**
	 * Destroy manager by removing PSDK listeners
	 */
	@Override
	public void destroy() {
		mediaPlayer.removeEventListener(MediaPlayerEvent.CAPTIONS_UPDATED, ccUpdatedEventListener);
		mediaPlayer.removeEventListener(MediaPlayerEvent.STATUS_CHANGED, statusChangeEventListener);
	}

	@Override
	public void addEventListener(CCManagerEventListener eventListener) {
		eventListeners.add(eventListener);
	}

	@Override
	public void removeEventListener(CCManagerEventListener eventListener) {
		eventListeners.remove(eventListener);
	}
	
	/**
	 * statusChangeEventListener that intercepts status change events
	 */
	private final StatusChangeEventListener statusChangeEventListener = new StatusChangeEventListener() {

		@Override
		public void onStatusChanged(MediaPlayerStatusChangeEvent event) {
			MediaPlayerStatus status = event.getStatus();			
			lastKnownStatus = status;
			if(status == MediaPlayerStatus.PREPARED) {
				isPrepared = true;
				initialize();
			}
		}
	};	

	/**
	 * ccUpdatedUpdatedEventListener that intercepts CC update events
	 */
	private final CaptionsUpdatedEventListener ccUpdatedEventListener = new CaptionsUpdatedEventListener() {

		@Override
		public void onCaptionsUpdated(MediaPlayerItemEvent event) {
			MediaPlayerItem item = event.getItem();
			showClosedCaptions(item);

			for (CCManagerEventListener listener : eventListeners) {
				listener.onCaptionUpdated();
			}
		}
	};

	// Public methods

	/**
	 * Determine if closed captioning is available
	 * 
	 * @return true if closed captioning is available, false otherwise
	 */
	public boolean hasClosedCaptions() {
		return mediaPlayer.getCurrentItem().hasClosedCaptions();
	}

	/**
	 * Set the index of the currently selected closed captioning track
	 * 
	 * @param index
	 *            in the closed captioning track list
	 */
	private void setSelectedClosedCaptionsIndex(int index) {
		this.selectedClosedCaptionsIndex = index;
	}

	/**
	 * Get the index of the currently selected closed captioning track
	 * 
	 * @return int the selected index of the closed captioning track list
	 */
	@Override
	public int getSelectedClosedCaptionsIndex() {
		return this.selectedClosedCaptionsIndex;
	}

	/**
	 * Select closed captioning track from the list with the given index
	 * 
	 * @param index
	 *            in the closed captioning track list
	 */
	@Override
	public void selectClosedCaptionTrack(int index) {

		MediaPlayerItem currentItem = mediaPlayer.getCurrentItem();
		if (index >= 0) {
			ClosedCaptionsTrack desiredClosedCaptionsTrack = currentItem.getClosedCaptionsTracks().get(index);
            try {
                currentItem.selectClosedCaptionsTrack(desiredClosedCaptionsTrack);
                PrimetimeReference.logger.i(LOG_TAG + "#", "CC track:" + desiredClosedCaptionsTrack.getName() + "selected.");
            } catch(MediaPlayerException e)
            {
                PrimetimeReference.logger.i(LOG_TAG + "#", "CC track:" + " selection failed. ErrorCode - " + e.getErrorCode());
            }
            
            setSelectedClosedCaptionsIndex(index);
		} else {
			PrimetimeReference.logger.i(LOG_TAG + "#", "None Option Selected.");
			setSelectedClosedCaptionsIndex(-1);
			setCCVisibility(false);
		}
	}

	/**
	 * Turn closed captioning visibility on and off
	 * 
	 * @param visible
	 *            the visibility flag
	 */
	@Override
	public void setCCVisibility(boolean visible) {
		if (mediaPlayer != null && lastKnownStatus != MediaPlayerStatus.RELEASED) {
			if (visible) {
				mediaPlayer.setCCVisibility(Visibility.VISIBLE);
			} else {
				mediaPlayer.setCCVisibility(Visibility.INVISIBLE);
			}
		}
	}

	/**
	 * Get the CC visibility
	 * 
	 * @return true if CC is visible, false otherwise
	 */
	@Override
	public boolean getCCVisibility() {
		return mediaPlayer.getCCVisibility() == Visibility.VISIBLE;
	}

	/**
	 * Set closed captioning style based on config.
	 */
	@Override
	public void setCCStyle() {
		if (mediaPlayer == null || !isPrepared)
			return;

		TextFormat tf = new TextFormatBuilder(
				ccConfig.getCCFont(),
				ccConfig.getCCSize(),
				ccConfig.getCCFontEdge(),
				ccConfig.getCCFontColor().getValue(),
				ccConfig.getCCBackgroundColor().getValue(),
				 ccConfig.getCCFillColor().getValue(),
				ccConfig.getCCEdgeColor().getValue(),
				ccConfig.getCCFontOpacity(),
				ccConfig.getCCBackgroundOpacity(),
				ccConfig.getCCFillOpacity(),
				ccConfig.getCCBottomInset(),  
				ccConfig.getCCSafeArea()).toTextFormat(); 
				

		tf.setTreatSpaceAsAlphaNum(true);

		mediaPlayer.setCCStyle(tf);
	}

	/**
	 * Get the array of closed captioning in string format. Each of them
	 * concatenates with a given label.
	 * 
	 * @param label
	 *            - the status of the activity of the track.
	 * @return an array of String each represents the closed captioning track
	 */
	@Override
	public List<String> getClosedCaptionTracks(String label) {

		MediaPlayerItem currentItem = mediaPlayer.getCurrentItem();
		List<ClosedCaptionsTrack> closedCaptionsTracks = currentItem.getClosedCaptionsTracks();
		Iterator<ClosedCaptionsTrack> iterator = closedCaptionsTracks.iterator();

		List<String> closedCaptionsTracksAsStrings = new ArrayList<String>();
		if (currentItem != null) {
			while (iterator.hasNext()) {
				ClosedCaptionsTrack closedCaptionsTrack = iterator.next();

				closedCaptionsTracksAsStrings.add(closedCaptionsTrack.getName() + " : ");
			}
		}

		return closedCaptionsTracksAsStrings;
	}

	/**
	 * Show the closed captioning tracks in log
	 */
	private void showClosedCaptions(MediaPlayerItem item) {
		List<ClosedCaptionsTrack> closedCaptionsTracks = item.getClosedCaptionsTracks();

		Iterator<ClosedCaptionsTrack> iterator = closedCaptionsTracks.iterator();
		while (iterator.hasNext()) {
			ClosedCaptionsTrack track = iterator.next();
			PrimetimeReference.logger.i(LOG_TAG + "#", "CC track: " + track.getName() + ". Has activity: " + ".");
		}
	}

	/**
	 * Select the initial CC track and set its visibility and style
	 */
	private void initialize() {

		setCCStyle();
	}

}
