package com.castlabs.sdk.castdemo;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar;
import androidx.fragment.app.FragmentActivity;
import androidx.mediarouter.app.MediaRouteButton;
import android.util.Log;
import android.widget.Toast;

import com.castlabs.android.SdkConsts;
import com.castlabs.android.player.AbstractPlayerListener;
import com.castlabs.android.player.PlayerController;
import com.castlabs.android.player.PlayerView;
import com.castlabs.android.player.models.AudioTrack;
import com.castlabs.android.player.models.SubtitleTrack;
import com.castlabs.android.player.models.VideoTrackQuality;
import com.castlabs.subtitles.presentation.SubtitlesStyle;
import com.castlabs.sdk.googlecast.GoogleCastManager;
import com.castlabs.sdk.playerui.PlayerControllerProgressBar;
import com.castlabs.sdk.playerui.PlayerControllerView;
import com.google.android.gms.cast.framework.CastButtonFactory;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.cast.framework.Session;
import com.google.android.gms.cast.framework.SessionManager;
import com.google.android.gms.cast.framework.SessionManagerListener;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;

import org.json.JSONException;
import org.json.JSONObject;

public class SimplePlaybackDemo extends FragmentActivity {
	private static final String TAG = "SimplePlaybackDemo";
	// This is the player view that we use to start playback
	private PlayerView playerView;

	// Google cast media route button
	private MediaRouteButton mMediaRouteButton;

	private final SessionManagerListener mSessionManagerListener = new SessionManagerListenerImpl();
	private final RemoteMediaClient.Callback mRemoteClientCallback = new RemoteClientCallback();

	private SessionManager mSessionManager;

	// Members to recover tracks when stopping cast
	private Long remotePosition = null;
	private Integer qualityBitrate = null;
	private String audioLanguage = null;
	private String subtitleLanguage = null;
	private SubtitlesStyle subtitlesStyle = null;

	private class SessionManagerListenerImpl implements SessionManagerListener {
		@Override
		public void onSessionStarting(Session session) {

		}

		@Override
		public void onSessionStarted(Session session, String sessionId) {
			// Register the remote client listener
			mSessionManager.getCurrentCastSession().getRemoteMediaClient().registerCallback(mRemoteClientCallback);

			// Start casting whatever the current player is playing
			//GoogleCastManager.startCasting(playerView.getPlayerController(), mSessionManager, null);

			// Start cast with a Bundle, in the same way we start the PlayerController playback
			// If you want to set Cast ads, you should use this option
			final Bundle castBundle = getIntent().getExtras();

			final PlayerController playerController = playerView.getPlayerController();

			// Position to play
			castBundle.putLong(SdkConsts.INTENT_POSITION_TO_PLAY, playerController.getPosition());
			// Start playing or not
			final PlayerController.State state = playerController.getPlayerState();
			boolean startPlaying = state == PlayerController.State.Playing
				|| state == PlayerController.State.Preparing
				|| state == PlayerController.State.Buffering;
			castBundle.putBoolean(SdkConsts.INTENT_START_PLAYING, startPlaying);
			// Stream duration
			castBundle.putLong(GoogleCastManager.INTENT_STREAM_DURATION, playerController.getDuration());
			// Live or buffered content
			castBundle.putBoolean(GoogleCastManager.INTENT_STREAM_LIVE, playerController.isLive());
			// Currently selected audio track
			if (playerController.getAudioTrack() != null) {
				castBundle.putString(SdkConsts.INTENT_PREFERRED_AUDIO_LANGUAGE, playerController.getAudioTrack().getLanguage());
			}
			// Currently selected subtitle
			if (playerController.getSubtitleTrack() != null) {
				castBundle.putString(SdkConsts.INTENT_PREFERRED_TEXT_LANGUAGE, playerController.getSubtitleTrack().getLanguage());
			}
			// Current SubtitlesStyle
			if (playerController.getSubtitlesStyle() != null) {
				castBundle.putParcelable(GoogleCastManager.INTENT_SUBTITLES_STYLE, playerController.getSubtitlesStyle());
			}

			GoogleCastManager.startCasting(castBundle, mSessionManager, null);
			// Once we start casting, pause local player
			playerController.pause();
		}

