/* 
 * Copyright 2015 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.net.URI;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnCancelListener;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
import android.text.InputType;
import android.text.TextUtils;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import de.avm.android.fritzapp.R;
import de.avm.android.fritzapp.com.ComSettingsChecker;
import de.avm.android.fritzapp.com.ConnectionProblem;
import de.avm.android.fritzapp.com.discovery.BoxInfo;
import de.avm.android.fritzapp.com.discovery.BoxInfoList;
import de.avm.android.fritzapp.service.BoxServiceConnection;
import de.avm.android.fritzapp.service.ComErrorMessage;
import de.avm.android.fritzapp.service.IBoxService;
import de.avm.android.fritzapp.util.ResourceHelper;
import de.avm.fundamentals.logger.FileLog;

/* GUI for the settings of call route exceptions */
public class SettingsFritzBoxActivity extends PreferenceActivity
{
	private static final String TAG = "SettingsFritzBoxAct";
	
	// finish activity after user selected item, show only available boxes to select from
	public static final String EXTRA_AUTOFINISH = "de.avm.android.fritzapp.extra.AUTOFINISH";

	// suppress new search for fritzboxes with false (default is true)
	public static final String EXTRA_SEARCH = "de.avm.android.fritzapp.extra.SEARCH";

	private static final String SAVED_LOADING = "loading";
	private static final String SAVED_BLANK = "blank";
	private static final String SAVED_OPTIONALHOST = "optional";
	private static final String SAVED_ITEM_CLICKED_UDN = "udn";

	private static final int DIALOG_OPTIONALHOST = 1;
	private static final int DIALOG_ITEM_CLICKED = 2;
	
	private ListView mListView = null;
	private boolean mLoadOnResume = false;
	private boolean mInitiallyBlank = false;

	private boolean mLoading = false;
	private String mOptionalHost = "";		// manually added box' address
	private String mItemClickedUdn = null;
	private WaitDialog mWaitDialog = null;

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

		setContentView(R.layout.settings);
		mListView = (ListView)findViewById(android.R.id.list);
		mListView.setSelector(R.drawable.list_selector_background);
		
		// Title and description
		ResourceHelper.setTitle(this, R.string.pref_address);
    	TextView description = (TextView)findViewById(R.id.Description);
    	description.setText(R.string.pref_address_desc);
    	description.setVisibility(View.VISIBLE);

		if (savedInstanceState != null)
		{
			// re-created
			mLoadOnResume = savedInstanceState.getBoolean(SAVED_LOADING);
			mInitiallyBlank = savedInstanceState.getBoolean(SAVED_BLANK);
			mOptionalHost = savedInstanceState.getString(SAVED_OPTIONALHOST);
			mItemClickedUdn = savedInstanceState.getString(SAVED_ITEM_CLICKED_UDN);
		}
		else if (getIntent().getBooleanExtra(EXTRA_SEARCH, true))
		{
			// new search
			mLoadOnResume = true;
			mInitiallyBlank = true;
		}
    	
