package jp.satorufujiwara.player;

import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;

import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.TextureView;

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

public class VideoTexturePresenter implements Player.Listener,
        AudioCapabilitiesReceiver.Listener {

    private final VideoTextureView textureView;
    private final List<OnStateChangedListener> onStateChangedListeners = new ArrayList<>();
    private final List<OnErrorListener> onErrorListeners = new ArrayList<>();
    private final List<OnVideoSizeChangedListener> onVideoSizeChangedListeners = new ArrayList<>();

    private Player player;
    private AudioCapabilitiesReceiver audioCapabilitiesReceiver;
    private RendererBuilder rendererBuilder;
    private long limitBitrate = Long.MAX_VALUE;
    private boolean playerNeedsPrepare;
    private SurfaceTexture surfaceTexture;

    public VideoTexturePresenter(final VideoTextureView view) {
        this.textureView = view;
        audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(view.getContext(), this);
        textureView.setSurfaceTextureListener(
                new TextureView.SurfaceTextureListener() {
                    @Override
                    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
                            int height) {
                        surfaceTexture = surface;
                        if (player != null) {
                            player.setSurface(new Surface(surface));
                        }
                    }

                    @Override
                    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
                            int height) {
                        //no op
                    }

                    @Override
                    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                        surfaceTexture = null;
                        if (player != null) {
                            player.blockingClearSurface();
                        }
                        playerNeedsPrepare = true;
                        return true;
                    }

                    @Override
                    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
                        //no op
                    }
                }

        );
    }

    @Override
    public void onStateChanged(boolean playWhenReady, int playbackState) {
        fireOnStateChanged(playWhenReady, playbackState);
    }

    @Override
    public void onError(Exception e) {
        fireOnError(e);
    }


    @Override
    public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
            float pixelWidthHeightRatio) {
        textureView.setVideoWidthHeightRatio(
                height == 0 ? 1 : (width * pixelWidthHeightRatio) / height);
        fireOnVideoSizeChanged(width, height, pixelWidthHeightRatio);
    }

    @Override
    public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
        if (player == null) {
            return;
        }
        boolean backgrounded = player.getBackgrounded();
        boolean playWhenReady = player.getPlayWhenReady();
        release();
        prepare();
        if (playWhenReady) {
            play();
        }
        player.setBackgrounded(backgrounded);
    }

    public EventProxy eventListeners() {
        return rendererBuilder != null ? rendererBuilder.eventProxy : null;
    }

    public void setLimitBitrate(final long limitBitrate) {
        this.limitBitrate = limitBitrate;
        if (rendererBuilder != null) {
            rendererBuilder.setLimitBitrate(limitBitrate);
        }
    }

    public void setSource(final VideoSource source) {
        rendererBuilder = source.createRendererBuilder(textureView.getContext());
        rendererBuilder.setLimitBitrate(limitBitrate);
        playerNeedsPrepare = true;
    }

    public void onCreate() {
        audioCapabilitiesReceiver.register();
    }

    public void onDestroy() {
        audioCapabilitiesReceiver.unregister();
    }

    public void prepare() {
        if (player == null) {
            player = new Player();
            player.addListener(this);
            if (surfaceTexture != null) {
                player.setSurface(new Surface(surfaceTexture));
            }
        }
        playerNeedsPrepare = true;
        if (rendererBuilder == null) {
            return;
        }
        player.setRendererBuilder(rendererBuilder);
    }

    public void release() {
        if (player == null) {
            return;
        }
        player.removeListener(this);
        player.release();
        player = null;
    }

    public void play() {
        if (player == null) {
            prepare();
        }
        if (rendererBuilder == null) {
            return;
        }
        if (playerNeedsPrepare) {
            player.prepare();
            playerNeedsPrepare = false;
        }
        player.setPlayWhenReady(true);
    }

    public void pause() {
        if (player == null) {
            return;
        }
        player.setPlayWhenReady(false);
    }

    public void stop() {
        if (player == null) {
            return;
        }
        player.stop();
        player.seekTo(0);
    }

    public void seekTo(final long positionMs) {
        if (player == null) {
            return;
        }
        player.seekTo(positionMs);
    }

    public void setMute(final boolean isMute) {
        if (player == null) {
            return;
        }
        player.setMute(isMute);
    }

    public long getDuration() {
        return player == null ? 0 : player.getDuration();
    }

    public long getCurrentPosition() {
        return player == null ? 0 : player.getCurrentPosition();
    }

    public int getBufferedPercentage() {
        return player == null ? 0 : player.getBufferedPercentage();
    }

    public long getBufferedPosition() {
        return player == null ? 0 : player.getBufferedPosition();
    }

    public boolean isPlaying() {
        return player != null && player.getPlayWhenReady();
    }

    public int getPlaybackState() {
        return player == null ? -1 : player.getPlaybackState();
    }

    public void addOnStateChangedListener(OnStateChangedListener l) {
        if (l != null) {
            onStateChangedListeners.add(l);
        }
    }

    public void removeOnStateChangedListener(OnStateChangedListener l) {
        onStateChangedListeners.remove(l);
    }

    public void clearAllOnStateChangedListener() {
        onStateChangedListeners.clear();
    }

    public void addOnErrorListener(OnErrorListener l) {
        if (l != null) {
            onErrorListeners.add(l);
        }
    }

    public void removeOnErrorListener(OnErrorListener l) {
        onErrorListeners.remove(l);
    }

    public void clearOnErrorListener() {
        onErrorListeners.clear();
    }

    public void addOnVideoSizeChangedListener(OnVideoSizeChangedListener l) {
        if (l != null) {
            onVideoSizeChangedListeners.add(l);
        }
    }

    public void removeOnVideoSizeChangedListener(OnVideoSizeChangedListener l) {
        onVideoSizeChangedListeners.remove(l);
    }

    public void clearOnVideoSizeChangedListener() {
        onVideoSizeChangedListeners.clear();
    }

    private void fireOnStateChanged(final boolean playWhenReady, final int playbackState) {
        for (OnStateChangedListener l : onStateChangedListeners) {
            l.onStateChanged(playWhenReady, playbackState);
        }
    }

    private void fireOnError(final Exception e) {
        for (OnErrorListener l : onErrorListeners) {
            l.onError(e);
        }
    }

    private void fireOnVideoSizeChanged(final int width, final int height,
            final float pixelWidthHeightRatio) {
        for (OnVideoSizeChangedListener l : onVideoSizeChangedListeners) {
            l.onVideoSizeChanged(width, height, pixelWidthHeightRatio);
        }
    }

    public interface OnStateChangedListener {

        void onStateChanged(boolean playWhenReady, int playbackState);
    }

    public interface OnErrorListener {

        void onError(Exception e);
    }

    public interface OnVideoSizeChangedListener {

        void onVideoSizeChanged(int width, int height, float pixelWidthHeightRatio);
    }
}