package org.wikipedia.media;

import android.media.MediaPlayer;
import android.support.annotation.NonNull;

import org.wikipedia.util.StringUtil;
import org.wikipedia.util.log.L;

import java.io.IOException;

public class MediaPlayerImplementation {
    private static final boolean VERBOSE = false;

    @NonNull private final MediaPlayer player = new MediaPlayer();

    public void deinit() {
        if (VERBOSE) {
            L.v("Releasing");
        }
        player.release();
    }

    public void init() {
    }

    public void load(@NonNull String path,
                     @NonNull AvPlayer.Callback callback,
                     @NonNull AvPlayer.ErrorCallback errorCallback) {
        load(path, new PreparedListenerCallbackWrapper(callback),
                new ErrorListenerErrorCallbackWrapper(errorCallback));
    }

    public void stop() {
        if (VERBOSE) {
            L.v("Stopping");
        }

        // Do not call MediaPlayer.stop(). This requires going through the whole lifecycle again.
        // Also, seek triggers playback, so call before pausing.
        player.seekTo(0);
        pause();
    }

    public void play(@NonNull AvPlayer.Callback callback,
                     @NonNull AvPlayer.ErrorCallback errorCallback) {
        play(new CompletionListenerCallbackWrapper(callback),
                new ErrorListenerErrorCallbackWrapper(errorCallback));
    }

    public void pause() {
        if (VERBOSE) {
            L.v("Pausing");
        }
        player.pause();
    }

    public boolean isPlaying() {
        return player.isPlaying();
    }

    private void load(@NonNull String path,
                      @NonNull MediaPlayer.OnPreparedListener listener,
                      @NonNull MediaPlayer.OnErrorListener errorListener) {
        if (VERBOSE) {
            L.v("Loading path=" + path);
        }
        player.reset();
        player.setOnPreparedListener(listener);
        player.setOnErrorListener(errorListener);
        if (setDataSource(path)) {
            player.prepareAsync();
        } else {
            errorListener.onError(player, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
        }
    }

    private void play(@NonNull MediaPlayer.OnCompletionListener listener,
                      @NonNull MediaPlayer.OnErrorListener errorListener) {
        if (VERBOSE) {
            L.v("Playing");
        }
        player.setOnCompletionListener(listener);
        player.setOnErrorListener(errorListener);
        player.start();
    }

    private boolean setDataSource(@NonNull String path) {
        try {
            player.setDataSource(path);
            return true;
        } catch (IOException e) {
            L.d(e);
            return false;
        }
    }

    private abstract static class CallbackWrapper {
        @NonNull private final AvPlayer.Callback callback;
        CallbackWrapper(@NonNull AvPlayer.Callback callback) {
            this.callback =  callback;
        }

        protected void onSuccess() {
            callback.onSuccess();
        }
    }

    private abstract static class ErrorCallbackWrapper {
        @NonNull private final AvPlayer.ErrorCallback errorCallback;
        ErrorCallbackWrapper(@NonNull AvPlayer.ErrorCallback errorCallback) {
            this.errorCallback =  errorCallback;
        }

        protected void onError() {
            errorCallback.onError();
        }
    }

    private static class PreparedListenerCallbackWrapper extends CallbackWrapper implements
            MediaPlayer.OnPreparedListener {
        PreparedListenerCallbackWrapper(@NonNull AvPlayer.Callback callback) {
            super(callback);
        }

        @Override
        public void onPrepared(MediaPlayer player) {
            if (VERBOSE) {
                L.v("Loaded");
            }
            onSuccess();
        }
    }

    private static class CompletionListenerCallbackWrapper extends CallbackWrapper implements
            MediaPlayer.OnCompletionListener {
        CompletionListenerCallbackWrapper(@NonNull AvPlayer.Callback callback) {
            super(callback);
        }

        @Override
        public void onCompletion(MediaPlayer mp) {
            if (VERBOSE) {
                L.v("Stopped");
            }
            onSuccess();
        }
    }

    private static class ErrorListenerErrorCallbackWrapper extends ErrorCallbackWrapper implements
            MediaPlayer.OnErrorListener {
        ErrorListenerErrorCallbackWrapper(@NonNull AvPlayer.ErrorCallback errorCallback) {
            super(errorCallback);
        }

        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            if (VERBOSE) {
                L.v("Error: what=" + StringUtil.intToHexStr(what) + " extra=" + StringUtil.intToHexStr(extra));
            }
            onError();
            return true;
        }
    }
}
