package com.castlabs.sdk.subtitle_styles;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Toast;

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.googlecast.RemotePlayerState;
import com.castlabs.logutils.Log;
import com.google.android.gms.cast.AdBreakInfo;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.framework.CastButtonFactory;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.cast.framework.CastSession;
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.gms.cast.framework.media.widget.ExpandedControllerActivity;

import org.apache.commons.text.StringEscapeUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.List;

/**
 * In addition to the default logic of the {@link ExpandedControlsActivity} class, this activity sets
 * up three custom buttons and controls user interaction.
 * <p>
 * When an audio, video quality or subtitle track are selected, the {@link GoogleCastManager}
 * will be requested to update the remote player with the user choice.
 */
public class ExpandedControlsActivity extends ExpandedControllerActivity {

	public static final String INTENT_RESULT_REMOTE_PLAYER_POSITION = "INTENT_RESULT_REMOTE_PLAYER_POSITION";
	public static final String INTENT_RESULT_REMOTE_PLAYER_SUBTITLE_LANGUAGE = "INTENT_RESULT_REMOTE_PLAYER_SUBTITLE_LANGUAGE";
	public static final String INTENT_RESULT_REMOTE_PLAYER_AUDIO_LANGUAGE = "INTENT_RESULT_REMOTE_PLAYER_AUDIO_LANGUAGE";
	public static final String INTENT_RESULT_REMOTE_PLAYER_VIDEO_QUALITY_BITRATE = "INTENT_RESULT_REMOTE_PLAYER_VIDEO_QUALITY_BITRATE";
	public static final String INTENT_RESULT_REMOTE_PLAYER_SUBTITLES_STYLE = "INTENT_RESULT_REMOTE_PLAYER_SUBTITLES_STYLE";

	private static final String TAG = "ExpandedControlsActivity";
	private final SessionManagerListener sessionManagerListener = new SessionManagerListenerImpl();

	private SessionManager sessionManager;
	private RemotePlayerState playerState;
	private ImageView videoQualitiesButton;
	private ImageView audioButton;
	private ImageView subtitlesButton;
	private View.OnClickListener buttonListener;
	private ImageView subtitlesStyleButton;

	private JSONObject customData;

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

		@Override
		public void onSessionStarted(Session session, String sessionId) {
		}

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

		@Override
		public void onSessionEnding(Session session) {
			final RemoteMediaClient remoteMediaClient = ((CastSession) session).getRemoteMediaClient();
			if (remoteMediaClient != null) {
				Intent i = new Intent("RESULT_ACTION");

				// Get the remote media client position and forward it back to our local player activity
				// there we can sync the local player to play from the last position of the remote player
				i.putExtra(INTENT_RESULT_REMOTE_PLAYER_POSITION, remoteMediaClient.getApproximateStreamPosition());

				// We also get Subtitle, Audio and Video quality tracks
				final RemotePlayerState remoteState = GoogleCastManager.getCurrentPlayerState(sessionManager);

				if (remoteState != null) {
					if (remoteState.getSubtitleTrack() != null) {
						i.putExtra(INTENT_RESULT_REMOTE_PLAYER_SUBTITLE_LANGUAGE, remoteState.getSubtitleTrack().getLanguage());
					}

					if (remoteState.getVideoQuality() != null) {
						i.putExtra(INTENT_RESULT_REMOTE_PLAYER_VIDEO_QUALITY_BITRATE, remoteState.getVideoQuality().getBitrate());
					}

					if (remoteState.getAudioTrack() != null) {
						i.putExtra(INTENT_RESULT_REMOTE_PLAYER_AUDIO_LANGUAGE, remoteState.getAudioTrack().getLanguage());
					}
				}

				// Save remote subtitle style to SharedPreferences for next cast session
				SubtitlesStyle remoteStyle = GoogleCastManager.getSubtitleStyle(sessionManager);
				if (remoteStyle != null) {
					// Extract font family from typeface (best effort)
					String fontFamily = null;
					if (remoteStyle.typeface != null) {
						// Note: Cannot reliably extract font family from Typeface, use default
						fontFamily = "sans-serif";
					}
					saveRemoteStyleToPreferences(remoteStyle, fontFamily);
				}

				setResult(RESULT_OK, i);
			}
		}