		update();
	}

	protected void onSaveInstanceState(Bundle outState)
	{
		outState.putBoolean(SAVED_LOADING, mLoading);
		outState.putString(SAVED_OPTIONALHOST, mOptionalHost);
		outState.putBoolean(SAVED_BLANK, mInitiallyBlank);
		outState.putString(SAVED_ITEM_CLICKED_UDN, mItemClickedUdn);
	}
	
	@Override
	protected void onPause()
	{
		super.onPause();
		mBoxServiceConnection.unbindService();
	}
	
	@Override
	protected void onResume()
	{
		super.onResume();

		if (!mBoxServiceConnection.bindService(getApplicationContext()))
			FileLog.w(TAG, "Failed to bind to BoxService.");
		onStatusChanged();
		if (mLoadOnResume)
		{
			mLoadOnResume = false;
			searchBoxes();
		}
	}	
	
	private BoxServiceConnection mBoxServiceConnection = new BoxServiceConnection()
	{
		public void onComStatusChanged()
		{
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					onStatusChanged();
				}
			});
		}
		
		public void onBoxesSearchDone(final Exception error)
		{
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					onBoxSearchDone(error);
				}
			});
		}

		public void onComError(final ComErrorMessage comError)
		{
			runOnUiThread(new Runnable()
			{
				public void run()
				{
					onError(comError);
				}
			});
		}
	};
	
	@Override
	protected void onDestroy ()
	{
		dismissWait();
		super.onDestroy();
	}

	/**
	 * Finishes activity if requested by caller
	 * 
	 * @return true if activity is finishing
	 */
	private boolean autoFinish()
	{
		Intent intent = getIntent();
		if (intent != null && intent.getBooleanExtra(EXTRA_AUTOFINISH, false))
		{
			finish();
			return true;
		}
		return false;
	}
	
	private void onError(ComErrorMessage comError)
	{
		if (comError.getProblem() == ConnectionProblem.FRITZBOX_PASSWORD)
		{
			String udn = comError.getUdn();
			if (!TextUtils.isEmpty(udn))
			{
				DialogActivity.startPasswordDialog(this, udn,
						comError.getAdditionalMessage(),
						comError.getHasAnonymousLogin());
				return;
			}
		}
//		else if (comError.getProblem() == ConnectionProblem.CERTIFICATE_ERROR)
//		{
//			IBoxService srv = mBoxServiceConnection.getService();
//			if (srv != null) srv.removeLastComError(comError);
//
//			String udn = comError.getUdn();
//			X509Certificate cert = comError.getCertificate();
//			if (!TextUtils.isEmpty(udn) && (cert != null))
//			{
//				DialogActivity.startCertificateDialog(this, udn, cert);
//				return;
//			}
//		}
	}
	
	/**
	 * Builds preference screen (actions and list)
	 */
	private void update()
	{
		PreferenceScreen root = getPreferenceManager()
				.createPreferenceScreen(this);
		
		// boxes list
		PreferenceCategory groupAvailable = new PreferenceCategory(this);
		groupAvailable.setTitle(getString(R.string.pref_cat_address));
		root.addPreference(groupAvailable);
		PreferenceCategory groupKnown = null;
		final boolean autoFinish = getIntent().getBooleanExtra(EXTRA_AUTOFINISH, false);
		if (!autoFinish)
		{
			groupKnown = new PreferenceCategory(this);
			groupKnown.setTitle(getString(R.string.pref_cat_address_others));
			root.addPreference(groupKnown);
		}
		BoxInfoList list = ComSettingsChecker.getBoxes();
		boolean hasKnown = false;
		synchronized(list)
		{
			for(BoxInfo info : list)
			{
				FritzboxListItem boxPref = new FritzboxListItem(this, info);
				boxPref.setOnPreferenceClickListener(new OnPreferenceClickListener()
				{
					public boolean onPreferenceClick(Preference preference)
					{
						if (!autoFinish)
						{
							FritzboxListItem item = (FritzboxListItem)preference;
							item.setChecked(!item.isChecked()); // restore, we will do this later
							mItemClickedUdn = item.getUdn();
							showDialog(DIALOG_ITEM_CLICKED);
						}
						else onConnect(((FritzboxListItem)preference).getUdn()); 
						return true;
					}
				});
				if (info.isAvailable())
					groupAvailable.addPreference(boxPref);
				else if (!autoFinish && info.hasPreferences())
				{
					hasKnown = true;
					groupKnown.addPreference(boxPref);
				}
			}
		}
		if (!autoFinish && !hasKnown) root.removePreference(groupKnown);

		// manual input of box' address
		Preference newPref = new Preference(this);
		newPref.setTitle(R.string.pref_address_manual);
		newPref.setSummary(R.string.pref_address_manual2);
		newPref.setPersistent(false);
		newPref.setOnPreferenceClickListener(new OnPreferenceClickListener()
		{
			public boolean onPreferenceClick(Preference preference)
			{
				showDialog(DIALOG_OPTIONALHOST);
				return true;
			}
		});
		groupAvailable.addPreference(newPref);

		root.setPersistent(false);
		root.bind(mListView);
		mListView.setAdapter(root.getRootAdapter());
		setPreferenceScreen(root);
		
		updateSelection();
	}
	
	private void updateSelection()
	{
		String udn = ComSettingsChecker.getUdnSwitchTo();
		if (udn.length() == 0)
		{
			BoxInfo boxInfo = ComSettingsChecker.getBoxes().getUsableMru();
			udn = (boxInfo == null) ? "" : boxInfo.getUdn();
		}
		
		ListAdapter adapter = mListView.getAdapter();
		for (int ii = 0; (adapter != null) && (ii < adapter.getCount()); ii++)
		{
			Object item = adapter.getItem(ii);
			if ((item != null) &&
					item.getClass().equals(FritzboxListItem.class))
			{
				FritzboxListItem boxPref =(FritzboxListItem)item;
				boxPref.setChecked(udn.equals(boxPref.getUdn()));
			}
		}
	}

	private void onConnect(String udn)
	{
		BoxInfoList boxes = ComSettingsChecker.getBoxes();
		BoxInfo boxInfo = boxes.get(udn, true);
		if (boxInfo == null) return;

		if (boxInfo.isTr64())
		{
			IBoxService srv = mBoxServiceConnection.getService();
			if (srv != null)
			{
				// currently excluded from connecting to?
				if (!boxInfo.isAutoConnect())
				{
					boxInfo.setIsAutoconnect(true);
					boxes.save(getApplicationContext());
				}
				srv.switchTo(udn);
			}
			if (!autoFinish()) updateSelection();
		}
		else
		{
			updateSelection();
			TextDialog.create(SettingsFritzBoxActivity.this,
					TextDialog.getDefaultTitle(SettingsFritzBoxActivity.this),
					ConnectionProblem.FRITZBOX_NOTR064.getDisplayMessage(
							SettingsFritzBoxActivity.this),
                    TextDialog.DEFAULT_MESSAGE_ICON)
					.show();
		}
	}
	
	private void onRemoveCredentials(String udn)
	{
		BoxInfoList boxes = ComSettingsChecker.getBoxes();
		BoxInfo boxInfo = boxes.get(udn, false);
		IBoxService srv = mBoxServiceConnection.getService();
		if ((boxInfo != null) && (srv != null))
		{
			boxInfo.setBoxPassword("");
			boxInfo.setIsAutoconnect(false);
			boxes.save(getApplicationContext());
			BoxInfo boxConnected = ComSettingsChecker.getBoxInfo();
			if ((boxConnected != null) &&
					udn.equals(boxConnected.getUdn()))
				srv.switchTo(null);
			update();
		}
	}
	
	private void onRemovePreferences(String udn)
	{
		BoxInfoList boxes = ComSettingsChecker.getBoxes();
		BoxInfo boxInfo = boxes.get(udn, false);
		IBoxService srv = mBoxServiceConnection.getService();
		if ((boxInfo != null) && (srv != null))
		{
			boxInfo.removePreferences();
			boxes.save(getApplicationContext());
			// currently connected to this box?
			BoxInfo boxConnected = ComSettingsChecker.getBoxInfo();
			if ((boxConnected != null) &&
					udn.equals(boxConnected.getUdn()))
				srv.switchTo(null);
			update();
		}
	}
	
	private void searchBoxes()
	{
		if (!mLoading)
		{
			IBoxService srv = mBoxServiceConnection.getService();
			if (srv != null)
			{
				showWait();
				mLoading = true;
				String[] hosts = (mOptionalHost.length() > 0) ?
						new String[] { mOptionalHost } : null;
				srv.searchBoxes(hosts);
			}
		}
	}

	private void onBoxSearchDone(Exception error)
	{
		dismissWait();
		mInitiallyBlank = false;
		if (error != null)
		{
			if (mLoading)
				TextDialog.createOk(SettingsFritzBoxActivity.this,
						getString(R.string.soap_tranfer_failed),
						android.R.drawable.ic_dialog_alert)
						.show();
			mLoading = false;
			return;
		}
		mLoading = false;

		update();
		try
		{
			if (mListView.getAdapter().getCount() == 0)
			{
				// no box available
				TextDialog.create(SettingsFritzBoxActivity.this,
						TextDialog.getDefaultTitle(SettingsFritzBoxActivity.this),
						ResourceHelper.getTextForHelpTopic(
								Help.HelpTopic.FINDBOX,
								SettingsFritzBoxActivity.this),
                        TextDialog.DEFAULT_MESSAGE_ICON)
						.setOnCancelListener(new OnCancelListener()
						{
							public void onCancel(DialogInterface dialog)
							{
								autoFinish();
							}
						})
						.show();
			}
		}
		catch(Exception e)
		{
            FileLog.w(TAG, e.getMessage(), e);
		}
	}
	
	private void showWait()
	{
		if (mWaitDialog == null)
		{
			mWaitDialog = WaitDialog.show(this, R.string.wait_dialog,
					new DialogInterface.OnCancelListener()
			{
				public void onCancel(DialogInterface dialog)
				{
					if (dialog == mWaitDialog)
					{
						// cancel pending load
						mWaitDialog = null;
						dialog.dismiss();
						mLoading = false;
						
						// if first loading, close activity
						if (mInitiallyBlank)
							SettingsFritzBoxActivity.this.finish();
					}
					else dialog.dismiss();
				}
			});
		}
	}
	
	private void dismissWait()
	{
		if (mWaitDialog != null)
		{
			mWaitDialog.dismiss();
			mWaitDialog = null;
		}
	}

	@Override
	protected Dialog onCreateDialog(int id)
	{
		switch (id)
		{
			// add new exception
			case DIALOG_OPTIONALHOST: 
			{
				return TextDialog.createEdit(this,
						getString(R.string.pref_address_manual), mOptionalHost,
						InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI)
						.setPositiveButton(android.R.string.ok,
								new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialogInterface, int id)
							{
								try
								{
									String optionalHost = ((TextView)(((Dialog)dialogInterface)
											.findViewById(R.id.message))).getText()
											.toString(); 
									URI uri = new URI(optionalHost);
									mOptionalHost = (uri.getHost() == null) ?
											optionalHost : uri.getHost();
									searchBoxes();
								}
								catch(Exception e)
								{
									TextDialog.createOk(SettingsFritzBoxActivity.this,
											getString(R.string.pref_address_manual_invalid),
											android.R.drawable.ic_dialog_alert)
											.show();
								}
								removeDialog(DIALOG_OPTIONALHOST);
							}
						}).setNegativeButton(android.R.string.cancel,
									new DialogInterface.OnClickListener()
						{
							public void onClick(DialogInterface dialogInterface, int id)
							{
								removeDialog(DIALOG_OPTIONALHOST);
							}
						}).create();
			}
			
			case DIALOG_ITEM_CLICKED:
			{
				final BoxInfo boxInfo = ComSettingsChecker.getBoxes()
						.get(mItemClickedUdn, false);
				if (boxInfo != null)
				{
					final int[] resIds = new int[] { 0, 0, 0 };
					int ii = 0;
					BoxInfo currentBox = ComSettingsChecker.getBoxInfo();
					if (boxInfo.isAvailable() &&
							((currentBox == null) ||
							!currentBox.getUdn().equals(boxInfo.getUdn())))
						resIds[ii++] = R.string.pref_address_action_connect;
					if (boxInfo.hasPreferences())
					{
						if (!TextUtils.isEmpty(boxInfo.getBoxPassword()))
								resIds[ii++] = R.string.pref_address_action_delcred;
						resIds[ii++] = R.string.pref_address_action_delete;
					}
					String[] items = new String[ii];
					for (ii--; ii >= 0; ii--) items[ii] = getString(resIds[ii]);

					return new AlertDialog.Builder(this)
							.setTitle(boxInfo.getDisplayName())
							.setCancelable(true)
							.setInverseBackgroundForced(true)
							.setItems(items, new OnClickListener()
							{
								public void onClick(DialogInterface dialog, int which)
								{
									switch (resIds[which])
									{
										case R.string.pref_address_action_connect:
											onConnect(boxInfo.getUdn());
											break;
										case R.string.pref_address_action_delcred:
											onRemoveCredentials(boxInfo.getUdn());
											break;
										case R.string.pref_address_action_delete:
											onRemovePreferences(boxInfo.getUdn());
											break;
									}
									removeDialog(DIALOG_ITEM_CLICKED);
								}
							})
							.setOnCancelListener(new OnCancelListener()
							{
								public void onCancel(DialogInterface dialog)
								{
									removeDialog(DIALOG_ITEM_CLICKED);
								}
							}).create();
				}
			}
		}
		return null;
	}

	private void onStatusChanged()
	{
		if (mListView != null)
		{
			ListAdapter adapter = mListView.getAdapter();
			if (adapter != null)
			{
				for (int ii = 0; ii < adapter.getCount(); ii++)
				{
					Object object = adapter.getItem(ii);
					if ((object != null) && FritzboxListItem.class.equals(object.getClass()))
						((FritzboxListItem)object).updateInfo();
				}
			}
			updateSelection();
		}
	}
	
	/**
	 * Preparations on settings to do on app's start
	 * @param context context for reading and writing the settings
	 * @param firstRun true for first run after install
	 */
	public static void prepareSettings(Context context, boolean firstRun)
	{
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(context); 
		Editor edit = prefs.edit();
		
		// remove old settings
		edit.remove(SettingsActivity.PREF_BOX_UDN);
		edit.remove(SettingsActivity.PREF_PASS);

		edit.apply();
	}
}
