/*
 * ************************************************************************
 *
 * 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.advertising;

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

import com.adobe.mediacore.MediaPlayerItem;
import com.adobe.mediacore.MediaPlayerItemConfig;
import com.adobe.mediacore.metadata.AdvertisingMetadata;
import com.adobe.mediacore.timeline.advertising.*;
import com.adobe.mediacore.timeline.advertising.policy.*;
import com.adobe.mediacore.utils.TimeRange;
import com.adobe.primetime.reference.PrimetimeReference;

/**
 * Since this is an empty class with no customized behavior, by default the
 * player will implement the following default behavior of these 8 scenarios
 * 
 * Scenario 1: Running into an ad break during normal play Default Behavior:
 * Play a watched ad break, remove after play
 * 
 * Scenario 2: Seek forward over ad break(s) into content Default Behavior: Play
 * the last break skipped over, then skip to the selected seek position
 * 
 * Scenario 3: Seek backward over ad break(s) into content Default Behavior: Do
 * not play any ad break and skip to the user seek position
 * 
 * Scenario 4: Seek forward into ad break Default Behavior: Play from the the
 * beginning of ad seeked into
 * 
 * Scenario 5: Seek backward into ad break Default Behavior: Play from the
 * beginning of the ad seeked into
 * 
 * Scenario 6: Seek over watched ad break(s) Default Behavior: If last ad break
 * skipped is watched already, skip to user seeked position
 * 
 * Scenario 7: Seek into a watched ad break Default Behavior: Skip ad break and
 * seek to position immediately after ad
 * 
 * Scenario 8: Trick Play Default Behavior: All ads are skipped during trick
 * play, play the last break skipped over after trick play ends, skip to the
 * user selected trick play position upon break(s) playback completion
 * 
 */

public class CustomAdBreakPolicySelector implements AdPolicySelector {
	private final String LOG_TAG = PrimetimeReference.LOG_APP_NAME + CustomAdBreakPolicySelector.class.getSimpleName();

	private boolean isLive = false;
	private AdBreakWatchedPolicy watchedPolicy = AdBreakWatchedPolicy.WATCHED_ON_BEGIN;
	private long initialTime = -1;

	public CustomAdBreakPolicySelector(MediaPlayerItem item)
	{
		if (item != null)
		{
			isLive = item.isLive();
			watchedPolicy = extractWatchedPolicy(item.getConfig());

			AdvertisingMetadata metadata = item.getConfig().getAdvertisingMetadata();
			if (metadata != null && metadata.containsKey("initial_playback_time")) {
				initialTime = Long.parseLong(metadata.getValue("initial_playback_time"));
			}

		}

	}

	@Override
	public List<AdBreakTimelineItem> selectAdBreaksToPlay(AdPolicyInfo adPolicyInfo) {

		List<AdBreakTimelineItem> timelineItems = adPolicyInfo.getAdBreakTimelineItems();

		if (timelineItems != null && timelineItems.size() > 0)
		{
			if (adPolicyInfo.getCurrentTime() <= adPolicyInfo.getSeekToTime())
			{
				AdBreakTimelineItem item = timelineItems.get(timelineItems.size()-1);

				// select last skipped ad break if not watched and skipped during a forward seek.
				if (item.getVirtualRange().getBegin() < adPolicyInfo.getSeekToTime() && !item.isWatched())
				{
					List<AdBreakTimelineItem> selectItems = new ArrayList<AdBreakTimelineItem>();
					selectItems.add(item);
					return selectItems;
				}
			}
		}

		return null;
	}

	@Override
	public AdBreakPolicy selectPolicyForAdBreak(AdPolicyInfo adPolicyInfo) {

		List<AdBreakTimelineItem> timelineItems = adPolicyInfo.getAdBreakTimelineItems();
		if (timelineItems != null && timelineItems.size() > 0)
		{
			AdBreakTimelineItem item = timelineItems.get(0);

			// skip ad break if seeked into already watched break
			if (adPolicyInfo.getMode() == AdPolicyMode.SEEK && item.isWatched())
			{
				return AdBreakPolicy.SKIP;
			}

			// remove breaks which occur before or at initial playback time
			// may be used to remove a moved pre-roll ad when using prepareToPlay(initialTime)
			// must set initialTime in AdvertisingMetadata
			TimeRange localRange = item.getLocalRange();
            if (initialTime >= localRange.getBegin())
            {
				PrimetimeReference.logger.i(LOG_TAG, "Remove ad break based on custom ad selection policy, " +
						"initial playback time (" + initialTime + ") is >= ad break local time (" + localRange.getBegin() + ")");
                return AdBreakPolicy.REMOVE;
            }
		}

		if (isLive)
		{
			// Play ad break but keep on timeline if live stream
			return AdBreakPolicy.PLAY;
		}

		// Keep ad break on timeline after play
		return AdBreakPolicy.PLAY;
	}

	@Override
	public AdPolicy selectPolicyForSeekIntoAd(AdPolicyInfo adPolicyInfo) {

		// Simple Ad Policy selector
		// if the first ad in the break was watched, skip to the next add after the seek position
		// otherwise, play the ads in the break from the beginning

		List<AdBreakTimelineItem> timelineItems = adPolicyInfo.getAdBreakTimelineItems();
		if (timelineItems != null && timelineItems.size() > 0)
		{
			if (timelineItems.get(0).isWatched())
			{
				return AdPolicy.SKIP_TO_NEXT_AD_IN_AD_BREAK;
			}
		}

		// Resume play from the next ad in the break
		return AdPolicy.PLAY_FROM_AD_BREAK_BEGIN;
	}

	@Override
	public AdBreakWatchedPolicy selectWatchedPolicyForAdBreak(AdPolicyInfo adPolicyInfo) {

		// Use the AdBreakWatchedPolicy from the AdvertisingMetadata for this stream
		return watchedPolicy;
	}


	private AdBreakWatchedPolicy extractWatchedPolicy(MediaPlayerItemConfig config)
	{
		if (config != null)
		{
			AdvertisingMetadata metadata = config.getAdvertisingMetadata();
			if (metadata != null)
			{
				return metadata.getAdBreakWatchedPolicy();
			}
		}

		return AdBreakWatchedPolicy.WATCHED_ON_BEGIN;
	}
}