		@Override
		public void onSessionStartFailed(Session session, int i) {

		}

		@Override
		public void onSessionEnding(Session session) {

		}

		@Override
		public void onSessionResumed(Session session, boolean wasSuspended) {
			Toast.makeText(SimplePlaybackDemo.this, "Session resumed", Toast.LENGTH_SHORT).show();
			playerView.getPlayerController().pause();
			switchToExpandedControls();
		}

		@Override
		public void onSessionResumeFailed(Session session, int i) {

		}

		@Override
		public void onSessionSuspended(Session session, int i) {

		}

		@Override
		public void onSessionEnded(Session session, int error) {
			// Remove the remote client listener
			RemoteMediaClient mediaClient = mSessionManager.getCurrentCastSession().getRemoteMediaClient();
			if (mediaClient != null) { ;
				mediaClient.unregisterCallback(mRemoteClientCallback);
			}

			// Exit activity when cast session ends
			finish();
		}

		@Override
		public void onSessionResuming(Session session, String s) {

		}
	}

	private class RemoteClientCallback extends RemoteMediaClient.Callback {

		@Override
		public void onStatusUpdated() {

			RemoteMediaClient remoteMediaClient = mSessionManager.getCurrentCastSession().getRemoteMediaClient();

			if (remoteMediaClient.hasMediaSession()) {
				switchToExpandedControls();
				remoteMediaClient.unregisterCallback(this);
			}
		}
	}

	private void switchToExpandedControls() {
		Intent i = new Intent(SimplePlaybackDemo.this, ExpandedControlsActivity.class);
		startActivityForResult(i, 0);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		// We can only come from the ExpandedControlsActivity
		// Here we get the remote playback position and track selections and set it to our local player
		if (data != null) {
			// Save position and tracks to set them to our local player afterwards
			long position = data.getLongExtra(ExpandedControlsActivity.INTENT_RESULT_REMOTE_PLAYER_POSITION, SdkConsts.UNKNOWN_TIME_US);
			if (position != SdkConsts.UNKNOWN_TIME_US) {
				remotePosition = position;
			}
			int bitrate = data.getIntExtra(ExpandedControlsActivity.INTENT_RESULT_REMOTE_PLAYER_VIDEO_QUALITY_BITRATE, -1);
			if (bitrate != -1) {
				qualityBitrate = bitrate;
			}
			audioLanguage = data.getStringExtra(ExpandedControlsActivity.INTENT_RESULT_REMOTE_PLAYER_AUDIO_LANGUAGE);
			subtitleLanguage = data.getStringExtra(ExpandedControlsActivity.INTENT_RESULT_REMOTE_PLAYER_SUBTITLE_LANGUAGE);
			subtitlesStyle = data.getParcelableExtra(ExpandedControlsActivity.INTENT_RESULT_REMOTE_PLAYER_SUBTITLES_STYLE);
		}

		playerView.getPlayerController().addPlayerListener(new AbstractPlayerListener() {
			@Override
			public void onPlayerModelChanged() {
				final PlayerController playerController = playerView.getPlayerController();
				if (remotePosition != null) {
					// Remote is in milliseconds and our player controller works in microseconds
					playerController.setPosition(remotePosition * 1000);
					remotePosition = null;
				}
				// Tracks to set to the PlayerController
				VideoTrackQuality selectedQuality = null;
				AudioTrack selectedAudioTrack = null;
				SubtitleTrack selectedSubtitleTrack = null;

				if (qualityBitrate != null) {
					for (VideoTrackQuality quality : playerController.getVideoQualities()) {
						if (qualityBitrate == quality.getBitrate()) {
							selectedQuality = quality;
							break;
						}
					}
					qualityBitrate = null;
				}
				if (audioLanguage != null) {
					for (AudioTrack audioTrack : playerController.getAudioTracks()) {
						if (audioLanguage.equals(audioTrack.getLanguage())) {
							selectedAudioTrack = audioTrack;
							break;
						}
					}
					audioLanguage = null;
				}
				if (subtitleLanguage != null) {
					for (SubtitleTrack subtitleTrack : playerController.getSubtitleTracks()) {
						if (subtitleLanguage.equals(subtitleTrack.getLanguage())) {
							selectedSubtitleTrack = subtitleTrack;
							break;
						}
					}
					subtitleLanguage = null;
				}

				// Set tracks
				playerController.setVideoQuality(selectedQuality);
				playerController.setAudioTrack(selectedAudioTrack);
				playerController.setSubtitleTrack(selectedSubtitleTrack);
				playerController.setSubtitlesStyle(subtitlesStyle);
				subtitlesStyle = null;


				playerController.removePlayerListener(this);

				// Resume Playback
				playerController.play();
			}
		});
	}

