/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.onemedia.playback;

import android.content.Context;
import android.media.MediaPlayer;
import android.os.Bundle;

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

/**
 * TODO: Insert description here. (generated by epastern)
 */
public abstract class Renderer {
    public static final String FEATURE_SET_CONTENT = "com.android.media.SET_CONTENT";
    public static final String FEATURE_SET_NEXT_CONTENT = "com.android.media.SET_NEXT_CONTENT";
    public static final String FEATURE_PLAY = "com.android.media.PLAY";
    public static final String FEATURE_PAUSE = "com.android.media.PAUSE";
    public static final String FEATURE_NEXT = "com.android.media.NEXT";
    public static final String FEATURE_PREVIOUS = "com.android.media.PREVIOUS";
    public static final String FEATURE_SEEK_TO = "com.android.media.SEEK_TO";
    public static final String FEATURE_STOP = "com.android.media.STOP";
    // TODO move states somewhere else
    public static final int STATE_ERROR = 0;
    /**
     * The state MediaPlayerManager starts in before any action has been
     * performed.
     */
    public static final int STATE_INIT = 1 << 0;
    /**
     * Indicates the source has been set and it is being prepared/buffered
     * before starting playback.
     */
    public static final int STATE_PREPARING = 1 << 1;
    /**
     * The media is ready and playback can be started.
     */
    public static final int STATE_READY = 1 << 2;
    /**
     * The media is currently playing.
     */
    public static final int STATE_PLAYING = 1 << 3;
    /**
     * The media is currently paused.
     */
    public static final int STATE_PAUSED = 1 << 4;
    /**
     * The service has been stopped and cannot be started again until a new
     * source has been set.
     */
    public static final int STATE_STOPPED = 1 << 5;
    /**
     * The playback has reached the end. It can be restarted by calling play().
     */
    public static final int STATE_ENDED = 1 << 6;

    // TODO decide on proper way of describing features
    protected List<String> mFeatures = new ArrayList<String>();
    protected List<Listener> mListeners = new ArrayList<Listener>();

    public Renderer(Context context, Bundle params) {
        onCreate(params);
        initFeatures(params);
    }

    abstract public void setContent(Bundle request);

    public void onCreate(Bundle params) {
        // Do nothing by default
    }

    public void setNextContent(Bundle request) {
        throw new UnsupportedOperationException("setNextContent() is not supported.");
    }

    public List<String> getFeatures() {
        return mFeatures;
    }

    public boolean onPlay() {
        // TODO consider making these log warnings instead of crashes (or
        // Log.wtf)
        // throw new UnsupportedOperationException("play is not supported.");
        return false;
    }

    public boolean onPause() {
        // throw new UnsupportedOperationException("pause is not supported.");
        return false;
    }

    public boolean onNext() {
        // throw new UnsupportedOperationException("next is not supported.");
        return false;
    }

    public boolean onPrevious() {
        // throw new
        // UnsupportedOperationException("previous is not supported.");
        return false;
    }

    public boolean onStop() {
        // throw new UnsupportedOperationException("stop is not supported.");
        return false;
    }

    public boolean onSeekTo(int time) {
        // throw new UnsupportedOperationException("seekTo is not supported.");
        return false;
    }

    public long getSeekPosition() {
        // throw new
        // UnsupportedOperationException("getSeekPosition is not supported.");
        return -1;
    }

    public long getDuration() {
        // throw new
        // UnsupportedOperationException("getDuration is not supported.");
        return -1;
    }

    public int getPlayState() {
        // throw new
        // UnsupportedOperationException("getPlayState is not supported.");
        return 0;
    }

    public void onDestroy() {
        // Do nothing by default
    }

    public void registerListener(Listener listener) {
        if (!mListeners.contains(listener)) {
            mListeners.add(listener);
        }
    }

    public void unregisterListener(Listener listener) {
        mListeners.remove(listener);
    }

    protected void initFeatures(Bundle params) {
        mFeatures.add(FEATURE_SET_CONTENT);
    }

    protected void pushOnError(int type, int extra, Bundle extras, Throwable error) {
        for (Listener listener : mListeners) {
            listener.onError(type, extra, extras, error);
        }
    }

    protected void pushOnStateChanged(int newState) {
        for (Listener listener : mListeners) {
            listener.onStateChanged(newState);
        }
    }

    protected void pushOnBufferingUpdate(int percent) {
        for (Listener listener : mListeners) {
            listener.onBufferingUpdate(percent);
        }
    }

    protected void pushOnFocusLost() {
        for (Listener listener : mListeners) {
            listener.onFocusLost();
        }
    }

    protected void pushOnNextStarted() {
        for (Listener listener : mListeners) {
            listener.onNextStarted();
        }
    }

    public interface Listener {
        public static final int ERROR_LOAD_FAILED = 1770;
        public static final int ERROR_PREPARE_ERROR = 1771;
        public static final int ERROR_PLAYBACK_FAILED = 1772;

        /**
         * When an error occurs onError will be called but not onStateChanged.
         * The Manager will remain in the error state until
         * {@link #setContent()} is called again.
         */
        public void onError(int type, int extra, Bundle extras,
                Throwable error);

        /**
         * onStateChanged will be called whenever the state of the manager
         * transitions except to an error state.
         */
        public void onStateChanged(int newState);

        /**
         * This is a passthrough of
         * {@link MediaPlayer.OnBufferingUpdateListener}.
         */
        public void onBufferingUpdate(int percent);

        /**
         * Called when audio focus is lost and it is not transient or ducking.
         */
        public void onFocusLost();

        /**
         * Called when the next item was started playing. Only called if a next
         * item has been set and the current item has ended.
         */
        public void onNextStarted();
    }
}
