/**
 * $URL: svn://172.30.17.62:3691/downloadable-agent/android/trunk/reference-application/src/main/java/com/insidesecure/drm/android/reference
 * /nativeplayer/activities/PlayMediaNativePlayerActivity.java $
 * DAAND
 * Copyright (C) 2004 - 2013, INSIDE Secure BV.  All rights reserved.
 */
package com.insidesecure.drm.android.reference.xoplayer.activities;


import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.*;
import android.widget.*;

import com.insidesecure.drm.android.reference.activities.*;
import com.insidesecure.drmagent.v2.*;
import com.insidesecure.drmagent.v2.id3.GeobFrame;
import com.insidesecure.drmagent.v2.id3.Id3Frame;
import com.insidesecure.drmagent.v2.id3.PrivFrame;
import com.insidesecure.drmagent.v2.id3.TxxxFrame;
import com.insidesecure.drmagent.v2.mediaplayer.MediaPlayer;

import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

public abstract class PlayMediaSharedPlayerActivity extends PlayMediaActivity implements MediaPlayer.OnBufferingUpdateListener,
        MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnErrorListener,
        MediaPlayer.OnInfoListener, SurfaceHolder.Callback, MediaPlayer.OnSeekCompleteListener, MediaPlayer.Id3MetadataListener {
    public static final String TAG = PlayMediaSharedPlayerActivity.class.getSimpleName();

    private int mVideoWidth;
    private int mVideoHeight;
    private boolean mIsVideoSizeKnown = false;
    protected boolean mReadyForPlay = false;
    private long mDesiredPosition = 0;

    protected void createActivity() {

        mSurfaceHolder = ((SurfaceView) mSurfaceView).getHolder();
        mSurfaceHolder.addCallback(getSurfaceHolderCallback());
    }

    @Override
    public void onCreate(Bundle icicle) {
        // Do this *before* we call super since PlayMediaActivity will
        // use the view to retrieve components it needs
        setContentView(getContentView());

        super.onCreate(icicle);

        createActivity();
    }

    /**
     * To handle screen orientation
     */
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Log.d(TAG, "onConfigurationChanged called");
        super.onConfigurationChanged(newConfig);
        if (mIsVideoSizeKnown) {
            //(re)calculate size of surface;
            setSurfaceSize();
        }
    }

    protected void resumeActivity() {
        try {
            mDrmAgentException = null;

            final AtomicReference<Exception> threadException = new AtomicReference<Exception>();

            Thread thread = new Thread(new Runnable() {


                @Override
                public void run() {

                    try {
                        // The player has been preparing in the background until we get here
                        mMediaPlayer = getDRMContent().retrieveMediaPlayer(PlayMediaSharedPlayerActivity.this);
                    } catch (Exception exception) {
                        threadException.set(exception);
                    }
                }
            });
            thread.start();
            thread.join();

            if (threadException.get() != null) {
                throw threadException.get();
            }

            if (mMediaPlayer == null) {
                Log.e(TAG, "Error occurred during retrieval of media player");
                setupReturnErrorIntent(mLastIntent.getExtras(), mDrmAgentException == null ? DRMError.GENERAL_DRM_ERROR : mDrmAgentException.getDRMError(), 0);
                finish();
                return;
            }

            ActivityHelper.setupSelectedAudio(mMediaPlayer, mLastIntent.getExtras());
            ActivityHelper.setupSelectedSubtitleTrack(getDRMContent(), mMediaPlayer, mLastIntent.getExtras(), PlayMediaSharedPlayerActivity.this);

            // At this point, we should be ready for playback
            mReadyForPlay = true;

            Log.d(TAG, "Media player retrieved");
            Log.d(TAG, "Content opened: " + getElapsedTime());


            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

            mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(final SeekBar seekBar, final int currentPosition, final boolean seekBarTracking) {
                    Log.v(TAG, "Progress changed: " + currentPosition + (seekBarTracking ? "(tracking!)" : ""));
                    if (seekBarTracking) {
                        mDesiredPosition = currentPosition;
                    }
                }

                public void onStartTrackingTouch(final SeekBar seekBar) {
                    Log.v(TAG, "SeekBar tracking started");
                    mSeekbarDisabled = true;
                }

                public void onStopTrackingTouch(final SeekBar seekBar) {
                    Log.v(TAG, "SeekBar tracking stopped");
                    if (mMediaPlayer != null) {
                        Log.v(TAG, "Disable the seekbar and seeking to: " + mDesiredPosition);
                        // Means we were tracking & now we are not - seek to the time specified
                        mMediaPlayer.seekTo((mDesiredPosition * 1000));
                    } else {
                        mSeekbarDisabled = false;
                    }
                }
            });

            Log.d(TAG, "Initialization complete");

            if (mSurfaceView.isShown()) {
                Log.d(TAG, "Seems surface view is visible, will kick off player");

                setMediaListeners(this);

                mMediaPlayer.setScreenOnWhilePlaying(true);
                mMediaPlayer.setDisplay(mSurfaceHolder);

                mMediaPlayer.setVideoQualityLevelEvaluator(new MediaPlayer.VideoQualityLevelEvaluator() {
                    @Override
                    public DRMContent.VideoQualityLevel evaluateVideoQualityLevel(final DRMContent drmContent, final DRMContent.VideoQualityLevel currentVideoQualityLevel, final DRMContent.VideoQualityLevel idealVideoQualityLevel, final long estimatedBitrate, final long bufferedDurationUs) {
                        Log.d(TAG, "Evaluating video quality level: " + currentVideoQualityLevel + "/" + idealVideoQualityLevel + "/" + estimatedBitrate + "/" + bufferedDurationUs);
                        return idealVideoQualityLevel;
                    }
                });


                //prevent actions while preparing
                enableControls(false);

                mMediaPlayer.prepareAsync();
            }
        } catch (Exception e) {
            Log.d(TAG, "Error while initializing for play: " + e.getMessage(), e);

            setupReturnErrorIntent(mLastIntent.getExtras(), e instanceof DRMAgentException ?
                    ((DRMAgentException) e).getDRMError() : DRMError.GENERAL_DRM_ERROR, 0);
            finish();
        }
    }

    @Override
    protected void onResumeActivityLoaderFinished() {
        resumeActivity();
    }

    protected void playVideo() {
        doCleanUp();
        try {
            if (!mReadyForPlay) {
                Log.d(TAG, "Not ready for play, will bail");
                setupReturnErrorIntent(mLastIntent.getExtras(), mDrmAgentException == null ? DRMError.GENERAL_DRM_ERROR : mDrmAgentException.getDRMError(), 0);
                finish();
                return;
            }


            // Create a new media player and set the listeners
            setMediaListeners(this);

            mMediaPlayer.setDisplay(mSurfaceHolder);
            mMediaPlayer.setScreenOnWhilePlaying(true);

            mMediaPlayer.setVideoQualityLevelEvaluator(new MediaPlayer.VideoQualityLevelEvaluator() {
                @Override
                public DRMContent.VideoQualityLevel evaluateVideoQualityLevel(final DRMContent drmContent, final DRMContent.VideoQualityLevel currentVideoQualityLevel, final DRMContent.VideoQualityLevel selectedVideoQualityLevel, final long estimatedBitrate, final long bufferedDurationUs) {
                    Log.d(TAG, "Evaluating video quality level: " + currentVideoQualityLevel + "/" + selectedVideoQualityLevel + "/" + estimatedBitrate + "/" + bufferedDurationUs);
                    return selectedVideoQualityLevel;
                }
            });

            updateUserMessage("Connecting...");

            //prevent actions while preparing
            enableControls(false);

            mMediaPlayer.prepareAsync();
        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage(), e);
        }
    }

    public void onBufferingUpdate(MediaPlayer mediaplayer, int percent) {
        mSeekbarDisabled = true;
        Log.d(TAG, "Buffering " + percent + " %");
        updateUserMessage("Buffering " + percent + " %");
    }

    public void onVideoSizeChanged(MediaPlayer mediaplayer, int width, int height) {
        Log.v(TAG, "onVideoSizeChanged called: " + mediaplayer.getCurrentPosition() + "(" + width + "/" + height + ")");
        if (width == 0 || height == 0) {
            Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
            return;
        }
        // here we assume that the values (width and height) for the first bitrate will be used for scaling
        // the video for the duration of playback; (there could be differences, not easy to calculate,
        // between "display aspect ratio" and "pixel aspect ratio")
        if (mVideoWidth == 0) {
            mVideoWidth = width;
            mVideoHeight = height;
        }
        mIsVideoSizeKnown = true;

        //calculate size of surface;
        setSurfaceSize();

        if (!mediaplayer.isPlaying() && mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            Log.d(TAG, "Starting videoplayback after video size changed and we aint playing yet");
            startVideoPlayback(mediaplayer);
        }

        //Updating the Content Info layout
        updateContentInfoTxtView(mStreamResolutionInfoTxt, "Resolution: " + width + " x " + height);
    }

    private void setSurfaceSize() {
        ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();

        int widthToUse;
        int heightToUse;

        //calculate the scale
        int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
        int screenHeight = getWindowManager().getDefaultDisplay().getHeight();
        Log.d(TAG, "setSurfaceSize screenWidth=" + screenWidth + "; screenHeight=" + screenHeight);
        Log.d(TAG, "setSurfaceSize mVideoWidth=" + mVideoWidth + "; mVideoHeight=" + mVideoHeight);

        float wScale = (float) screenWidth / (float) mVideoWidth;
        float hScale = (float) screenHeight / (float) mVideoHeight;
        float scale = Math.min(wScale, hScale);

        Log.d(TAG, "setSurfaceSize scale=" + scale);

        widthToUse = (int) (mVideoWidth * scale);
        heightToUse = (int) (mVideoHeight * scale);

        Log.d(TAG, "setSurfaceSize - setting layout in " + (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ?
                "LANDSCAPE" : "PORTRAIT") + " mode: " + widthToUse + "/" + heightToUse);
        lp.width = widthToUse;
        lp.height = heightToUse;
        mSurfaceView.setLayoutParams(lp);
    }

    public void onPrepared(MediaPlayer mediaplayer) {
        Log.d(TAG, "onPrepared called: audioSessionID: " + mMediaPlayer.getAudioSessionId());
        mIsVideoReadyToBePlayed = true;
        startVideoPlayback(mediaplayer);
        enableControls(true);
    }

    public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {
        Log.d(TAG, "surfaceChanged called");
    }

    public void surfaceDestroyed(SurfaceHolder surfaceholder) {
        Log.d(TAG, "surfaceDestroyed called");

        if (mMediaPlayer != null) {
            mMediaPlayer.setDisplay(null);
        }
    }


    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated called");
    }

    protected void doCleanUp() {
        mVideoWidth = 0;
        mVideoHeight = 0;
        mIsVideoReadyToBePlayed = false;
        mIsVideoSizeKnown = false;
    }

    public void onSeekComplete(final MediaPlayer mediaPlayer) {
        mSeekbarDisabled = false;
        mButtonPlayPause.setEnabled(true);
        Log.v(TAG, "onSeekComplete: " + mediaPlayer.getCurrentPosition());
    }

    public boolean onError(final MediaPlayer mediaPlayer, final MediaPlayer.OnErrorEventType eventType, final Object extra) {
        String msg = "Error received: " + eventType + "/" + extra;
        Log.v(TAG, "onError: " + msg);

        switch (eventType) {
            case MEDIA_ERROR_IO:
                // Ignore apparently
                break;
            default:
                if (mMediaPlayer != null) {
                    setupReturnErrorIntent(mLastIntent.getExtras(), DRMError.MEDIA_PLAYER_INTERNAL_ERROR, 0);
                    finish();
                }
        }
        return false;
    }

    public boolean onInfo(final MediaPlayer mediaPlayer, final MediaPlayer.OnInfoEventType eventType, final Object extra) {
        Log.d(TAG, "onInfo what: " + eventType + " extra: " + extra);

        switch (eventType) {
            case MEDIA_INFO_BUFFERING_START:
                onBufferingBegin();
                mSeekbarDisabled = true;
                updateUserMessage("");
                break;
            case MEDIA_INFO_BUFFERING_END:
                onBufferingEnd();
                mSeekbarDisabled = false;
                onAudioSelectionFlag = false;
                updateUserMessage("");
                break;
            case MEDIA_INFO_BANDWIDTH: {
                // Display perceived bandwidth!
                int bandwidth = ((Integer) extra);
                Log.d(TAG, "Perceived bandwidth: " + bandwidth / 1024 + " kbps");
                if (bandwidth == 0 || bandwidth == -1) {
                    mBandwidthInfoTxt.setVisibility(View.GONE);
                } else {
                    updateContentInfoTxtView(mBandwidthInfoTxt, "Perceived Bandwidth: " + bandwidth / 1024 + " Kbps");
                }
                break;
            }
            case MEDIA_INFO_VIDEO_QUALITY_LEVEL_CHANGE: {
                int bitrate = ((Integer) extra);
                // Display new bitrate
                Toast.makeText(this, "Bitrate changed to " + bitrate, Toast.LENGTH_SHORT).show();

                Log.d(TAG, "Bitrate switch detected: " + bitrate / 1024 + " kbps");

                if (bitrate == 0 || bitrate == -1) {
                    mBitrateInfoTxt.setVisibility(View.GONE);
                } else {
                    updateContentInfoTxtView(mBitrateInfoTxt, "Current Bitrate: " + bitrate / 1024 + " Kbps");
                }
            }
            default:
        }

        return true;

    }

    public void onId3Metadata(List<Id3Frame> id3Frames) {

        for (Id3Frame id3Frame : id3Frames) {
            if (id3Frame instanceof GeobFrame) {
                GeobFrame geobFrame = (GeobFrame) id3Frame;
                Log.d(TAG,
                        String.format("onId3Metadata id: %s, mimeType: %s, filename: %s, description: %s",
                                geobFrame.id,
                                geobFrame.mimeType,
                                geobFrame.filename,
                                geobFrame.description));
            } else if (id3Frame instanceof PrivFrame) {
                PrivFrame privFrame = (PrivFrame) id3Frame;
                Log.d(TAG,
                        String.format("onId3Metadata id: %s, owner: %s",
                                privFrame.id,
                                privFrame.owner));
            } else if (id3Frame instanceof TxxxFrame) {
                TxxxFrame txxxFrame = (TxxxFrame) id3Frame;
                Log.d(TAG,
                        String.format("onId3Metadata id: %s, description: %s, value: %s",
                                txxxFrame.id,
                                txxxFrame.description,
                                txxxFrame.value));
            } else {
                Log.d(TAG,
                        String.format("onId3Metadata id: %s",
                                id3Frame.id));
            }
        }
    }

    protected abstract int getContentView();

    /**
     * Check if view is not null and set the new text for textView on the Content Info layout
     *
     * @param textView view which will be updated
     * @param text     new text for TextView
     */
    protected void updateContentInfoTxtView(final TextView textView, final String text) {
        if (textView != null) {
            textView.setText(text);
            textView.setVisibility(View.VISIBLE);
            setVisibilityContentInfoLayout(View.VISIBLE);
        }
    }

    protected SurfaceHolder.Callback getSurfaceHolderCallback() {
        return this;
    }
}