		@Override
		public void onSessionResumed(Session session, boolean wasSuspended) {
		}

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

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

		@Override
		public void onSessionEnded(Session session, int error) {
		}

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

	@Override
	protected void onCreate(Bundle bundle) {
		super.onCreate(bundle);

		// Save session manager
		sessionManager = CastContext.getSharedInstance(this).getSessionManager();
		final RemoteMediaClient remoteMediaClient = sessionManager.getCurrentCastSession().getRemoteMediaClient();

		try {
			sessionManager.getCurrentCastSession().setMessageReceivedCallbacks("urn:x-cast:castlabs", new Cast.MessageReceivedCallback() {
				@Override
				public void onMessageReceived(CastDevice castDevice, String namespace, String message) {
					if (message == null) {
						return;
					}

					try {
						// Remove quotes if any
						if (message.startsWith("\"") && message.endsWith("\"")) {
							message = message.substring(1, message.length()-1);
						}
						// Unescape java string if necessary
						message = StringEscapeUtils.unescapeJava(message);
						// Parse to JSON object
						customData = new JSONObject(message);
					} catch (JSONException e) {
						// NOTE: Current receiver sometimes produces malformed JSON files. The issue
						//       is under investigation.
						//       Logging of those errors will be disabled to not confuse during tests.
						// TODO: Re-enable error logging when fixed on the receiver-side
						//Log.e(TAG, "Error parsing customData", e);
					}
				}
			});
		} catch (IOException e) {
			e.printStackTrace();
		}

		remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() {
			@Override
			public void onStatusUpdated() {
				sessionManager.getCurrentCastSession().getRemoteMediaClient().setParseAdsInfoCallback(new RemoteMediaClient.ParseAdsInfoCallback() {
					@Override
					public boolean parseIsPlayingAdFromMediaStatus(MediaStatus mediaStatus) {
						if (customData == null) {
							return false;
						}

						final JSONObject chromecast = customData.optJSONObject("chromecast");
						if (chromecast == null) {
							Log.i(TAG, "Error parsing customData. No 'chromecast' key");
							return false;
						}
						final JSONObject adsInfo = chromecast.optJSONObject("adsInfo");
						if (adsInfo == null) {
							Log.i(TAG, "Error parsing customData. No 'adsInfo' key");
							return false;
						}
						if (adsInfo.has("adsPlaying")) {
							return adsInfo.optBoolean("adsPlaying");
						} else {
							Log.i(TAG, "Error parsing customData. No 'adsPlaying' key");
							return false;
						}
					}

					@Override
					public List<AdBreakInfo> parseAdBreaksFromMediaStatus(MediaStatus mediaStatus) {
						return null;
					}
				});
			}
		});
	}

	@Override
	protected void onResume() {
		super.onResume();
		sessionManager.addSessionManagerListener(sessionManagerListener);
	}

	@Override
	protected void onPause() {
		super.onPause();
		sessionManager.removeSessionManagerListener(sessionManagerListener);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);

		// Custom buttons, defined in arrays.xml
		videoQualitiesButton = getButtonImageViewAt(0);
		audioButton = getButtonImageViewAt(1);
		subtitlesButton = getButtonImageViewAt(2);
		subtitlesStyleButton = getButtonImageViewAt(3);

		buttonListener = new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				playerState = GoogleCastManager.getCurrentPlayerState(sessionManager);

				if (v == videoQualitiesButton) {
					showQualitiesDialog();
				} else if (v == audioButton) {
					showAudioDialog();
				} else if (v == subtitlesButton) {
					showSubtitleDialog();
				} else if (v == subtitlesStyleButton) {
					showSubtitleStyleDialog();
				}
			}
		};

		setUpUIButton(videoQualitiesButton, R.drawable.cl_ic_settings_black_24px, buttonListener);
		setUpUIButton(audioButton, R.drawable.cl_ic_tracks_audio_black_24dp, buttonListener);
		setUpUIButton(subtitlesButton, R.drawable.cl_ic_tracks_subtitle_black_24dp, buttonListener);
		setUpUIButton(subtitlesStyleButton, R.drawable.cl_ic_crop_black_24dp, buttonListener);

		// Menu and cast button
		getMenuInflater().inflate(R.menu.menu_expanded_controller, menu);
		CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
		return true;
	}

	private void setUpUIButton(ImageView button, int resource, View.OnClickListener listener) {
		button.setBackgroundColor(Color.TRANSPARENT);
		button.setImageResource(resource);
		button.setColorFilter(Color.WHITE);
		button.setOnClickListener(listener);

		// Bind button to UIController
		getUIMediaController().bindViewToUIController(button, new CustomUIController(button));
	}

	private void showSubtitleStyleDialog() {
		SubtitleStyleDialog dialog = new SubtitleStyleDialog(this, GoogleCastManager.getSubtitleStyle(sessionManager));

		dialog.setOnDialogSubmitListener(new SubtitleStyleDialog.OnDialogSubmitListener() {
			@Override
			public void onDialogSubmit(SubtitleStyleDialog dialog) {
				SubtitlesStyle.Builder subtitlesBuilder = SubtitlesStyle.Builder.create(ExpandedControlsActivity.this, false);
				subtitlesBuilder.backgroundColor(dialog.getBackgroundColor(), true)
						.foregroundColor(dialog.getForegroundColor(), true)
						.edgeColor(dialog.getEdgeColor(), true)
						.windowColor(dialog.getWindowColor(), true)
						.edgeType(dialog.getEdgeType(), true)
						.fontScale(dialog.getFontScale());

				String fontFamily = dialog.getFontFamily();
				SubtitlesStyle newRemoteStyle = subtitlesBuilder.get();

				// Apply to remote player
				GoogleCastManager.setSubtitleStyle(sessionManager, newRemoteStyle, fontFamily);

				// Save to SharedPreferences for next cast session
				saveRemoteStyleToPreferences(newRemoteStyle, fontFamily);

				dialog.dismiss();
			}
		});

		dialog.show();
	}

	private void showAudioDialog() {
		final ArrayAdapter<AudioTrack> subtitleAdapter = new ArrayAdapter<>(ExpandedControlsActivity.this, android.R.layout.select_dialog_singlechoice);
		int selectedAudio = playerState.getAudioTracks().indexOf(playerState.getAudioTrack());

		subtitleAdapter.addAll(playerState.getAudioTracks());

		AlertDialog.Builder alertBuilder = new AlertDialog.Builder(ExpandedControlsActivity.this);
		alertBuilder.setCancelable(true).setTitle("Audio selection");

		alertBuilder.setSingleChoiceItems(subtitleAdapter, selectedAudio, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				AudioTrack selectedTrack = subtitleAdapter.getItem(which);
				GoogleCastManager.selectAudioTrack(sessionManager, playerState, selectedTrack);
				Toast.makeText(ExpandedControlsActivity.this, "Selected audio: " + selectedTrack.getLanguage(), Toast.LENGTH_SHORT).show();
				dialog.dismiss();
			}
		});

		alertBuilder.show();
	}

	private void showQualitiesDialog() {
		final ArrayAdapter<VideoTrackQuality> subtitleAdapter = new ArrayAdapter<>(ExpandedControlsActivity.this, android.R.layout.select_dialog_singlechoice);
		int selectedQuality = playerState.getVideoQualities().indexOf(playerState.getVideoQuality());

		subtitleAdapter.addAll(playerState.getVideoQualities());

		AlertDialog.Builder alertBuilder = new AlertDialog.Builder(ExpandedControlsActivity.this);
		alertBuilder.setCancelable(true).setTitle("Quality selection");

		if (selectedQuality != -1) {
			alertBuilder.setNeutralButton("Adaptive Bitrate", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					GoogleCastManager.selectVideoQuality(sessionManager, playerState, null);
					Toast.makeText(ExpandedControlsActivity.this, "ABR enabled", Toast.LENGTH_SHORT).show();
				}
			});
		}

		alertBuilder.setSingleChoiceItems(subtitleAdapter, selectedQuality, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				VideoTrackQuality selectedTrack = subtitleAdapter.getItem(which);
				GoogleCastManager.selectVideoQuality(sessionManager, playerState, selectedTrack);
				Toast.makeText(ExpandedControlsActivity.this, "Selected video quality: " + selectedTrack.getBitrate(), Toast.LENGTH_SHORT).show();
				dialog.dismiss();
			}
		});

		alertBuilder.show();
	}

	private void showSubtitleDialog() {

		final ArrayAdapter<SubtitleTrack> subtitleAdapter = new ArrayAdapter<>(ExpandedControlsActivity.this, android.R.layout.select_dialog_singlechoice);
		int selectedSubtitle = playerState.getSubtitleTracks().indexOf(playerState.getSubtitleTrack());

		subtitleAdapter.addAll(playerState.getSubtitleTracks());

		AlertDialog.Builder alertBuilder = new AlertDialog.Builder(ExpandedControlsActivity.this);
		alertBuilder.setCancelable(true).setTitle("Subtitle selection");

		if (selectedSubtitle != -1) {
			alertBuilder.setNeutralButton("Disable subtitles", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					GoogleCastManager.selectSubtitleTrack(sessionManager, playerState, null);
					Toast.makeText(ExpandedControlsActivity.this, "Disabled subtitles", Toast.LENGTH_SHORT).show();
				}
			});
		}

		alertBuilder.setSingleChoiceItems(subtitleAdapter, selectedSubtitle, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				SubtitleTrack selectedTrack = subtitleAdapter.getItem(which);
				GoogleCastManager.selectSubtitleTrack(sessionManager, playerState, selectedTrack);
				Toast.makeText(ExpandedControlsActivity.this, "Selected subtitle language: " + selectedTrack.getLanguage(), Toast.LENGTH_SHORT).show();
				dialog.dismiss();
			}
		});

		alertBuilder.show();
	}

	/**
	 * Save remote subtitle style to SharedPreferences for next cast session
	 */
	private void saveRemoteStyleToPreferences(SubtitlesStyle style, String fontFamily) {
		if (style == null) {
			return;
		}

		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		SharedPreferences.Editor editor = prefs.edit();

		// Save colors with "remote_" prefix
		editor.putString("remote_" + getString(R.string.prefs_subtitle_foreground_color_key),
				String.format("#%08X", style.foregroundColor));
		editor.putString("remote_" + getString(R.string.prefs_subtitle_background_color_key),
				String.format("#%08X", style.backgroundColor));
		editor.putString("remote_" + getString(R.string.prefs_subtitle_edge_color_key),
				String.format("#%08X", style.edgeColor));
		editor.putString("remote_" + getString(R.string.prefs_subtitle_window_color_key),
				String.format("#%08X", style.windowColor));

		// Save edge type
		editor.putString("remote_" + getString(R.string.prefs_subtitle_edge_type_key),
				String.valueOf(style.edgeType));

		// Save font scale
		editor.putString("remote_" + getString(R.string.prefs_subtitle_font_scale_key),
				String.valueOf(style.fontScale));

		// Save font family if provided
		if (fontFamily != null && !fontFamily.isEmpty()) {
			// Map font family string to font index
			int fontIndex = 0; // default
			if (fontFamily.equalsIgnoreCase("serif")) {
				fontIndex = 1;
			} else if (fontFamily.equalsIgnoreCase("monospace")) {
				fontIndex = 2;
			} else if (fontFamily.equalsIgnoreCase("sans-serif") || fontFamily.equalsIgnoreCase("sans serif")) {
				fontIndex = 0;
			} else if (fontFamily.equalsIgnoreCase("cursive")) {
				fontIndex = 3;
			}
			editor.putString("remote_" + getString(R.string.prefs_subtitle_font_key),
					String.valueOf(fontIndex));
		}

		editor.apply();
	}
}
