package com.anren.omplayer;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Handler;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.webkit.CookieManager;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.tencent.ijk.media.player.IMediaPlayer;
import com.tencent.ijk.media.player.IjkMediaPlayer;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by lcg on 2017/6/18.
 */
public class OMAudioPlayerIjk implements IjkMediaPlayer.OnPreparedListener,
        IjkMediaPlayer.OnErrorListener,
        IjkMediaPlayer.OnBufferingUpdateListener,
        IjkMediaPlayer.OnCompletionListener,
        IjkMediaPlayer.OnInfoListener {

    public static final String EVENT_PROP_FAST_FORWARD = "canPlayFastForward";
    public static final String EVENT_PROP_SLOW_FORWARD = "canPlaySlowForward";
    public static final String EVENT_PROP_SLOW_REVERSE = "canPlaySlowReverse";
    public static final String EVENT_PROP_REVERSE = "canPlayReverse";
    public static final String EVENT_PROP_STEP_FORWARD = "canStepForward";
    public static final String EVENT_PROP_STEP_BACKWARD = "canStepBackward";

    public static final String EVENT_PROP_DURATION = "duration";
    public static final String EVENT_PROP_PLAYABLE_DURATION = "playableDuration";
    public static final String EVENT_PROP_CURRENT_TIME = "currentTime";
    public static final String EVENT_PROP_SEEK_TIME = "seekTime";
    public static final String EVENT_PROP_NATURALSIZE = "naturalSize";
    public static final String EVENT_PROP_WIDTH = "width";
    public static final String EVENT_PROP_HEIGHT = "height";
    public static final String EVENT_PROP_ORIENTATION = "orientation";

    public static final String EVENT_PROP_ERROR = "error";
    public static final String EVENT_PROP_WHAT = "what";
    public static final String EVENT_PROP_EXTRA = "extra";

    private OMAudioPlayerModuleIjk mAudioPlayerModule = null;
    private Integer mPlayerId = 0;
    private IjkMediaPlayer mMediaPlayer;
    private String mToken = null;
    private String mSrcUriString = null;
    private boolean mMediaPlayerValid = false;
    private boolean isCompleted = false;
    private long mAudioDuration = 0;
    private int mAudioBufferedDuration = 0;

    private boolean mPauseBecauseCall = false;

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

    private boolean mRepeat = false;
    private boolean mPaused = true;
    private boolean mMuted = false;
    private float mVolume = 1.0f;
    private float mRate = 1.0f;

    private AudioManager mAm;

    private boolean mAutoPause = false;

    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
                    || focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                // Pause playback
                if (mMediaPlayer.isPlaying()){
                    mAutoPause = true;
                    pause();
                }

            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                // Resume playback
                if (mAutoPause && !mMediaPlayer.isPlaying()) {
                    play();

                }
            }
        }
    };

    private boolean requestAudioFocus() {
        // Request audio focus for playback
        int result = mAm.requestAudioFocus(mAudioFocusChangeListener,
                // Use the music stream.
                AudioManager.STREAM_MUSIC,
                // Request permanent focus.
                AudioManager.AUDIOFOCUS_GAIN);
        return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    }

    public OMAudioPlayerIjk(Integer playerId, OMAudioPlayerModuleIjk audioPlayerModule){
        mPlayerId = playerId;
        mAudioPlayerModule = audioPlayerModule;
        mAm = (AudioManager) audioPlayerModule.getContext().getSystemService(Context.AUDIO_SERVICE);

//        initTelephonyListener();
        initializeMediaPlayerIfNeeded();

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

                if (mMediaPlayerValid && !isCompleted &&!mPaused) {
                    WritableMap event = Arguments.createMap();
                    event.putDouble(EVENT_PROP_CURRENT_TIME, mMediaPlayer.getCurrentPosition() / 1000.0);
                    event.putDouble(EVENT_PROP_PLAYABLE_DURATION, mAudioBufferedDuration / 1000.0); //TODO:mBufferUpdateRunnable
                    event.putInt("id", mPlayerId);
                    mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_PROGRESS, event);

                    // Check for update after an interval
                    // TODO: The update interval is fixed at 250. There is a property in React component that defines this value. Totally ignored !!!
                    mProgressUpdateHandler.postDelayed(mProgressUpdateRunnable, 250);
                }
            }
        };
    }

    private void initTelephonyListener() {
        TelephonyManager telephonyManager = (TelephonyManager)mAudioPlayerModule.getContext().getSystemService(Context.TELEPHONY_SERVICE);
        telephonyManager.listen(new PhoneStateListener(){
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                if (state == TelephonyManager.CALL_STATE_RINGING || state == TelephonyManager.CALL_STATE_OFFHOOK) {

                    if (mMediaPlayer.isPlaying()) {
                        mPauseBecauseCall = true;
                        pause();
                    }

                }
                else if (state == TelephonyManager.CALL_STATE_IDLE) {
                    if (mPauseBecauseCall) {
                        mPauseBecauseCall = false;
                        play();
                    }
                }

                super.onCallStateChanged(state, incomingNumber);
            }
        }, PhoneStateListener.LISTEN_CALL_STATE);
    }

    private void initializeMediaPlayerIfNeeded() {
        if (mMediaPlayer == null) {
            mMediaPlayerValid = false;
            mMediaPlayer = new IjkMediaPlayer();
            mMediaPlayer.setScreenOnWhilePlaying(true);
            mMediaPlayer.setOnErrorListener(this);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnBufferingUpdateListener(this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnInfoListener(this);
        }
    }

    private void applyModifiers() {
        setRepeat(mRepeat);
        if (mPaused){
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
            }
        }
        else {
            if (requestAudioFocus()) {
                mMediaPlayer.start();
                mProgressUpdateHandler.post(mProgressUpdateRunnable);
            }
        }
        setMuted(mMuted);
    }

    @Override
    public void onPrepared(IMediaPlayer mediaPlayer) {
        mMediaPlayerValid = true;
        mAudioDuration = mediaPlayer.getDuration();

        WritableMap event = Arguments.createMap();
        event.putDouble(EVENT_PROP_DURATION, mAudioDuration / 1000.0);
        event.putDouble(EVENT_PROP_CURRENT_TIME, mMediaPlayer.getCurrentPosition() / 1000.0);

        // TODO: Actually check if you can.
        event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
        event.putBoolean(EVENT_PROP_SLOW_FORWARD, true);
        event.putBoolean(EVENT_PROP_SLOW_REVERSE, true);
        event.putBoolean(EVENT_PROP_REVERSE, true);
        event.putBoolean(EVENT_PROP_FAST_FORWARD, true);
        event.putBoolean(EVENT_PROP_STEP_BACKWARD, true);
        event.putBoolean(EVENT_PROP_STEP_FORWARD, true);
        event.putInt("id", mPlayerId);

        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_LOAD, event);

        applyModifiers();
    }

    @Override
    public boolean onError(IMediaPlayer mediaPlayer, int what, int extra) {
        WritableMap error = Arguments.createMap();
        error.putInt(EVENT_PROP_WHAT, what);
        error.putInt(EVENT_PROP_EXTRA, extra);
        WritableMap event = Arguments.createMap();
        event.putMap(EVENT_PROP_ERROR, error);
        event.putInt("id", mPlayerId);

        mAm.abandonAudioFocus(mAudioFocusChangeListener);
        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_ERROR, event);
        return true;
    }

    @Override
    public boolean onInfo(IMediaPlayer mediaPlayer, int what, int extra) {
        WritableMap event = Arguments.createMap();
        event.putInt("id", mPlayerId);

        switch (what) {
            case IjkMediaPlayer.MEDIA_INFO_BUFFERING_START:
                mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_LOAD_STALLED, event);
                break;
            case IjkMediaPlayer.MEDIA_INFO_BUFFERING_END:
                mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_LOAD_RESUME, event);
                break;
            case IjkMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
                mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_READY_FOR_PLAY, event);
                break;

            default:
        }
        return false;
    }

    @Override
    public void onBufferingUpdate(IMediaPlayer mediaPlayer, int percent) {
        mAudioBufferedDuration = (int) Math.round((double) (mAudioDuration * percent) / 100.0);
    }

    @Override
    public void onCompletion(IMediaPlayer mediaPlayer) {
        isCompleted = true;
        WritableMap event = Arguments.createMap();
        event.putInt("id", mPlayerId);

        mAm.abandonAudioFocus(mAudioFocusChangeListener);

        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_END, event);
    }

    public void setAudioPlayerModule(OMAudioPlayerModuleIjk audioPlayerModule) {
        mAudioPlayerModule = audioPlayerModule;
    }

    public void setAudioPlayerId(Integer playerId){
        mPlayerId = playerId;
    }

    public void setToken(final String tokenString) {

        mToken = tokenString;
        if (mSrcUriString == null || !mSrcUriString.endsWith(".m3u8")) {
            return;
        }
        setSrc(mSrcUriString);

    }

    public void setSrc(final String uriString) {

        if (uriString == null || uriString.endsWith(".m3u8") && mToken == null) {
            return;
        }
        mSrcUriString = uriString;
        mMediaPlayerValid = false;
        mAudioDuration = 0;
        mAudioBufferedDuration = 0;

        initializeMediaPlayerIfNeeded();
        mMediaPlayer.reset();

        try {
            if (uriString.startsWith("http")) {
                // Use the shared CookieManager to access the cookies
                // set by WebViews inside the same app
                CookieManager cookieManager = CookieManager.getInstance();

                Uri parsedUrl = Uri.parse(uriString);
                Uri.Builder builtUrl = parsedUrl.buildUpon();

                String cookie = cookieManager.getCookie(builtUrl.build().toString());

                Map<String, String> headers = new HashMap<String, String>();

                if (cookie != null) {
                    headers.put("Cookie", cookie);
                }
                if (mToken != null) {
                    mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", "ijkplayer_" + mToken);

                    headers.put("User-Agent", "ijkplayer_" + mToken);
                }
                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mMediaPlayer.setDataSource(mAudioPlayerModule.getContext(), parsedUrl, headers);
//                if (uriString.endsWith(".m3u8")) {
//                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//                    mMediaPlayer.getClass().getMethod("setDataSource", String.class, Map.class).invoke(mMediaPlayer, uriString, headers);
//                } else {
//                    mMediaPlayer.setDataSource(mAudioPlayerModule.getContext(), parsedUrl, headers);
//                }
            } else {
                if (uriString.startsWith("content://")) {
                    Uri parsedUrl = Uri.parse(uriString);
                    mMediaPlayer.setDataSource(mAudioPlayerModule.getContext(), parsedUrl);
                } else {
                    mMediaPlayer.setDataSource(uriString);
                }
            }
            mMediaPlayer.setRate(mRate);
        } catch (Exception e) {
            WritableMap event = Arguments.createMap();
            event.putString(OMVideoViewManager.PROP_SRC, uriString);
            event.putInt("id", mPlayerId);
            event.putString("error", e.getMessage());
            mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_ERROR, event);
            return;
        }

        WritableMap src = Arguments.createMap();
        src.putString(OMVideoViewManager.PROP_SRC_URI, uriString);

        WritableMap event = Arguments.createMap();
        event.putMap(OMVideoViewManager.PROP_SRC, src);
        event.putInt("id", mPlayerId);
        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_LOAD_START, event);

        // not async to prevent random crashes on Android playback from local resource due to race conditions
        try {
            mMediaPlayer.prepareAsync();
        } catch (Exception e) {
            WritableMap prepareEvent = Arguments.createMap();
            prepareEvent.putString(OMVideoViewManager.PROP_SRC, uriString);
            prepareEvent.putInt("id", mPlayerId);
            prepareEvent.putString("error", e.getMessage());
            mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_ERROR, prepareEvent);
        }
    }

    public void setRepeat(boolean repeat) {
        mRepeat = repeat;

        if (mMediaPlayerValid){
            mMediaPlayer.setLooping(mRepeat);
        }
    }

    public void setMuted(boolean muted) {
        mMuted = muted;

        if (!mMediaPlayerValid) {
            return;
        }

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

    public void setVolume(Float volume) {
        mVolume = volume;

        mMediaPlayer.setVolume(volume, volume);
    }

    public void setRate(Float rate) {
        mRate = rate;
        mMediaPlayer.setRate(rate);
//        not implement
//        mMediaPlayer.setPlaybackSpeed(rate);
    }

    public void seek(int seek) {
        if (mMediaPlayerValid) {
            WritableMap event = Arguments.createMap();
            event.putDouble(EVENT_PROP_CURRENT_TIME, mMediaPlayer.getCurrentPosition() / 1000.0);
            event.putDouble(EVENT_PROP_SEEK_TIME, seek / 1000.0);
            event.putInt("id", mPlayerId);
            mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_SEEK, event);

            mMediaPlayer.seekTo(seek);
            if (isCompleted && mAudioDuration != 0 && seek < mAudioDuration) {
                isCompleted = false;
            }
        }
    }

    public void play() {
        if (requestAudioFocus()) {
            mPaused = false;
            mAutoPause = false;

            if (!mMediaPlayerValid) {
                return;
            }

            mMediaPlayer.start();
            mProgressUpdateHandler.post(mProgressUpdateRunnable);

            WritableMap event = Arguments.createMap();
            event.putInt("id", mPlayerId);
            mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_RESUME, event);
        }
    }

    public void pause() {
        mPaused = true;

        if (!mMediaPlayerValid) {
            return;
        }

        mMediaPlayer.pause();

        WritableMap event = Arguments.createMap();
        event.putInt("id", mPlayerId);
        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_STALLED, event);
    }

    public void stop() {

        if (!mMediaPlayerValid) {
            return;
        }

        mMediaPlayer.stop();
        mAm.abandonAudioFocus(mAudioFocusChangeListener);

        WritableMap event = Arguments.createMap();
        event.putInt("id", mPlayerId);
        mAudioPlayerModule.sendEvent(OMAudioEvent.EVENT_AUDIO_END, event);
    }
}
