/* NFC Spy is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

NFC Spy 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
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Wget.  If not, see <http://www.gnu.org/licenses/>.

Additional permission under GNU GPL version 3 section 7 */

package com.sinpo.nfcspy;

import static com.sinpo.nfcspy.ServiceFactory.ERR_APDU_CMD;
import static com.sinpo.nfcspy.ServiceFactory.ERR_APDU_RSP;
import static com.sinpo.nfcspy.ServiceFactory.MSG_CHAT_RECV;
import static com.sinpo.nfcspy.ServiceFactory.MSG_CHAT_SEND;
import static com.sinpo.nfcspy.ServiceFactory.MSG_HCE_APDU_CMD;
import static com.sinpo.nfcspy.ServiceFactory.MSG_HCE_APDU_RSP;
import static com.sinpo.nfcspy.ServiceFactory.MSG_HCE_ATTACH;
import static com.sinpo.nfcspy.ServiceFactory.MSG_HCE_DEACTIVATED;
import static com.sinpo.nfcspy.ServiceFactory.MSG_HCE_DETTACH;
import static com.sinpo.nfcspy.ServiceFactory.MSG_P2P_CONNECT;
import static com.sinpo.nfcspy.ServiceFactory.MSG_P2P_DISCONN;
import static com.sinpo.nfcspy.ServiceFactory.MSG_P2P_INIT;
import static com.sinpo.nfcspy.ServiceFactory.MSG_P2P_SOCKET;
import static com.sinpo.nfcspy.ServiceFactory.MSG_SERVER_VER;
import static com.sinpo.nfcspy.ServiceFactory.STA_ERROR;
import static com.sinpo.nfcspy.ServiceFactory.STA_FAIL;
import static com.sinpo.nfcspy.ServiceFactory.STA_NOTCARE;
import static com.sinpo.nfcspy.ServiceFactory.STA_P2P_ACCEPT;
import static com.sinpo.nfcspy.ServiceFactory.STA_P2P_CLIENT;
import static com.sinpo.nfcspy.ServiceFactory.STA_SUCCESS;

import java.io.File;
import java.io.FileOutputStream;

