/* 
 * Copyright 2012 by AVM GmbH <info@avm.de>
 *
 * This software contains free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License ("License") as 
 * published by the Free Software Foundation  (version 3 of the License). 
 * This software is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copy of the 
 * License you received along with this software for more details.
 */

package de.avm.android.fritzapp.gui;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Locale;

import de.avm.android.fritzapp.R;
import de.avm.android.fritzapp.com.ComSettingsChecker;
import de.avm.android.fritzapp.com.discovery.BoxInfo;
import de.avm.android.fritzapp.com.discovery.BoxInfoList;
import de.avm.android.fritzapp.service.BoxService;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

/* Invisible activity to show dialogs */
public class DialogActivity extends Activity
{
	private static final int DIALOG_PASSWORD = 1;
	private static final int DIALOG_MESSAGE = 2;
	private static final int DIALOG_UNIQUEMESSAGE = 3;
	private static final int DIALOG_CERTIFICATE = 4;

	private static final String EXTRA_DIALOG = "de.avm.android.fritzapp.gui.DialogActivity.DIALOG";
	private static final String EXTRA_TITLE = "de.avm.android.fritzapp.gui.DialogActivity.TITLE";
	private static final String EXTRA_MESSAGE = "de.avm.android.fritzapp.gui.DialogActivity.TEXT";
	private static final String EXTRA_ICON = "de.avm.android.fritzapp.gui.DialogActivity.ICON";
	private static final String EXTRA_WARNING = "de.avm.android.fritzapp.gui.DialogActivity.WARNING";
	private static final String EXTRA_UDN = "de.avm.android.fritzapp.gui.DialogActivity.UDN";
	private static final String EXTRA_CERTIFICATE_FMT = "de.avm.android.fritzapp.gui.DialogActivity.CERTIFICATE-%d";
	private static final String EXTRA_ANONYMOUS_LOGIN = "de.avm.android.fritzapp.gui.DialogActivity.ANONYMOUS_LOGIN";
	private static final String SAVED_INTENTS = "intents";
	
	private ArrayList<Intent> mIntents = new ArrayList<Intent>();
	private Handler mHandler = new Handler();

	/**
	 * Starts activity to show a password dialog
	 * 
	 * @param context
	 * 			an activity's context
	 * @param udn
	 * 			box' UDN
	 * @param warning
	 * 			additional warning to show
	 * @param anonymousLogin
	 * 			true if SSO-Username not needed for authentication
	 */
	public static void startPasswordDialog(Context context, String udn, String warning,
			boolean anonymousLogin)
	{
		Intent intent = new Intent(context, DialogActivity.class);
		if (Activity.class.isAssignableFrom(context.getClass()))
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP);
		else
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.putExtra(EXTRA_DIALOG, DIALOG_PASSWORD)
				.putExtra(EXTRA_UDN, udn)
				.putExtra(EXTRA_ANONYMOUS_LOGIN, anonymousLogin);
		if (!TextUtils.isEmpty(warning))
			intent.putExtra(EXTRA_WARNING, warning); // additional warning
		
