package com.insidesecure.drm.android.reference.xoplayer.activities;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.*;
import android.text.TextUtils;
import android.util.Log;
import android.view.*;
import android.widget.*;
import com.insidesecure.drm.android.reference.*;
import com.insidesecure.drm.android.reference.activities.*;
import com.insidesecure.drm.android.reference.entitlements.URLRewritingPlayReadyEntitlementDelegate;
import com.insidesecure.drm.android.reference.xoplayer.R;
import com.insidesecure.drm.android.reference.xoplayer.activities.helpers.*;
import com.insidesecure.drm.android.reference.xoplayer.activities.helpers.MediaController;
import com.insidesecure.drmagent.*;
import com.insidesecure.drmagent.mediaplayer.MediaPlayer;
import com.insidesecure.drmagent.subtitles.Subtitle;
import com.insidesecure.drmagent.utils.*;
import com.npaw.youbora.lib6.YouboraLog;
import com.npaw.youbora.lib6.plugin.Options;
import com.npaw.youbora.lib6.plugin.Plugin;
import com.npaw.youbora.lib6.utils.youboraconfigutils.YouboraConfigManager;
import com.npaw.youbora.plugins.insidesecurexoplayer.XOPlayerAdapter;

/**
 * @author
 */
public class XOPlayMediaActivity extends AbstractDRMActivity implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener,
        MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnInfoListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnCompletionListener,
        MediaPlayer.OnErrorListener, MediaPlayer.OnTimedTextListener
{
    private static final String TAG = XOPlayMediaActivity.class.getSimpleName();

    /**
     * Captured UI elements used by the player
     */
    private SurfaceView mSurfaceView;

    private SeekBar mSeekBar;
    private TextView mStatusText;
    private TextView mDurationText;

    private LinearLayout mControlPanelLinearLayout;

    private LinearLayout mBitrateAndBandwidthInfoLayout;
    private TextView mBitrateInfoTxt;
    private TextView mBandwidthInfoTxt;
    private TextView mStreamResolutionInfoTxt;
    private TextView mRenderedBufferCountTxt;
    private ImageButton mButtonPlayPause;
    private ProgressDialog mLicenseAquisitionProgressDialog;

    private MediaPlayer mMediaPlayer;

    private boolean mPlayerNeedsPrepare;
    private boolean mEnableBackgroundAudio = false;
    
    private DRMContent mDRMContent;
    private MediaController mMediaController;
    private SubtitleHandler mSubtitleHandler;

    public CachedDurationCapturingDownloadEventListener mDownloadEventListener;
    private AudioSubtitleController mAudioSubtitleController;

    private Plugin youboraPlugin;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.video);

        mControlPanelLinearLayout = (LinearLayout) findViewById(R.id.ControlPanel);
        mControlPanelLinearLayout.setVisibility(View.INVISIBLE);

        mSurfaceView = (SurfaceView) findViewById(R.id.surface);
        mSurfaceView.getHolder().addCallback(this);

        mSurfaceView.setClickable(true);

        // Setup a click listener which will allow the user to show/hide the scrubber and play controls
        mSurfaceView.setOnClickListener(new View.OnClickListener()
        {
            public void onClick(final View view)
            {
                int visibility = mControlPanelLinearLayout.isShown() ? View.INVISIBLE : View.VISIBLE;
                mControlPanelLinearLayout.setVisibility(visibility);
                setVisibilityContentInfoLayout(visibility);
                mAudioSubtitleController.toggleVisibility(visibility);
            }
        });

        mSeekBar = (SeekBar) findViewById(R.id.SeekBar);
        mSeekBar.setProgress(0);
        mSeekBar.setMax(Integer.MAX_VALUE);

        mRenderedBufferCountTxt = (TextView) findViewById(R.id.rendered_output_buffer_count_txt);

        mBitrateAndBandwidthInfoLayout = (LinearLayout) findViewById(R.id.bitrate_info_layout);
        setVisibilityContentInfoLayout(View.GONE);

        mBitrateInfoTxt = (TextView) findViewById(R.id.current_bitrate_txt);
        mBandwidthInfoTxt = (TextView) findViewById(R.id.perceived_bandwidth_txt);
        mStreamResolutionInfoTxt = (TextView) findViewById(R.id.stream_resolution_txt);

        mStatusText = (TextView) findViewById(R.id.StatusTextView);
        mDurationText = (TextView) findViewById(R.id.DurationTextView);

        mButtonPlayPause = (ImageButton) findViewById(R.id.PlayPauseButton);
        mButtonPlayPause.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(final View view)
            {
                if (mMediaPlayer == null)
                {
                    // We are not ready -
                    preparePlayer();
                    return;
                }
                else
                {
                    if (!mMediaPlayer.isPlaying())
                    {
                        mMediaPlayer.start();
                        mButtonPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.pause_selector));
                    }
                    else
                    {
                        mMediaPlayer.pause();
                        mButtonPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.play_selector));
                    }
                }
            }
        });
        mButtonPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.play_selector));

        mLicenseAquisitionProgressDialog = new ProgressDialog(this);
        mLicenseAquisitionProgressDialog.setIndeterminate(true);
        mLicenseAquisitionProgressDialog.setTitle("Acquiring license");
        mLicenseAquisitionProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

    }

    @Override
    public void onNewIntent(Intent intent)
    {
        releasePlayer();
        // playerPosition = 0;
        setIntent(intent);
        super.onNewIntent(intent);
    }

    @Override
    public void onStart()
    {
        super.onStart();
        
        if (AndroidVersion.BUILD_VERSION_SDK_INT > AndroidVersion.MARSHMALLOW)
        {
            onShown();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();

        if (AndroidVersion.BUILD_VERSION_SDK_INT <= AndroidVersion.MARSHMALLOW || mMediaPlayer == null)
        {
            onShown();
        }
    }

    private void onShown()
    {
        Intent intent = getIntent();
        if (mMediaPlayer == null)
        {
            preparePlayer();
        }
        else
        {
            mMediaPlayer.start();
        }
    }

    @Override
    public void onPause()
    {
        if (mMediaPlayer != null && mMediaPlayer.isPlaying())
        {
            mMediaPlayer.pause();
        }

        if (AndroidVersion.BUILD_VERSION_SDK_INT <= AndroidVersion.MARSHMALLOW)
        {
            onHidden();
        }

        super.onPause();
    }

    @Override
    public void onStop()
    {
        super.onStop();
        if (AndroidVersion.BUILD_VERSION_SDK_INT > AndroidVersion.MARSHMALLOW)
        {
            onHidden();
        }
    }

    private void onHidden()
    {
        if (!mEnableBackgroundAudio)
        {
            releasePlayer();
        }
        else
        {
            mMediaPlayer.pause();
        }

        // Rest the OFFLINE state after we go into background
        DRMAgentDelegate.setOfflineMode(false);
    }

    @Override
    public void onDestroy()
    {
        releasePlayer();

        mLicenseAquisitionProgressDialog.dismiss();

        super.onDestroy();
    }

    // SurfaceHolder.Callback implementation

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        if (mMediaPlayer != null)
        {
            mMediaPlayer.setDisplay(holder);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        // Do nothing.
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        if (mMediaPlayer != null)
        {
            mMediaPlayer.setDisplay(null);
        }
    }

    private void releasePlayer()
    {
        if (mSubtitleHandler != null)
        {
            mSubtitleHandler.shutdown();
        }

        if (mMediaController != null)
        {
            mMediaController.shutdown();
            mMediaController = null;
        }

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

    private void preparePlayer()
    {
        if (mMediaPlayer == null)
        {
            if (mDRMContent == null)
            {
                // We need to wait until the DRM content is available - it will come in the onDRMCOntentInstanceCreatedInternal method
                return;
            }

            // We setup whether or not the playback should be done in OFFLINE mode - e.g. to error out if any network access occurs during playback
            DRMAgentDelegate.setOfflineMode(getPlaybackInfo().mOfflineMode);
            YouboraLog.setDebugLevel(YouboraLog.Level.VERBOSE);
            mMediaPlayer = getDRMContent().retrieveMediaPlayer(XOPlayMediaActivity.this);
            initializeYoubora();
            // Setup the created media player with the appropriate listeners
            configureMediaPlayerBeforePrepare(mMediaPlayer);

            mPlayerNeedsPrepare = true;
        }

        if (mPlayerNeedsPrepare)
        {
            setLoadingDialogText("Preparing media player");
            mMediaPlayer.prepareAsync();
        }

    }

    private void initializeYoubora() {
        YouboraLog.setDebugLevel(YouboraLog.Level.VERBOSE);
        youboraPlugin = new Plugin(YouboraConfigManager.getInstance().getOptions(this));
        youboraPlugin.setActivity(this);
    }

    private void configureMediaPlayerBeforePrepare(final MediaPlayer mediaPlayer)
    {
        mediaPlayer.setOnPreparedListener(this);
        mediaPlayer.setOnVideoSizeChangedListener(this);
        mediaPlayer.setOnInfoListener(this);
        mediaPlayer.setOnCompletionListener(this);
        mediaPlayer.setOnTimedTextListener(this);
        mediaPlayer.setOnSeekCompleteListener(this);
        mediaPlayer.setOnErrorListener(this);

        XOPlayerAdapter adapter = new XOPlayerAdapter(mMediaPlayer);
        youboraPlugin.setAdapter(adapter);
        adapter.setOnPreparePluginListener(this);
        adapter.setOnVideoSizeChangedPluginListener(this);
        adapter.setOnInfoPluginListener(this);
        adapter.setOnCompletionPluginListener(this);
        adapter.setOnSeekCompletePluginListener(this);
        adapter.setOnErrorPluginListener(this);

        // Optional listeer to evaluate (and potentially override) the selection of video quality levels (bitrates)
        mediaPlayer.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;
            }
        });

        final Bundle extras = getIntent().getExtras();
        ActivityHelper.setupSelectedAudio(mediaPlayer, extras);
        ActivityHelper.setupSelectedSubtitleTrack(getDRMContent(), mediaPlayer,extras, XOPlayMediaActivity.this);

        mAudioSubtitleController = new AudioSubtitleController(this,getDRMContent(), mediaPlayer);

    }


    @Override
    public void onPrepared(final MediaPlayer mediaPlayer)
    {
        setLoadingDialogText("Starting playback");

        mPlayerNeedsPrepare = false;
        mediaPlayer.setDisplay(mSurfaceView.getHolder());

        startPlayback(mediaPlayer);

        mButtonPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.pause_selector));
        dismissLoadingDialog();

        int visibility = mControlPanelLinearLayout.isShown() ? View.INVISIBLE : View.VISIBLE;
        mControlPanelLinearLayout.setVisibility(visibility);
        setVisibilityContentInfoLayout(visibility);
        mAudioSubtitleController.toggleVisibility(visibility);

    }

    @Override
    protected void onDRMContentInstanceCreatedInternal(final DRMContent drmContent)
    {

        final DRMRights drmRights = drmContent.getDRMRights();
        final DRMRights.DRMRightsType drmRightsType = drmRights.getDRMRightsType();
        
        if (drmRightsType != DRMRights.DRMRightsType.VALID && drmRightsType != DRMRights.DRMRightsType.RIGHTS_ON_DEMAND)
        {
            // Now what?
            setupReturnErrorIntent(getIntent().getExtras(), DRMError.NO_RIGHTS);
            XOPlayMediaActivity.this.finish();
            return;
        }

        // Ok, DRM content instance is now available, we can now start playback
        mDRMContent = drmContent;

        initializeDRMContent(drmContent);

        preparePlayer();
    }

    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;
        }

        //calculate the scale
        int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
        int screenHeight = getWindowManager().getDefaultDisplay().getHeight();

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

        float wScale = (float) screenWidth / (float) width;
        float hScale = (float) screenHeight / (float) height;
        float scale = Math.min(wScale, hScale);

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

        int widthToUse = (int) (width * scale);
        int heightToUse = (int) (height * scale);

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

        ViewGroup.LayoutParams layoutParams = mSurfaceView.getLayoutParams();
        layoutParams.width = widthToUse;
        layoutParams.height = heightToUse;
        mSurfaceView.setLayoutParams(layoutParams);

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

    protected void onBufferingEnd()
    {
        if (mMediaController != null)
        {
            mMediaController.setUpdating(true);
        }
    }

    protected void onBufferingBegin()
    {
        setLoadingDialogText("Buffering...");
        mStatusText.setText("Buffering...");
    }


    public void onCompletion(MediaPlayer mediaPlayer)
    {
        Toast.makeText(XOPlayMediaActivity.this, "Playback complete", Toast.LENGTH_LONG).show();
        mButtonPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.play_selector));

        if (mMediaController != null)
        {
            mMediaController.complete();
        }

        releasePlayer();


    }

    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();
                break;
            case MEDIA_INFO_BUFFERING_END:
                onBufferingEnd();
                break;
            case MEDIA_INFO_BANDWIDTH:
                bandwidthUpdate((Integer) extra);
                break;
            case MEDIA_INFO_VIDEO_QUALITY_LEVEL_CHANGE:
                bitrateSwitch((Integer) extra);
                break;
            case MEDIA_INFO_VIDEO_RENDERING_START:
                firstFrameRendered();
                break;
            default:
                Log.d(TAG,"Unhandled event: " + eventType);
        }

        return true;

    }

    @Override
    public void onSeekComplete(final MediaPlayer mediaPlayer)
    {
        // Once a seek is over, we can allow the media bar updater to reflect the new position of the player
        if (mMediaController != null)
        {
            mMediaController.setUpdating(true);
        }
    }

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

        DRMError drmError = DRMError.MEDIA_PLAYER_INTERNAL_ERROR;
        if (extra instanceof DRMAgentException)
        {
            drmError = ((DRMAgentException) extra).getDRMError();
        }
        setupReturnErrorIntent(getIntent().getExtras(), drmError);
        finish();

        return false;
    }

    public void onSubtitle(final MediaPlayer mp, final Subtitle subtitle)
    {
        // Subtitle UI presentation is proxied away into the audio/subtitle controller
        mAudioSubtitleController.onSubtitle(mp, subtitle);
    }

    public void initializeDRMContent(final DRMContent drmContent)
    {
        try
        {
            final Bundle extras = getIntent().getExtras();

            setupDRMContentListener(extras, drmContent);

            setupEntitlementDelegate(drmContent);

            ActivityHelper.setupHDMIControl(XOPlayMediaActivity.this, getDRMAgent(), extras);

            ActivityHelper.setupSelectedVideoQualityLevels(drmContent, extras);

            if (getPlaybackInfo().mDownloadAndPlay)
            {
                mDownloadEventListener = new CachedDurationCapturingDownloadEventListener(XOPlayMediaActivity.this);
                ActivityHelper.setupDownloadAndPlay(drmContent, mDownloadEventListener, extras);
            }
        }
        catch (Exception e)
        {
            Log.e(TAG,"Error while initializing DRM content: " + e.getMessage(), e);
            setupReturnErrorIntent(getIntent().getExtras(),e);
            finish();
        }

    }

    protected void setupDRMContentListener(final Bundle extras, final DRMContent drmContent)
    {
        DRMContentListener drmContentListener = new DRMContentListener()
        {
            @Override
            public void error(DRMContent drmContent, DRMError error)
            {
                Log.w(TAG, "Received error from the DRM agent: " + error);
                Toast.makeText(XOPlayMediaActivity.this, "Error received: " + error, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void licenseAcquisitionInitiated(final DRMContent drmContent)
            {
                Log.i(TAG, "License acquisition initiated");
                Toast.makeText(XOPlayMediaActivity.this, "License acquisition initiated", Toast.LENGTH_SHORT).show();
                mLicenseAquisitionProgressDialog.show();
            }

            @Override
            public void licenseAcquisitionCompleted(final DRMContent drmContent, final DRMError drmError)
            {
                Log.i(TAG, "License acquisition completed: " + drmError);
                mLicenseAquisitionProgressDialog.hide();
            }

        };
        drmContent.setDRMContentListener(drmContentListener);
    }

    private void setupReturnErrorIntent(Bundle extras, Exception exception)
    {
        setupReturnErrorIntent(extras, exception == null ? DRMError.GENERAL_DRM_ERROR : exception instanceof DRMAgentException ? ((DRMAgentException) exception).getDRMError() : DRMError.GENERAL_DRM_ERROR);
    }

    private void setupReturnErrorIntent(Bundle extras, DRMError drmError)
    {
        Intent intent = new Intent(getBaseContext(), LicenseAcquisitionActivity.class);
        intent.putExtras(extras);
        intent.putExtra("DRM_ERROR", drmError);
        setResult(LicenseAcquisitionActivity.RESULT_DRM_ERROR, intent);
    }

    private void firstFrameRendered()
    {
    }

    private void bitrateSwitch(final Integer bitrate)
    {
        if (bitrate > 0)
        {
            Toast.makeText(this, "Bitrate changed to " + bitrate, Toast.LENGTH_SHORT).show();
            Log.d(TAG, "Bitrate switch detected: " + bitrate / 1024 + " kbps");
            updateContentInfoTxtView(mBitrateInfoTxt, "Current Bitrate: " + bitrate / 1024 + " kbps");
        }
    }

    private void bandwidthUpdate(final Integer bandwidth)
    {
        // Display perceived bandwidth!
        Log.d(TAG, "Perceived bandwidth: " + bandwidth / 1024 + " kbps");
        if (bandwidth >= 0)
        {
            updateContentInfoTxtView(mBandwidthInfoTxt, "Perceived Bandwidth: " + bandwidth / 1024 + " Kbps");
        }

    }

    /**
     * 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
     */
    private void updateContentInfoTxtView(final TextView textView, final String text)
    {
        if (textView != null)
        {
            textView.setText(text);
            textView.setVisibility(View.VISIBLE);
            setVisibilityContentInfoLayout(View.VISIBLE);
        }
    }

    private void setVisibilityContentInfoLayout(final int visibility)
    {
        if (mBitrateAndBandwidthInfoLayout != null)
        {
            // Content layout can be visible only if it has at least one non empty text view with the info
            if (visibility == View.VISIBLE && mControlPanelLinearLayout.isShown() && (!TextUtils.isEmpty(mBitrateInfoTxt.getText()) || !TextUtils.isEmpty(mBandwidthInfoTxt.getText()) || !TextUtils.isEmpty(mStreamResolutionInfoTxt.getText())))
            {
                mBitrateAndBandwidthInfoLayout.setVisibility(View.VISIBLE);
            }
            else
            {
                mBitrateAndBandwidthInfoLayout.setVisibility(View.GONE);
            }
        }
    }

    private void setupEntitlementDelegate(final DRMContent drmContent)
    {
        PlaybackInfo playbackInfo = getPlaybackInfo();

        drmContent.setCustomData(playbackInfo.mCustomData);
        drmContent.setEntitlementDelegate(new URLRewritingPlayReadyEntitlementDelegate(XOPlayMediaActivity.this, playbackInfo.mLicenseAcquisitionURL));

    }


    private void startPlayback(final MediaPlayer mediaPlayer)
    {
        mMediaController = new com.insidesecure.drm.android.reference.xoplayer.activities.helpers.MediaController(mediaPlayer, mSeekBar, mDurationText, mStatusText, false, getDRMContent().isLive(), Looper.getMainLooper(), new com.insidesecure.drm.android.reference.xoplayer.activities.helpers.MediaController.MediaInformationExchanger()
        {
            private long mDurationCached = -1;
            @Override
            public long getDurationMs()
            {
                return mediaPlayer.getDuration();
            }

            @Override
            public long getCurrentCachedDurationMs()
            {
                if (mDRMContent.isDownloadAndPlay())
                {
                    long durationCached = mDurationCached;

                    // If in offline mode, use the value from the cache information (only once)
                    if (getPlaybackInfo().mOfflineMode)
                    {
                        if (durationCached == -1)
                        {
                            mDurationCached = mDRMContent.getDRMCacheInfo().getDurationCached() * 1000;
                            durationCached = mDurationCached;
                        }
                    }
                    else
                    {
                        // If we are in online mode && we have a listener, use that value
                        if (mDownloadEventListener != null)
                        {
                            durationCached = mDownloadEventListener.getCurrentCachedDurationMs();
                        }
                        else
                        {
                            mDurationCached = mDRMContent.getDRMCacheInfo().getDurationCached()  * 1000;
                            durationCached = mDurationCached;
                        }
                    }

                    return durationCached;
                }
                else
                {
                    return 0;
                }
            }

            @Override
            public long getCurrentPositionMs()
            {
                return mediaPlayer.getCurrentPosition();
            }

            @Override
            public void performSeek(final long seekPositionMs)
            {
                mediaPlayer.seekTo(seekPositionMs);
            }

            @Override
            public void frameRateUpdate(final int currentFramePerSec)
            {
                updateContentInfoTxtView(mRenderedBufferCountTxt, "Rendered Buffer Count / Sec.: " + currentFramePerSec);
            }
        });

        mediaPlayer.start();
    }
}