	@SuppressLint("MissingInflatedId")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_simple_playback_demo);

		// Get the view components from the layout
		playerView = (PlayerView) findViewById(R.id.player_view);

		// This demo assumes that you send an intent to this Activity that contains the
		// playback information. We need to pass the intents bundle on to the PlayerController
		// to start playback. The open() method might throw an Exception in case the bundle
		// contains not all mandatory parameters or the parameters are malformed.
		if (getIntent() != null) {
			try {
				playerView.getPlayerController().open(getIntent().getExtras());
			} catch (Exception e) {
				Log.e(TAG, "Error while opening player: " + e.getMessage(), e);
				Snackbar.make(playerView, "Error while opening player: " + e.getMessage(),
						Snackbar.LENGTH_INDEFINITE).show();
			}
		} else {
			Snackbar.make(playerView, "No intent specified", Snackbar.LENGTH_INDEFINITE).show();
		}

		// Google cast setup
		mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
		CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton);

		// Get session manager
		mSessionManager = CastContext.getSharedInstance(this).getSessionManager();

		// Optional
		GoogleCastManager.setCastInfoProvider(new GoogleCastManager.CastInfoProvider() {
			@Override
			public void onSendCastRequest(@NonNull Bundle bundle, @NonNull JSONObject jsonObject) {
				// This will be called prior to sending a request.
				// You can read and change anything in the jsonObject param

				// Example: query for drm config
				try {
					JSONObject drm = jsonObject.getJSONObject("drm");
					Log.d(TAG, "drm object: " + drm);
					// Set drm url
					//drm.put("com.widevine.alpha", "http://my.drm.server/...");
				} catch (JSONException e) {
				}
				try {
					JSONObject drm = jsonObject.getJSONObject("drmtoday");
					Log.d(TAG, "drmtoday object: " + drm);
				} catch (JSONException e) {
				}
			}
		});
	}

	// Delegate the onStart event to the player views lifecycle delegate.
	// The delegate will make sure that the screen safer will be disabled and
	// the display will not go to sleep
	@Override
	protected void onStart() {
		super.onStart();
		playerView.getLifecycleDelegate().start(this);
	}

	// Delegate the onResume event to the player views lifecycle delegate.
	// The delegate ensures that the player recovers from a saved state. This needs to
	// be implemented to ensure the the user can for example go to the home screen and
	// come back to this activity.
	@Override
	protected void onResume() {
		super.onResume();

		final PlayerControllerView controls = (PlayerControllerView) findViewById(R.id.player_controls);
		controls.bind(playerView);

		PlayerControllerProgressBar progressBar = (PlayerControllerProgressBar) findViewById(R.id.progress_bar);
		progressBar.bind(playerView.getPlayerController());

		playerView.getLifecycleDelegate().resume();

		// Add cast listener
		mSessionManager.addSessionManagerListener(mSessionManagerListener);
	}

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

		// Remove cast listener
		mSessionManager.removeSessionManagerListener(mSessionManagerListener);
	}

	// Delegate the onStop event to the player views lifecycle delegate.
	// We release the player when the activity is stopped. This will release all the player
	// resources and save the current playback state. Saving the state is required so the
	// onResume callback can recover properly.
	@Override
	protected void onStop() {
		super.onStop();
		playerView.getLifecycleDelegate().releasePlayer(false);
	}

}