		context.startActivity(intent);
	}
	
	/**
	 * Starts activity to show a password dialog
	 * 
	 * @param context
	 * 			an activity's context
	 * @param udn
	 * 			box' UDN
	 * @param chain
	 * 			certificate chain to be trusted or rejected
	 */
	public static void startCertificateDialog(Context context,
			String udn, X509Certificate[] chain)
	{
		Intent intent = new Intent(context, DialogActivity.class);
		if (Activity.class.isAssignableFrom(context.getClass()))
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP);
		else
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

		intent.putExtra(EXTRA_DIALOG, DIALOG_CERTIFICATE)
				.putExtra(EXTRA_UDN, udn);
		if (chain != null) addToExtras(intent, chain);
		
		context.startActivity(intent);
	}
	
	/**
	 * Starts activity to show a message dialog
	 * 
	 * @param context
	 * 			an activity's context
	 * @param text
	 * 			the message text
	 * @param iconId
	 * 			the title bar icon
	 * @param unique
	 * 			if true, after showing this, all other enqueued
	 * 			messages with this flag set are removed from queue 
	 */
	public static void startMessageDialog(Context context,
			String text, int iconId, boolean unique)
	{
		Intent intent = new Intent(context, DialogActivity.class);
		if (Activity.class.isAssignableFrom(context.getClass()))
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP);
		else
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.putExtra(EXTRA_DIALOG,
				(unique) ? DIALOG_UNIQUEMESSAGE : DIALOG_MESSAGE);
		intent.putExtra(EXTRA_MESSAGE, text);
		intent.putExtra(EXTRA_ICON, iconId);
		context.startActivity(intent);
	}
	
	/**
	 * Starts activity to show a message dialog
	 * 
	 * @param context
	 * 			an activity's context
	 * @param title
	 * 			the title
	 * @param text
	 * 			the message text
	 * @param iconId
	 * 			the title bar icon
	 * @param unique
	 * 			if true, after showing this, all other enqueued
	 * 			messages with this flag set are removed from queue 
	 */
	public static void startMessageDialog(Context context,
			String title, String text, int iconId, boolean unique)
	{
		Intent intent = new Intent(context, DialogActivity.class);
		if (Activity.class.isAssignableFrom(context.getClass()))
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP);
		else
			intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP |
					Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
		intent.putExtra(EXTRA_DIALOG,
				(unique) ? DIALOG_UNIQUEMESSAGE : DIALOG_MESSAGE);
		intent.putExtra(EXTRA_TITLE, title);
		intent.putExtra(EXTRA_MESSAGE, text);
		intent.putExtra(EXTRA_ICON, iconId);
		context.startActivity(intent);
	}
	
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		if (savedInstanceState == null)
		{
			// current intent
			Intent intent = getIntent(); 
			mIntents.add(intent);
			showDialog(intent.getIntExtra(EXTRA_DIALOG, 0));
		}
		else
		{
			// restore intent list
			ArrayList<Intent> list = savedInstanceState
					.getParcelableArrayList(SAVED_INTENTS);
			if (list != null) mIntents.addAll(list);
			
			// set current intent 
			if (mIntents.isEmpty())
				finish(); // nothing to show
			else
				setIntent(mIntents.get(0));
		}
	}
	
	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		outState.putParcelableArrayList(SAVED_INTENTS, mIntents);
	}
	
	@Override
	protected void onNewIntent(Intent intent)
	{
		mIntents.add(intent);
	}

	private void next(final int lastId)
	{
		// delay it a bit to let the current dialog be dismissed
		mHandler.post(new Runnable()
		{
			public void run()
			{
				if (!mIntents.isEmpty()) mIntents.remove(0); // already done
				switch (lastId)
				{
					case DIALOG_PASSWORD:
					case DIALOG_CERTIFICATE:
					case DIALOG_UNIQUEMESSAGE:
						// don't show again
						for (int ii = mIntents.size() - 1; ii >= 0; ii--)
							if (mIntents.get(ii).getIntExtra(EXTRA_DIALOG, 0) ==
								lastId)
								mIntents.remove(ii);
						break;
				}
				
				if (!mIntents.isEmpty())
				{
					Intent intent = mIntents.get(0); 
					setIntent(intent);
					showDialog(intent.getIntExtra(EXTRA_DIALOG, 0));
				}
				else finish(); // done all
			}
		});
	}
	
	@Override
	protected Dialog onCreateDialog(int id)
	{
		super.onCreateDialog(id);
		
		switch (id)
		{
			case DIALOG_PASSWORD:
			{
			    LayoutInflater inflater = (LayoutInflater)getApplicationContext()
						.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				View layout = inflater.inflate(R.layout.password_dialog, null);
				final EditText user = (EditText)layout.findViewById(R.id.User);
				final EditText password = (EditText)layout.findViewById(R.id.Password);
				
				return new AlertDialog.Builder(this)
						.setTitle(R.string.pref_address)
						.setIcon(TextDialog.DEFAULT_EDIT_ICON)
						.setCancelable(false)
						.setInverseBackgroundForced(true)
						.setView(layout)
						.setPositiveButton(R.string.ok,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialog, int which)
							{
								Intent intent = getIntent();
								BoxInfoList boxes = null;
								if (ComSettingsChecker.getBoxFinder().isInitialized())
									boxes = ComSettingsChecker.getBoxFinder().getBoxes();
								BoxInfo boxInfo = (boxes == null) ? null :
										boxes.get(intent.getStringExtra(EXTRA_UDN), true);

								if (boxInfo != null)
								{
									if (intent.getBooleanExtra(EXTRA_ANONYMOUS_LOGIN, false))
										boxInfo.setBoxPassword(password.getText().toString());
									else
										boxInfo.setBoxPassword(user.getText().toString(),
												password.getText().toString());
									boxes.save(getApplicationContext());
									Intent serviceIntent = new Intent(DialogActivity.this,
											BoxService.class);
									serviceIntent.putExtra(BoxService.EXTRA_COMMAND,
											BoxService.Command.RECONNECT.ordinal());
									startService(serviceIntent);
								}
								dialog.dismiss();
								next(DIALOG_PASSWORD);
							}
						})
						.setNegativeButton(R.string.cancel,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialog, int which)
							{
								BoxInfoList boxes = null;
								if (ComSettingsChecker.getBoxFinder().isInitialized())
									boxes = ComSettingsChecker.getBoxFinder().getBoxes();
								BoxInfo boxInfo = (boxes == null) ? null :
										boxes.get(getIntent().getStringExtra(EXTRA_UDN), true);

								if (boxInfo != null)
								{
									boxInfo.setIsAutoconnect(false); 
									boxes.save(getApplicationContext());
									Intent serviceIntent = new Intent(DialogActivity.this,
											BoxService.class);
									serviceIntent.putExtra(BoxService.EXTRA_COMMAND,
											BoxService.Command.RECONNECT.ordinal());
									startService(serviceIntent);
								}
								dialog.dismiss();
								next(DIALOG_PASSWORD);
							}
						}).create();
			}

			case DIALOG_CERTIFICATE:
			{
			    LayoutInflater inflater = (LayoutInflater)getApplicationContext()
						.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				View layout = inflater.inflate(R.layout.certificate_dialog, null);
				
				return new AlertDialog.Builder(this)
						.setTitle(R.string.pref_address)
						.setIcon(TextDialog.DEFAULT_EDIT_ICON)
						.setCancelable(false)
						.setInverseBackgroundForced(true)
						.setView(layout)
						.setPositiveButton(R.string.yes,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialog, int which)
							{
								if (ComSettingsChecker.getBoxFinder().isInitialized())
								{
									BoxInfoList boxes = ComSettingsChecker.getBoxFinder().getBoxes();
									if (boxes != null)
									{
										Intent intent = getIntent();
										BoxInfo boxInfo = boxes.get(intent
												.getStringExtra(EXTRA_UDN), true);
										if (boxInfo != null)
										{
											String name = String.format(Locale.US,
													EXTRA_CERTIFICATE_FMT, 0);
											if (intent.hasExtra(name))
											{
												boxes.setCertificate(boxInfo, (X509Certificate)intent
														.getSerializableExtra(name));
												boxes.save(getApplicationContext());
												Intent serviceIntent = new Intent(DialogActivity.this,
														BoxService.class);
												serviceIntent.putExtra(BoxService.EXTRA_COMMAND,
														BoxService.Command.RECONNECT.ordinal());
												startService(serviceIntent);
											}
										}
									}
								}
								dialog.dismiss();
								next(DIALOG_PASSWORD);
							}
						})
						.setNegativeButton(R.string.no,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialog, int which)
							{
								BoxInfoList boxes = null;
								if (ComSettingsChecker.getBoxFinder().isInitialized())
									boxes = ComSettingsChecker.getBoxFinder().getBoxes();
								BoxInfo boxInfo = (boxes == null) ? null :
										boxes.get(getIntent().getStringExtra(EXTRA_UDN), true);

								if (boxInfo != null)
								{
									boxInfo.setIsAutoconnect(false); 
									boxes.save(getApplicationContext());
									Intent serviceIntent = new Intent(DialogActivity.this,
											BoxService.class);
									serviceIntent.putExtra(BoxService.EXTRA_COMMAND,
											BoxService.Command.RECONNECT.ordinal());
									startService(serviceIntent);
								}
								dialog.dismiss();
								next(DIALOG_PASSWORD);
							}
						}).create();
			}
			
			case DIALOG_MESSAGE:
			case DIALOG_UNIQUEMESSAGE:
			{
				Intent intent = getIntent();
				String title = intent.getStringExtra(EXTRA_TITLE);
				if (title == null) title = TextDialog.getDefaultTitle(this);
				final int dialogId = id;
				return TextDialog.create(this, title,
						intent.getStringExtra(EXTRA_MESSAGE),
						intent.getIntExtra(EXTRA_ICON, TextDialog.DEFAULT_MESSAGE_ICON))
						.setPositiveButton(R.string.ok,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialog, int which)
							{
								dialog.dismiss();
								next(dialogId);
							}
						})
						.create();
			}
		}
		return null;
	}
	
	@Override
	protected void onPrepareDialog(int id, Dialog dialog)
	{
		super.onPrepareDialog(id, dialog);
		
		switch (id)
		{
			case DIALOG_PASSWORD:
			{
				Intent intent = getIntent();
				((EditText)dialog.findViewById(R.id.Password)).setText("");
				TextView userLabelView = (TextView)dialog.findViewById(R.id.UserLabel);
				EditText userView = (EditText)dialog.findViewById(R.id.User);
				if (intent.getBooleanExtra(EXTRA_ANONYMOUS_LOGIN, false))
				{
					userLabelView.setVisibility(View.GONE);
					userView.setVisibility(View.GONE);
					((TextView)dialog.findViewById(R.id.PasswordLabel))
							.setText(R.string.password_box);
				}
				else
				{
					userLabelView.setVisibility(View.VISIBLE);
					userView.setVisibility(View.VISIBLE);

					// show recently used username if any
					BoxInfoList boxes = null;
					if (ComSettingsChecker.getBoxFinder().isInitialized())
						boxes = ComSettingsChecker.getBoxFinder().getBoxes();
					BoxInfo boxInfo = (boxes == null) ? null :
							boxes.get(intent.getStringExtra(EXTRA_UDN), false);
					if ((boxInfo != null) && !TextUtils.isEmpty(boxInfo.getBoxUsername()))
					{
						userView.setText(boxInfo.getBoxUsername());
						userView.selectAll();
					}
					else userView.setText("");
				}
				TextView warningView = (TextView)dialog.findViewById(R.id.Warning);
				String warning = intent.getStringExtra(EXTRA_WARNING);
				if (!TextUtils.isEmpty(warning))
				{
					warningView.setVisibility(View.VISIBLE);
					warningView.setText(warning);
				}
				else warningView.setVisibility(View.GONE);
				break;
			}

			case DIALOG_CERTIFICATE:
			{
				Intent intent = getIntent();
				BoxInfoList boxes = null;
				if (ComSettingsChecker.getBoxFinder().isInitialized())
					boxes = ComSettingsChecker.getBoxFinder().getBoxes();
				BoxInfo boxInfo = (boxes == null) ? null :
						boxes.get(intent.getStringExtra(EXTRA_UDN), false);

				TextView messageView = (TextView)dialog.findViewById(R.id.message);
				X509Certificate[] certificates = getFromExtras(intent);
				if ((certificates != null) && (boxInfo != null) &&
						!boxes.hasCertificate(boxInfo))
					messageView.setText(R.string.certificate_new);
				else
					messageView.setText(R.string.certificate_changed);

				if (certificates != null)
					((TextView)dialog.findViewById(R.id.Details)).setText(
							getDetailsDisplay(certificates));
				break;
			}
				
			case DIALOG_MESSAGE:
			case DIALOG_UNIQUEMESSAGE:
			{
				if (dialog.getClass().equals(AlertDialog.class))
				{
					Intent intent = getIntent();
					String title = intent.getStringExtra(EXTRA_TITLE);
					if (title == null) title = TextDialog.getDefaultTitle(this);
					dialog.setTitle(title);
					((AlertDialog)dialog).setIcon(intent.getIntExtra(EXTRA_ICON,
							TextDialog.DEFAULT_MESSAGE_ICON));
					TextDialog.getMessageView((AlertDialog)dialog)
							.setText(intent.getStringExtra(EXTRA_MESSAGE));
				}
				break;
			}
		}
	}
	
	public static String getDetailsDisplay(X509Certificate[] chain)
	{
		// TODO getDetailsDisplay
		StringBuilder result = new StringBuilder();
		for (X509Certificate certificate : chain)
		{
			if (result.length() != 0) result.append("\n====================\n");
			result.append(certificate.toString());
		}
		return result.toString();
	}
	
	public static void addToExtras(Intent intent, X509Certificate[] chain)
	{
		for (int ii = 0; ii < chain.length; ii++)
			intent.putExtra(String.format(Locale.US, EXTRA_CERTIFICATE_FMT, ii),
					chain[ii]);
	}
	
	public static X509Certificate[] getFromExtras(Intent intent)
	{
		ArrayList<X509Certificate> certificates = new ArrayList<X509Certificate>(); 
		for (int ii = 0; ; ii++)
		{
			String name = String.format(Locale.US, EXTRA_CERTIFICATE_FMT, ii);
			if (intent.hasExtra(name))
				certificates.add(ii, (X509Certificate)intent
						.getSerializableExtra(name));
			else
				break;
		}

		if (certificates.size() > 0)
			return certificates.toArray(new X509Certificate[0]);
		return null;
	}
}
