package com.nielsen.simple_noads_android_8100.activities;

import android.app.ProgressDialog;
import android.media.MediaPlayer;
import android.media.TimedMetaData;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.nielsen.app.sdk.AppSdk;
import com.nielsen.app.sdk.IAppNotifier;
import com.nielsen.simple_noads_android_8100.R;
import com.nielsen.simple_noads_android_8100.models.NielsenInit;
import com.nielsen.simple_noads_android_8100.models.SDKMethods;
import com.nielsen.simple_noads_android_8100.utils.Constants;

import org.json.JSONObject;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity implements IAppNotifier, SurfaceHolder.Callback, View.OnClickListener,
        MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener,
        MediaPlayer.OnTimedMetaDataAvailableListener, MediaPlayer.OnInfoListener {

    public static final String TAG = MainActivity.class.getSimpleName();

    private SurfaceView mSurfaceView;
    private SeekBar seek;
    Button btnPlay;
    TextView txtMetadata;

    LandingActivity la = new LandingActivity();
    private int videoType, totalVideos;
    private int totalVideosPlayed = 0;
    private boolean isVideoStarted = true, isPaused = false;
    JSONObject data = null;

    private MediaPlayer mMediaPlayer;
    private SurfaceHolder mSurfaceHolder;

    SDKMethods sdkMethods;

    private ProgressDialog dialog;
    private Handler playheadHandler;
    private Runnable playheadRunnable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Mark: In SDKMethods class we wrote methods which creates content,Ad,DTVR objects,urls
        sdkMethods = new SDKMethods();

        initUI();
        getIntents();

        mSurfaceView =  findViewById(R.id.surface_view);
        mSurfaceView.setVisibility(View.VISIBLE);

        showProgressDialog();

        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(MainActivity.this);

    }

    private void initUI() {

        //Initialising UI elements

        seek =  findViewById(R.id.seek);
        btnPlay =  findViewById(R.id.btnPlay);
        txtMetadata = (TextView) findViewById(R.id.txtMetadata);
        btnPlay.setOnClickListener(this);


    }

    private void getIntents() {

        //Getting intents from previous activity
        videoType = getIntent().getIntExtra(Constants.INTENT_VIDEO_TYPE, 0);
        totalVideos = getIntent().getIntExtra(Constants.INTENT_TOTAL_VIDEOS, 0);

        if(videoType == Constants.onlyContent){
            //loading video content url
            sdkMethods.setContentUrl();
            txtMetadata.setText(sdkMethods.loadContentData().toString());

        }else if(videoType == Constants.dtvrVideo){
            //loading video DTVR url
            sdkMethods.setDtvrUrl();
            txtMetadata.setText(sdkMethods.loadDtvr().toString());

        }else{
            //loading video Ad url
            sdkMethods.setAdUrl();
            txtMetadata.setText(sdkMethods.loadPreRollAdData().toString());
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Log.v(TAG, "surfaceCreated Called");

        if (isPaused) {
            //Once video is resumed after pause, setting surfaceholder to player.
            if (mMediaPlayer != null) {

                mSurfaceHolder = mSurfaceView.getHolder();
                mMediaPlayer.setDisplay(mSurfaceHolder);
            }

        } else {

            //This will execute only for first time.
            setUpPlayer();
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnErrorListener(this);
        }
    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer) {
        try {

            playheadHandler.removeCallbacks(playheadRunnable);

            ///As video completed playing, incrementing the variable value.
            totalVideosPlayed++;

            if (videoType == Constants.onlyContent || totalVideosPlayed == totalVideos) {

                //When content video completes or total videos finishes, call "end" method.
                la.appSdk.end();
            } else {
                //On completion of "AD" call "stop" method.
                la.appSdk.stop();
            }


            releaseMediaPlayer();

            checkVideosToBePlayed();

         } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Checks if any videos are pending to play.
    private void checkVideosToBePlayed(){

        //Checking if total videos played or not.
        if (totalVideosPlayed != totalVideos) {

            data = new JSONObject();

            //Checking if videoType is contentWithOneAd, then after completion of Ad, will play the content video.
            if (videoType == Constants.contentWithOneAd) {

                //loading video content data
                data = sdkMethods.loadContentData();
                txtMetadata.setText(sdkMethods.loadContentData().toString());

            }

            showProgressDialog();

            setUpPlayer();

            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnErrorListener(this);

        }
    }

    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int li) {
        Log.e(TAG, "Player error codes:" + i + ", " + li);
        return false;
    }

    //creating player
    private void setUpPlayer() {
        try {

            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setDisplay(mSurfaceHolder);
            mMediaPlayer.setDataSource(sdkMethods.url);
            mMediaPlayer.setOnPreparedListener(MainActivity.this);
            mMediaPlayer.prepareAsync();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.v(TAG, "surfaceChanged Called");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.v(TAG, "surfaceDestroyed Called");
    }

    @Override
    public void onPrepared(MediaPlayer mp) {

        if (dialog.isShowing()) {
            dialog.dismiss();
        }


        if(totalVideosPlayed == 0){
            //Only for first time sending Channel info to SDK using "play" method.
            la.appSdk.play(sdkMethods.loadChannelInfo());
        }

        if(videoType == Constants.dtvrVideo){

            //loading DTVR metadata
            data = sdkMethods.loadDtvr();
        }else{

            //loading video content metadata data
            data = sdkMethods.loadContentData();
        }
        //Sending metadata to SDK.
        la.appSdk.loadMetadata(data);

        if(videoType == Constants.contentWithOneAd && totalVideosPlayed == 0){
            //loading Ad data before playing ad
            data = sdkMethods.loadPreRollAdData();

            //Sending Ad metadata to SDK.
            la.appSdk.loadMetadata(data);
        }

        if(videoType == Constants.dtvrVideo){

            //Setting infoListener and TimedMetaDataListener to listen ID3 tags, needed only for DTVR
            mMediaPlayer.setOnInfoListener(this);
            mMediaPlayer.setOnTimedMetaDataAvailableListener(this);
        }

        //Setting max value to seekbar
        seek.setMax(convertTotime(mMediaPlayer.getDuration()));
        isVideoStarted = true;

        updateSeekbarAndPlayhead();
        mMediaPlayer.start();

    }

    @Override
    protected void onPause() {
        super.onPause();

        setPauseAction();
    }

    private void setPauseAction() {
        try {
            if (mMediaPlayer != null) {

                isVideoStarted = false;
                isPaused = true;
                //Pausing media player
                mMediaPlayer.pause();

                //As video is paused calling "stop" method of SDK.
                la.appSdk.stop();

                btnPlay.setText(getString(R.string.play));

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaPlayer();
    }

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

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btnPlay:

                playPauseMedia();
                break;
        }

    }

    private void playPauseMedia(){

        try {
            //If video is not yet played, then it will "play" else it will "pause" the video
            if (!isVideoStarted) {

                btnPlay.setText(getString(R.string.pause));

                if (isPaused) {

                    isVideoStarted = true;
                    isPaused = false;

                    mMediaPlayer.start();

                } else {

/*
                    mSurfaceView =  findViewById(R.id.surface_view);
                    mSurfaceView.setVisibility(View.VISIBLE);
*/

                    showProgressDialog();

                    mSurfaceHolder = mSurfaceView.getHolder();
                    mSurfaceHolder.addCallback(MainActivity.this);

                }

            } else {
                //Pause button clicked, so pausing the video
                setPauseAction();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Updates seekbar
    private void updateSeekbarAndPlayhead() {
        try {
            playheadHandler = new Handler();


            //Make sure you update Seekbar on UI thread
            MainActivity.this.runOnUiThread(playheadRunnable = new Runnable() {

                @Override
                public void run() {
                    if (mMediaPlayer != null) {

                        //getting current position of media player
                        int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;

                        seek.setProgress(mCurrentPosition);
                        try {
                            //Checking If video player is not paused and video is playing and is not a DTVR video
                            if (!isPaused && mMediaPlayer.isPlaying() && videoType != Constants.dtvrVideo) {

                                //Sending playHeadPosition to SDK. (Not needed for DTVR video)
                                la.appSdk.setPlayheadPosition(mCurrentPosition);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    }
                    playheadHandler.postDelayed(this, 1000);
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private int convertTotime(long milliSec) {

        return (int) (milliSec / 1000);
    }

    private void showProgressDialog() {

        runOnUiThread(new Runnable() {
            public void run() {
                //Showing progress dialog
                dialog = new ProgressDialog(MainActivity.this);
                dialog.setCancelable(false);
                dialog.setMessage(getString(R.string.loading));
                dialog.show();
            }
        });
    }

    @Override
    public void onAppSdkEvent(long l, int i, String s) {
        //Default method of IAppNotifier interface
    }

    //Needed only for DTVR video
    @Override
    public void onTimedMetaDataAvailable(MediaPlayer mediaPlayer, TimedMetaData timedMetaData) {

        if(timedMetaData != null && timedMetaData.getMetaData() != null && mMediaPlayer.isPlaying()){

            //getting metadata.
            String iD3Payload = new String(timedMetaData.getMetaData(), StandardCharsets.UTF_8);

            //If tag metadata contains "www.nielsen.com", then only sending to SDK
            if (null != iD3Payload && iD3Payload.contains("www.nielsen.com"))
            {
                //getting index
                int index = iD3Payload.indexOf("www.nielsen.com");

                //getting substring as ID3 tag will be of 249 characters
                String id3String = iD3Payload.substring(index, (index + 249));
                Log.d(TAG, "TimedMetaData ID3 Tag:" + id3String);

                //Sending ID3 tag to SDK.
                la.appSdk.sendID3(id3String);
            }

        }

    }

    //Needed only for DTVR video
    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra)
    {
        boolean isInfoHandled = false;
        selectId3Track(mp);
        isInfoHandled = true;
       /* if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE)
        {
            // Once metadata is available, select an ID3 track in order
            // to be able to receive timed metadata updates.
            selectId3Track(mp);
            isInfoHandled = true;
        }*/
        return isInfoHandled;
    }

    //Selects ID3 tag to receive TimedMetadata.
    private void selectId3Track(MediaPlayer mp)
    {
        try
        {
            if (mp != null)
            {
                //Getting the trackinfo of mediaplayer
                MediaPlayer.TrackInfo[] tracks = mp.getTrackInfo();
                if (tracks.length > 0)
                {
                    //Getting ID3 Pattern
                    Pattern id3mime = Pattern.compile("mime=.*\\/.*id3.*,", Pattern.CASE_INSENSITIVE);
                    for (int i = 0; i < tracks.length; i++)
                    {
                        //Getting single track from array
                        MediaPlayer.TrackInfo track = tracks[i];

                        //Matching track with ID3 pattern
                        if (id3mime.matcher(track.toString()).find())
                        {
                            //Selecting ID3 track to receive timedMetaData.
                            mp.selectTrack(i);
                            Log.d(TAG, "Track #" + i + " has been selected ::: " + track);
                            break;
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            Log.e(TAG, "Couldn't select ID3 track for MediaPlayer", e);
        }
    }
}
