package com.brentvatne.react;

import android.media.MediaPlayer;
import android.os.Handler;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.yqritc.scalablevideoview.ScalableType;
import com.yqritc.scalablevideoview.ScalableVideoView;

import java.io.IOException;

public class ReactVideoView extends ScalableVideoView implements MediaPlayer.OnPreparedListener, MediaPlayer
        .OnErrorListener, MediaPlayer.OnCompletionListener {

    public enum Events {
        EVENT_LOAD_START("onVideoLoadStart"),
        EVENT_LOAD("onVideoLoad"),
        EVENT_ERROR("onVideoError"),
        EVENT_PROGRESS("onVideoProgress"),
        EVENT_SEEK("onVideoSeek"),
        EVENT_END("onVideoEnd");

        private final String mName;

        Events(final String name) {
            mName = name;
        }

        @Override
        public String toString() {
            return mName;
        }
    }

    private ThemedReactContext mThemedReactContext;
    private RCTEventEmitter mEventEmitter;

    private Handler mProgressUpdateHandler = new Handler();
    private Runnable mProgressUpdateRunnable = null;

    private ScalableType mResizeMode = ScalableType.LEFT_TOP;
    private boolean mRepeat = false;
    private boolean mPaused = false;
    private boolean mMuted = false;
    private float mVolume = 1;

    public ReactVideoView(ThemedReactContext themedReactContext) {
        super(themedReactContext);

        mThemedReactContext = themedReactContext;
        mEventEmitter = themedReactContext.getJSModule(RCTEventEmitter.class);

        initializeMediaPlayerIfNeeded();
        setSurfaceTextureListener(this);

        final MediaPlayer mediaPlayer = mMediaPlayer;

        mProgressUpdateRunnable = new Runnable() {
            @Override
            public void run() {

                try {
                    WritableMap event = Arguments.createMap();
                    // TODO: Other event properties.
                    event.putDouble("currentTime", (double) mediaPlayer.getCurrentPosition() / (double) 1000);
                    event.putDouble("duration", (double) mediaPlayer.getDuration() / (double) 1000);
                    event.putDouble("playableDuration", (double) mediaPlayer.getDuration() / (double) 1000);
                    mEventEmitter.receiveEvent(getId(), Events.EVENT_PROGRESS.toString(), event);
                } catch (Exception e) {
                    // Do nothing.
                }
                mProgressUpdateHandler.postDelayed(mProgressUpdateRunnable, 250);
            }
        };
        mProgressUpdateHandler.post(mProgressUpdateRunnable);
    }

    private void initializeMediaPlayerIfNeeded() {
        if (mMediaPlayer == null) {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setScreenOnWhilePlaying(true);
            mMediaPlayer.setOnVideoSizeChangedListener(this);
            mMediaPlayer.setOnErrorListener(this);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnCompletionListener(this);
        }
    }

    public void reset() {
        mMediaPlayer.reset();
    }

    public void setSrc(final String uriString, final boolean isNetwork) throws IOException {
        reset();

        if (isNetwork) {
            setDataSource(uriString);
        } else {
            setRawData(mThemedReactContext.getResources().getIdentifier(
                    uriString,
                    "raw",
                    mThemedReactContext.getPackageName()
            ));
        }

        prepare(this);
    }

    public void setResizeModeModifier(final ScalableType resizeMode) {
        mResizeMode = resizeMode;
        initializeMediaPlayerIfNeeded();
        setScalableType(resizeMode);
        invalidate();
    }

    public void setRepeatModifier(final boolean repeat) {
        mRepeat = repeat;
        initializeMediaPlayerIfNeeded();
        setLooping(repeat);
    }

    public void setPausedModifier(final boolean paused) {
        mPaused = paused;

        try {
            initializeMediaPlayerIfNeeded();

            if (!mPaused) {
                start();
            } else {
                pause();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setMutedModifier(final boolean muted) {
        mMuted = muted;

        initializeMediaPlayerIfNeeded();

        if (mMuted) {
            setVolume(0, 0);
        } else {
            setVolume(mVolume, mVolume);
        }
    }

    public void setVolumeModifier(final float volume) {
        mVolume = volume;
        initializeMediaPlayerIfNeeded();
        setMutedModifier(mMuted);
    }

    public void applyModifiers() {
        setResizeModeModifier(mResizeMode);
        setRepeatModifier(mRepeat);
        setPausedModifier(mPaused);
        setVolumeModifier(mVolume);
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        WritableMap event = Arguments.createMap();
        event.putDouble("duration", (double) mp.getDuration() / (double) 1000);
        event.putDouble("currentTime", (double) mp.getCurrentPosition() / (double) 1000);
        // TODO: Add canX properties.
        mEventEmitter.receiveEvent(getId(), Events.EVENT_LOAD.toString(), event);

        applyModifiers();
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        WritableMap error = Arguments.createMap();
        error.putInt("what", what);
        error.putInt("extra", extra);
        WritableMap event = Arguments.createMap();
        event.putMap("error", error);
        mEventEmitter.receiveEvent(getId(), Events.EVENT_ERROR.toString(), event);
        return true;
    }

    @Override
    public void seekTo(int msec) {
        WritableMap event = Arguments.createMap();
        event.putDouble("currentTime", (double) getCurrentPosition() / (double) 1000);
        event.putDouble("seekTime", (double) msec / (double) 1000);
        mEventEmitter.receiveEvent(getId(), Events.EVENT_SEEK.toString(), event);

        super.seekTo(msec);
    }

    @Override
    public void onCompletion(MediaPlayer mp) {
        mEventEmitter.receiveEvent(getId(), Events.EVENT_END.toString(), null);
    }
}

@@ -3208,71 +3208,8 @@
  %7D%0A%0A
-    public void reset() %7B%0A        mMediaPlayer.reset();%0A    %7D%0A%0A
     
@@ -3305,16 +3305,29 @@
         
+mMediaPlayer.
 reset();
@@ -3638,16 +3638,21 @@
  prepare
+Async
 (this);%0A

Prepare media player asynchronously