package com.castlabs.sdk.subtitle_styles;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;

import com.castlabs.android.SdkConsts;
import com.castlabs.android.player.AbstractPlayerListener;
import com.castlabs.android.player.PlayerController;
import com.castlabs.android.player.models.AudioTrack;
import com.castlabs.android.player.models.SubtitleTrack;
import com.castlabs.android.player.models.VideoTrackQuality;
import com.castlabs.sdk.subtitles_styles.R;
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 com.google.android.material.snackbar.Snackbar;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.castlabs.android.player.PlayerView;

import androidx.fragment.app.FragmentActivity;
import androidx.mediarouter.app.MediaRouteButton;

public class SimplePlaybackDemo extends FragmentActivity {
	private static final String TAG = "SimplePlaybackDemo";
	// We use this main handler to post delayed messaged to hide the system
	// controls after 3 seconds
	private final Handler handler = new Handler();
	// This is the player view that we use to start playback
	private PlayerView playerView;
	private SharedPreferences preferences;

	// 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 PlayerControllerView controls;
	private PlayerControllerProgressBar progressBar;


	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
			SubtitlesStyle subtitlesStyle = playerController.getSubtitlesStyle();
			if (subtitlesStyle != null) {
				castBundle.putParcelable(GoogleCastManager.INTENT_SUBTITLES_STYLE, subtitlesStyle);
			}

			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);
		controls = (PlayerControllerView) findViewById(R.id.player_controls);
		progressBar = (PlayerControllerProgressBar) findViewById(R.id.progress_bar);

		// Hide the system toolbars
		//
		// We use the system UI flags to put this activity into a stable layout state
		// and hide the system toolbars. This ensures that we can use the full real estate
		// to show the video playback but avoid going into immersive fullscreen mode.
		//
		// With this setting, the system toolbars (the status bar and the navigation controls)
		// will become visible if the user interacts with the activity. In this demo, we
		// simply hide the controls again again after 3 seconds.
		/*hideSystemControls();
		getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(
				new View.OnSystemUiVisibilityChangeListener() {
					@Override
					public void onSystemUiVisibilityChange(int visibility) {
						if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
							// The navigation bar is no longer hidden, so we hide it
							// again in a few seconds
							delayedHideSystemControls();
						}
					}
				});*/

		// 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 {
				// The subtitle style is stored in the shared preferences of the
				// app. We need to read it from there
				preferences = PreferenceManager.getDefaultSharedPreferences(this);
				StyleBuilder styleBuilder = new StyleBuilder(this, preferences);
				playerView.getPlayerController().setSubtitlesStyle(styleBuilder.createSubtitleStyle());

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

	// 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();

		controls.bind(playerView);
		progressBar.bind(playerView.getPlayerController());
		playerView.getLifecycleDelegate().resume();

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

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

		controls.unbind();
		progressBar.unbind();

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

	// Utility method that hides the system controls and makes sure
	// the player view can use all available screen real estate.
	private void hideSystemControls() {
		final View decorView = getWindow().getDecorView();
		decorView.setSystemUiVisibility(
				View.SYSTEM_UI_FLAG_FULLSCREEN |
						View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
						View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
						View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
						View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
	}

	private void delayedHideSystemControls() {
		handler.postDelayed(new Runnable() {
			@Override
			public void run() {
				hideSystemControls();
			}
		}, 3000);
	}

}
