/*******************************************************************************
 * ADOBE CONFIDENTIAL
 *  ___________________
 *
 *  Copyright 2015 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;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

import com.adobe.mediacore.ABRControlParameters;
import com.adobe.mediacore.BufferControlParameters;
import com.adobe.mediacore.PSDKErrorCode;
import com.adobe.mediacore.MediaPlayerItem;
import com.adobe.mediacore.MediaPlayerItemConfig;
import com.adobe.mediacore.MediaPlayerItemLoader;
import com.adobe.mediacore.MediaResource;
import com.adobe.mediacore.metadata.AdSignalingMode;
import com.adobe.primetime.reference.PrimetimeReference;
import com.adobe.primetime.reference.config.ConfigProvider;
import com.adobe.primetime.reference.ui.catalog.VideoItem;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class InstantOnItem {

    private static final String TAG = "InstantOnItem";
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
    private Context context;
    private VideoItem videoItem;
    private MediaPlayerItemLoader itemLoader;
    private boolean isBufferComplete;
    private boolean hasFailed;
    private ConfigProvider preferences;
    private BillingManager billingManager;

    private int resourceId;
    private long lastRecordedTime;


    private Map<EventType, List<InstantOnEventListener>> listeners = new HashMap<EventType, List<InstantOnEventListener>>();

    public InstantOnItem(Context context, VideoItem videoItem)
    {
        this.videoItem = videoItem;
        this.context = context;
        itemLoader = new MediaPlayerItemLoader(context, loaderListener);
        resourceId = ATOMIC_INTEGER.incrementAndGet();

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        preferences = new ConfigProvider(sharedPreferences, videoItem);
        billingManager = new BillingManager(preferences, context);
    }

    public boolean isBufferComplete()
    {
        return isBufferComplete;
    }

    public MediaPlayerItem getMediaPlayerItem() {
        return itemLoader.getItem();
    }

    public void addEventListener(InstantOnEventListener listener) {
        addEventListener(getType(listener), listener);
    }

    public void addEventListener(EventType type, InstantOnEventListener listener) {
        if (!listeners.containsKey(type))
        {
            listeners.put(type, new ArrayList<InstantOnEventListener>());
        }

        listeners.get(type).add(listener);
    }

    public void removeEventListener(InstantOnEventListener listener) {
        removeEventListener(getType(listener), listener);
    }

    public void removeEventListener(EventType type, InstantOnEventListener listener) {
        if (listeners.containsKey(type))
        {
            listeners.get(type).remove(listener);
        }
    }

    public void destroy() {
        listeners.clear();

    }

    public void prepareBuffer() {
        loadItem();
    }


    private void loadItem()
    {
        MediaResource resource = videoItem.toMediaResource();

        MediaPlayerItemConfig config = new MediaPlayerItemConfig(context);
        config.setAdvertisingMetadata(AdsManagerOn.getAdvertisingMetadata(preferences));
        billingManager.setBillingConfiguration(config);

        AdsManagerOn.registerContentFactory(config, preferences);
        AdsManagerOn.setCustomTags(config, preferences);
        AdsManagerOn.adjustAdSignalingMode(config, preferences, videoItem);


        long initBufferTime = preferences.getInitBufferTime();
        long playBufferTime = preferences.getBufferTime();

        // trick TVSDK to pre-buffer up to the initial buffer time instead of the full buffer time
        BufferControlParameters bufferControlParameters = BufferControlParameters.createDual((int) initBufferTime, (int) initBufferTime);

        itemLoader.setBufferControlParameters(bufferControlParameters);
        setABRControlParameters();

        lastRecordedTime = System.currentTimeMillis();
        itemLoader.load(resource, resourceId, config);
    }

    private void setABRControlParameters() {
        if (preferences.isABRControlEnabled()) {
            int initialBitRate = preferences.getABRInitialBitRate();
            int minBitRate = preferences.getABRMinBitRate();
            int maxBitRate = preferences.getABRMaxBitRate();
			
			 int minTrickplayBitRate = preferences.getABRMinTrickplayBitRate();
            int maxTrickplayBitRate = preferences.getABRMaxTrickplayBitRate();
            int maxTrickplayBandWidth = preferences.getABRMaxTrickplayBandwidthUsage();
			
            ABRControlParameters.ABRPolicy abrPolicy = preferences.getABRPolicy();
            abrPolicy = abrPolicy != null ? abrPolicy : ABRControlParameters.ABRPolicy.ABR_MODERATE;
            itemLoader.setABRControlParameters(new ABRControlParameters(initialBitRate, minBitRate, maxBitRate, abrPolicy,  minTrickplayBitRate, maxTrickplayBitRate, maxTrickplayBandWidth,  ABRControlParameters.DEFAULT_MAX_PLAYOUT_RATE));
        }
    }


    private MediaPlayerItemLoader.LoaderListener loaderListener = new MediaPlayerItemLoader.LoaderListener() {

        @Override
        public void onError(PSDKErrorCode mediaError, String s) {
            PrimetimeReference.logger.e(TAG+resourceId, "Error loading MediaPlayerItem for InstantOn playback (" + videoItem.getId() + "): " + mediaError.toString());
            hasFailed = true;
        }

        @Override
        public void onLoadComplete(MediaPlayerItem item) {
            long loadTime = System.currentTimeMillis() - lastRecordedTime;

            if (preferences.isCustomPositionPrefEnabled()) {
                long initialTime = preferences.retrieveStartTimePref();
                PrimetimeReference.logger.e(TAG+resourceId, "[" + videoItem.getId() + "] MediaPlayerItem load complete ("
                        + loadTime + "ms), preparing buffer at time " + initialTime);

                itemLoader.prepareBuffer(initialTime);
            }
            else {
                PrimetimeReference.logger.e(TAG+resourceId, "[" + videoItem.getId() + "] MediaPlayerItem load complete ("
                        + loadTime + "ms), preparing buffer");

                lastRecordedTime = System.currentTimeMillis();
                itemLoader.prepareBuffer();
            }
        }

        @Override
        public void onBufferingBegin() {
        
        }
        @Override
        public void onBufferPrepared() {
            long bufferTime = System.currentTimeMillis() - lastRecordedTime;

            PrimetimeReference.logger.e(TAG+resourceId, "[" + videoItem.getId() + "] MediaPlayerItem buffering complete (" + bufferTime + "ms)");

            isBufferComplete = true;
            if (listeners.containsKey(EventType.BUFFERING_COMPLETE))
            {
                for (InstantOnEventListener l : listeners.get(EventType.BUFFERING_COMPLETE))
                {
                    l.onEvent(videoItem.getId());
                }
            }

        }
    };


    public enum EventType {
        PREPARED,
        BUFFERING_COMPLETE
    }

    private EventType getType(InstantOnEventListener listener) {
        if (listener instanceof InstantOnBufferingCompleteEventListener)
        {
            return EventType.BUFFERING_COMPLETE;
        }
        else if (listener instanceof InstantOnPreparedEventListener)
        {
            return EventType.PREPARED;
        }

        return null;
    }

    private interface InstantOnEventListener {
        void onEvent(String id);
    }

    public interface InstantOnBufferingCompleteEventListener extends InstantOnEventListener {
    }

    public interface InstantOnPreparedEventListener extends InstantOnEventListener {
    }


    @Override
    public String toString()
    {
        return "InstantOnItem[" + videoItem.getId() + "] is buffered? " + isBufferComplete + ", has failed? " + hasFailed;
    }
}

