package com.castlabs.sdk.demos;

import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.castlabs.android.player.ExternalSourceSelector;
import com.google.android.material.snackbar.Snackbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.Toast;

import com.castlabs.android.player.AbstractPlayerListener;
import com.castlabs.android.player.PlayerConfig;
import com.castlabs.android.player.PlayerController;
import com.castlabs.android.player.PlayerView;
import com.castlabs.android.player.TrickplayConfiguration;
import com.castlabs.android.player.exceptions.CastlabsPlayerException;
import com.castlabs.android.views.SubtitlesViewComponent;
import com.castlabs.sdk.playerui.PlayerControllerProgressBar;
import com.castlabs.sdk.playerui.PlayerControllerView;

public class TrickplaySettingsPlaybackDemo extends Activity {
	private static final String TAG = "SimplePlaybackDemo";
	private static final String SAVED_PLAYBACK_STATE_BUNDLE_KEY = "SAVED_PLAYBACK_STATE_BUNDLE_KEY";
	// This is the player view that we use to start playback
	private PlayerView playerView;
	private PlayerControllerView playerControllerView;
	// This is an example implementation that demonstrates how you can
	// listen to changes on the player controller and how you can access the
	// SubtitleViewComponent to apply adjustments.
	//
	// In this example we add some padding to move the subtitles view up a bit when
	// we are showing the controller.
	private PlayerControllerView.Listener playerControllerViewListener = new PlayerControllerView.Listener() {
		@Override
		public void onVisibilityChanged(int visibility) {
			// This is how you can get a component, in this case the SubtitlesViewComponent
			// from the player view.
			SubtitlesViewComponent svc = playerView.getComponent(SubtitlesViewComponent.class);
			if (svc != null && svc.getView() != null) {
				int height = visibility == View.VISIBLE ? playerControllerView.getHeight() : 0;
				svc.getView().setPadding(0, 0, 0, height);
			}
		}
	};

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

		findViewById(R.id.button_play).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				playerView.getPlayerController().play();
			}
		});

		findViewById(R.id.button_pause).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				playerView.getPlayerController().pause();
			}
		});

		((Switch) findViewById(R.id.switch_prefer_trickplay)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				PlayerController pc = playerView.getPlayerController();
				pc.setTrickplayConfiguration(new TrickplayConfiguration.Builder(pc.getTrickplayConfiguration())
						.preferTrickPlayTracks(isChecked)
					.get());
				Log.d(TAG, "Setting preferTrickPlayTracks = " + isChecked);
			}
		});

		findViewById(R.id.button_decoder_mode).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				final float speed = playerView.getPlayerController().getSpeed();
				if (speed <= 0) {
					Toast.makeText(TrickplaySettingsPlaybackDemo.this,
							"Cannot play at speed < 0 in Decoder mode!", Toast.LENGTH_SHORT).show();
				} else {
					PlayerController pc = playerView.getPlayerController();
					pc.setTrickplayConfiguration(new TrickplayConfiguration.Builder(pc.getTrickplayConfiguration())
						.speedupMode(TrickplayConfiguration.SpeedupMode.DECODER)
					.get());
				}
			}
		});

		findViewById(R.id.button_seek_mode).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				PlayerController pc = playerView.getPlayerController();
				pc.setTrickplayConfiguration(new TrickplayConfiguration.Builder(pc.getTrickplayConfiguration())
						.speedupMode(TrickplayConfiguration.SpeedupMode.SEEK)
					.get());
			}
		});

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

		Bundle playbackBundle = null;
		if (savedInstanceState == null) {
			Log.d(TAG, "Opening playback from intent bundle");
			// This demo assumes that you send an intent to this Activity that contains the
			// playback information.
			if (getIntent() != null) {
				playbackBundle = getIntent().getExtras();
			} else {
				Snackbar.make(playerView, "No intent specified", Snackbar.LENGTH_INDEFINITE).show();
			}
		} else {
			Log.d(TAG, "Opening playback from saved state bundle");
			playbackBundle = savedInstanceState.getBundle(SAVED_PLAYBACK_STATE_BUNDLE_KEY);
		}

		if (playbackBundle != null) {
			try {
				// Subscribe for the playback errors
				playerView.getPlayerController().addPlayerListener(new AbstractPlayerListener() {
					@Override
					public void onError(@NonNull CastlabsPlayerException error) {
						Snackbar.make(playerView, "Error while opening player: " + error.getMessage(),
								Snackbar.LENGTH_SHORT).show();
					}
				});
				// (Optional) Install the CDN fallback implementation
				playerView.getPlayerController().setExternalSourceSelector(new ExternalSourceSelector() {
					@Nullable
					@Override
					public SourceData onError(@NonNull String manifestUrl, @NonNull Exception error) {
						// Have the fallback CDN only for one particular URL
						if (manifestUrl.equals("https://demo.cf.castlabs.com/media/QA/QA_BBB_single_4/Manifest.mpd")) {
							return new SourceData("https://demo.cf.castlabs.com/media/QA/QA_BBB_single_2/Manifest.mpd");
						}
						// No fallback CDN for other URLs
						return null;
					}

					@Nullable
					@Override
					public PlayerConfig onRestart(@NonNull String manifestUrl, @NonNull PlayerConfig playerConfig) {
						// Do the 'hard' restart for the selected URL when the 'soft' one fails
						if (manifestUrl.equals("https://demo.cf.castlabs.com/media/QA/QA_BBB_single_4/Manifest.mpd")) {
							// Simplify here and just update the currently failed player config with the fallback CDN URL
							// Usually, ensure that the DRM and other player config parameters are valid for the
							// returned player config or create a completely new player config
							return new PlayerConfig.Builder(playerConfig)
									.contentUrl("https://demo.cf.castlabs.com/media/QA/QA_BBB_single_2/Manifest.mpd")
								.get();
						}
						// No fallback CDN for other URLs
						return null;
					}
				});

				// Need to pass the 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.
				playerView.getPlayerController().open(playbackBundle);
			} 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, "Can not start playback: no bundle specified", Snackbar.LENGTH_INDEFINITE).show();
		}
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		boolean handled = false;
		if (playerControllerView != null && playerControllerView.getVisibility() == View.VISIBLE) {
			handled = playerControllerView.dispatchKeyEvent(event);
		} else { // Not visible
			if (event.getAction() == KeyEvent.ACTION_DOWN){
				handled = playerControllerView.dispatchKeyEvent(event);
			}
		}
		return handled || super.dispatchKeyEvent(event);
	}

	// 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();
		// Bind the controller view and its listener
		playerControllerView.bind(playerView);
		playerControllerView.addListener(playerControllerViewListener);

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

		playerView.getLifecycleDelegate().resume();
	}

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

		// Unbind the player controller view and remove the listener
		playerControllerView.unbind();
		playerControllerView.removeListener(playerControllerViewListener);

		playerView.getLifecycleDelegate().releasePlayer(false);
	}

	// Save the playback state when the activity is destroyed in order to correctly
	// resume after the activity is re-created again i.e. onCreate is called
	@Override
	public void onSaveInstanceState(Bundle outState) {
		Bundle savedStateBundle = new Bundle();
		PlayerConfig playerConfig = playerView.getPlayerController().getPlayerConfig();
		if (playerConfig != null) {
			playerView.getPlayerController().getPlayerConfig().save(savedStateBundle);
			outState.putBundle(SAVED_PLAYBACK_STATE_BUNDLE_KEY, savedStateBundle);
		}
		super.onSaveInstanceState(outState);
	}
}