import com.sinpo.nfcspy.NfcManager.TagListener;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.text.ClipboardManager;
import android.text.Html;
import android.text.TextUtils;
import android.text.method.DigitsKeyListener;
import android.text.method.LinkMovementMethod;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ActivityMain extends ActivityBase implements Handler.Callback,
		TagListener {

	public ActivityMain() {
		inbox = new Messenger(new Handler(this));
	}

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

		messages = new ArrayAdapter<CharSequence>(this,
				R.layout.listitem_message);

		((ListView) findViewById(R.id.list)).setAdapter(messages);

		messageView = (TextView) findViewById(R.id.txtChatLine);

		nfc = new NfcManager(this);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.action_copy:
			copyLogs();
			return true;
		case R.id.action_save:
			saveLogs();
			return true;
		case R.id.action_share:
			shareLogs();
			return true;
		case R.id.action_settings:
			startActivity(new Intent(this, ActivityManageP2P.class));
			return true;
		case R.id.action_viewlogs:
			showHCELogs();
			return true;
		case R.id.action_clearlogs:
			clearHCELogs();
			return true;
		case R.id.action_discoverydelay:
			setDiscoveryDelay();
			return true;
		case R.id.action_about:
			showHelp();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	public void startServer(View ignore) {
		if (!nfc.isEnabled()) {
			messages.add(Logger.fmtError(getString(R.string.status_no_nfc)));
			return;
		}

		if (!WiFiP2PManager.isWiFiEnabled(this)) {
			messages.add(Logger.fmtError(getString(R.string.status_no_wifi)));
			return;
		}

		if (!NfcManager.hasHCE())
			messages.add(Logger.fmtWarn(getString(R.string.status_no_hce)));

		ServiceFactory.startServer(this, inbox);
	}

	public void stopServer(View ignore) {
		ServiceFactory.stopServer(this);
	}

	public void clearList(View ignore) {
		messages.clear();
	}

	public void enableHighSpeed(View v) {
		ServiceFactory.setHighSpeed2Server(this, ((CheckBox) v).isChecked());
	}

	public void sendChatMessage(View ignore) {
		final String msg = messageView.getText().toString();
		if (!TextUtils.isEmpty(msg))
			ServiceFactory.sendChatMessage2Server(this, msg);
	}

	@Override
	protected void onResume() {
		super.onResume();
		nfc.onResume(this);
	}

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

	@Override
	protected void onDestroy() {
		nfc = null;
		super.onDestroy();
	}

	@Override
	protected void onNewIntent(Intent intent) {
		onNewTagIntent(intent);
	}

	@Override
	public void onNewTagIntent(Intent intent) {
		ServiceFactory.setTag2Server(this, intent);
	}

	@Override
	public boolean handleMessage(Message msg) {

		switch (msg.what) {

		case MSG_HCE_APDU_CMD:
		case MSG_HCE_APDU_RSP:
			printHceApdu(msg);
			break;
		case MSG_HCE_ATTACH:
			printNfcAttachMessage(msg);
			break;
		case MSG_HCE_DEACTIVATED:
			printHceDeactivatedMessage();
			break;
		case MSG_HCE_DETTACH:
			printStatusMessage(R.string.event_nfc_lost, STA_ERROR, 0);
			break;
		case MSG_CHAT_SEND:
		case MSG_CHAT_RECV:
			printChatMessage(msg);
			break;
		case MSG_P2P_SOCKET:
			printStatusMessage(R.string.status_getaddr, msg.arg1, msg.arg2);
			break;
		case MSG_P2P_CONNECT:
			printStatusMessage(R.string.status_connect, msg.arg1, msg.arg2);
			break;
		case MSG_P2P_DISCONN:
			printStatusMessage(R.string.status_disconn, msg.arg1, msg.arg2);
			break;
		case MSG_P2P_INIT:
			printStatusMessage(R.string.status_init, msg.arg1, msg.arg2);
			break;
		case MSG_SERVER_VER:
			printVersionInfo(msg);
			break;
		default:
			return false;
		}
		return true;
	}

	private void printStatusMessage(int descId, int status, int error) {
		final CharSequence desc;
		if (status == STA_NOTCARE) {
			desc = Logger.fmtInfo(getString(descId));
		} else if (status == STA_SUCCESS) {
			String sta = getString(R.string.status_success);
			desc = Logger.fmtInfo("%s%s", getString(descId), sta);
		} else if (status == STA_FAIL) {
			String sta = getString(R.string.status_failure);
			desc = Logger.fmtError("%s%s", getString(descId), sta);
		} else if (status == STA_ERROR) {
			desc = Logger.fmtError(getString(descId));
		} else {

			if (status == STA_P2P_ACCEPT)
				descId = R.string.status_p2p_accept;
			else if (status == STA_P2P_CLIENT)
				descId = R.string.status_p2p_client;

			String sta = getString(R.string.status_waitting);
			desc = Logger.fmtWarn("%s %s", getString(descId), sta);
		}

		messages.add(desc);
	}

	private void printChatMessage(Message msg) {

		byte[] raw = ServiceFactory.extractDataFromMessage(msg);
		if (raw != null && raw.length > 0) {
			String txt = new String(raw);

			if (MSG_CHAT_SEND == msg.what) {
				messages.add(Logger.fmtChatOut(txt));
				messageView.setText(null);
			} else {
				messages.add(Logger.fmtChatIn(txt));
			}
		}
	}

	private void printVersionInfo(Message msg) {

		byte[] raw = ServiceFactory.extractDataFromMessage(msg);
		if (raw != null && raw.length > 0) {
			String peer = new String(raw);
			String me = ThisApplication.version();
			if (me.equals(peer)) {
				String fmt = getString(R.string.event_p2p_version);
				messages.add(Logger.fmtInfo(fmt, me));
			} else {
				String fmt = getString(R.string.event_p2p_version2);
				messages.add(Logger.fmtWarn(fmt, peer, me));
			}
		}
	}

	private void printNfcAttachMessage(Message msg) {

		byte[] raw = ServiceFactory.extractDataFromMessage(msg);
		if (raw != null && raw.length > 0) {
			long stamp = System.currentTimeMillis();
			messages.add(Logger.fmtNfcAttachMessage(new String(raw), stamp));
		}
	}

	private void printHceDeactivatedMessage() {
		long stamp = System.currentTimeMillis();
		messages.add(Logger.fmtHceDeactivatedMessage(stamp));
	}

	private void printHceApdu(Message msg) {
		byte[] apdu = ServiceFactory.extractDataFromMessage(msg);
		if (apdu != null && apdu.length > 0) {
			boolean isCmd = (MSG_HCE_APDU_CMD == msg.what);
			messages.add(Logger.fmtApdu(msg.arg2, isCmd, apdu));
		} else {
			String hint;
			if (STA_ERROR == msg.arg1) {
				if (ERR_APDU_CMD == msg.arg2)
					hint = getString(R.string.event_p2p_connect);
				else if (ERR_APDU_RSP == msg.arg2)
					hint = getString(R.string.event_nfc_rsp);
				else
					hint = getString(R.string.event_p2p_connect);
			} else {
				hint = getString(R.string.event_nfc_lost);
			}

			messages.add(Logger.fmtError(hint));
		}
	}

	private void shareLogs() {
		CharSequence logs = getAllLogs();
		if (!TextUtils.isEmpty(logs)) {
			Intent intent = new Intent();
			intent.setAction(Intent.ACTION_SEND);
			intent.putExtra(Intent.EXTRA_TEXT, logs);
			intent.setType("text/plain");
			startActivity(intent);
		}
	}

	private void copyLogs() {
		CharSequence logs = getAllLogs();
		if (!TextUtils.isEmpty(logs)) {
			((ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE))
					.setText(logs);

			Toast.makeText(this, R.string.event_log_copy, Toast.LENGTH_LONG)
					.show();
		}
	}

	private void showHCELogs() {
		int n = Logger.getCachedLogsCount();
		for (int i = 0; i < n; ++i)
			messages.add(Logger.getCachedLog(i));
	}

	private void clearHCELogs() {
		Logger.clearCachedLogs();
	}

	private void setDiscoveryDelay() {

		final EditText input = new EditText(this);
		input.setHint(this.getString(R.string.hint_discoverydelay));
		input.setText(Integer.toString(nfc.getDiscoveryDelay()));
		input.setInputType(EditorInfo.TYPE_CLASS_NUMBER);
		input.setKeyListener(DigitsKeyListener.getInstance("01234567890"));
		input.setSingleLine(true);

		SetDelayHelper helper = new SetDelayHelper(nfc, input);

		new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_LIGHT)
				.setTitle(R.string.action_discoverydelay)
				.setMessage(R.string.lab_discoverydelay).setView(input)
				.setPositiveButton(R.string.action_ok, helper)
				.setNegativeButton(R.string.action_cancel, helper).show();
	}

	private void showHelp() {
		CharSequence title = Logger.fmt("%s v%s", ThisApplication.name(),
				ThisApplication.version());
		CharSequence info = Html.fromHtml(getString(R.string.info_about));

		TextView tv = (TextView) getLayoutInflater().inflate(
				R.layout.dialog_message, null);
		tv.setLinksClickable(true);
		tv.setMovementMethod(LinkMovementMethod.getInstance());
		tv.setText(info);

		new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_LIGHT)
				.setTitle(title).setView(tv)
				.setNeutralButton(R.string.action_ok, null).show();
	}

	private void saveLogs() {
		CharSequence logs = getAllLogs();
		if (!TextUtils.isEmpty(logs)) {
			View root = getLayoutInflater().inflate(R.layout.dialog_savelog,
					null);
			EditText name = (EditText) root.findViewById(R.id.file);
			name.setText(Logger.getCurrentLogFileName());

			EditText note = (EditText) root.findViewById(R.id.note);

			SaveLogHelper helper = new SaveLogHelper(logs, name, note);
			
			setTheme(android.R.style.Theme_Holo_Light);
			new AlertDialog.Builder(this)
					.setTitle(R.string.action_save).setView(root)
					.setNegativeButton(R.string.action_cancel, helper)
					.setPositiveButton(R.string.action_ok, helper).show();
		}
	}

	private CharSequence getAllLogs() {
		StringBuilder ret = new StringBuilder();

		final ArrayAdapter<CharSequence> messages = this.messages;
		final int count = messages.getCount();
		for (int i = 0; i < count; ++i) {
			if (i > 0)
				ret.append("\n\n");

			ret.append(messages.getItem(i));
		}

		return ret;
	}

	private static final class SetDelayHelper implements
			DialogInterface.OnClickListener {

		SetDelayHelper(NfcManager nfc, EditText input) {
			this.nfc = nfc;
			this.input = input;
		}

		@Override
		public void onClick(DialogInterface dialog, int which) {
			if (which == DialogInterface.BUTTON_POSITIVE) {
				CharSequence text = input.getText();
				if (TextUtils.isEmpty(text))
					text = "";

				int delay = -1;
				try {
					delay = Integer.parseInt(text.toString().trim());
				} catch (Exception e) {
					delay = -1;
				}

				if (delay < 100)
					delay = nfc.getDiscoveryDelay();

				nfc.setDiscoveryDelay(delay);
			}

			nfc = null;
			input = null;
			dialog.dismiss();
		}

		private NfcManager nfc;
		private EditText input;
	}

	private static final class SaveLogHelper implements
			DialogInterface.OnClickListener {
		CharSequence logs;
		EditText nameView, noteView;

		SaveLogHelper(CharSequence logs, EditText name, EditText note) {
			this.logs = logs;
			this.nameView = name;
			this.noteView = note;
		}

		@Override
		public void onClick(DialogInterface dialog, int which) {
			if (which == DialogInterface.BUTTON_POSITIVE)
				saveLogs();

			dialog.dismiss();
			nameView = null;
			noteView = null;
			logs = null;
		}

		private void saveLogs() {
			String file = nameView.getText().toString();
			if (TextUtils.isEmpty(file))
				return;

			Context ctx = nameView.getContext();
			String msg;
			try {
				File root = Environment.getExternalStorageDirectory();
				File path = new File(root, "/nfcspy/logs");

				File logf = new File(path, file);
				file = logf.getAbsolutePath();

				path.mkdirs();

				FileOutputStream os = new FileOutputStream(file);

				CharSequence note = Logger.fmtHeader(noteView.getText());
				if (!TextUtils.isEmpty(note))
					os.write(note.toString().getBytes());

				os.write(logs.toString().getBytes());
				os.close();

				msg = ctx.getString(R.string.event_log_save);
			} catch (Exception e) {
				msg = ctx.getString(R.string.event_log_notsave);
			}

			Toast.makeText(ctx, Logger.fmt(msg, file), Toast.LENGTH_LONG)
					.show();
		}
	}

	private NfcManager nfc;
	private Messenger inbox;
	private ArrayAdapter<CharSequence> messages;
	private TextView messageView;
}