/* 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 android.nfc.NfcAdapter.EXTRA_TAG;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.ReaderCallback;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;

final class NfcManager {
	final static String KEY_DISCOVERYDELAY = "opt_discovery_delay";
	final static int DEF_DISCOVERYDELAY = 3000;

	interface TagListener {
		void onNewTagIntent(Intent intent);
	}

	static boolean hasHCE() {
		return VERSION.SDK_INT >= VERSION_CODES.KITKAT;
	}

	NfcManager(Activity activity) {
		this.activity = activity;
		nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
		pendingIntent = PendingIntent.getActivity(activity, 0, new Intent(
				activity, activity.getClass())
				.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
	}

	void onPause() {
		if (nfcAdapter != null) {
			if (hasHCE())
				setReaderMode(false, 0);
			else
				nfcAdapter.disableForegroundDispatch(activity);
		}
	}

	void onResume(TagListener listener) {
		this.tagListener = listener;

		if (nfcAdapter != null) {
			if (hasHCE()) {
				setReaderMode(true, getDiscoveryDelay());
			} else {
				nfcAdapter.enableForegroundDispatch(activity, pendingIntent,
						TAGFILTERS, TECHLISTS);
			}
		}
	}

	boolean isEnabled() {
		return nfcAdapter != null && nfcAdapter.isEnabled();
	}

	int getDiscoveryDelay() {
		return ThisApplication.getPreference(KEY_DISCOVERYDELAY,
				DEF_DISCOVERYDELAY);
	}

	void setDiscoveryDelay(int delay) {
		ThisApplication.setPreference(KEY_DISCOVERYDELAY, delay);
		setReaderMode(true, delay);
	}

	@SuppressLint("NewApi")
	private void setReaderMode(boolean enable, int delay) {
		if (nfcAdapter == null || !hasHCE())
			return;

		if (!enable) {
			nfcAdapter.disableReaderMode(activity);
			return;
		}

		Bundle opts = new Bundle();
		opts.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 5000);

		int flags = NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
		flags |= NfcAdapter.FLAG_READER_NFC_A;

		// For the 'READ BINARY' problem of Braodcom's NFC stack.
		// Only works on Android 4.4+
		nfcAdapter.enableReaderMode(activity, new ReaderCallback() {

			@Override
			public void onTagDiscovered(Tag tag) {
				Intent i = new Intent().putExtra(EXTRA_TAG, tag);
				tagListener.onNewTagIntent(i);
			}
		}, flags, opts);
	}

	static IsoDep attachCard(Intent intent) {
		Tag tag = (Tag) intent.getParcelableExtra(EXTRA_TAG);
		return (tag != null) ? IsoDep.get(tag) : null;
	}

	static String getCardId(IsoDep isodep) {
		if (isodep != null) {
			byte[] id = isodep.getTag().getId();
			if (id != null && id.length > 0)
				return Logger.toHexString(id, 0, id.length);
		}

		return "UNKNOWN";
	}

	static void disconnect(IsoDep tag) {
		try {
			if (tag != null)
				tag.close();
		} catch (Exception e) {
		}
	}

	static byte[] transceiveApdu(IsoDep tag, byte[] cmd) {
		if (tag != null) {
			try {
				if (!tag.isConnected()) {
					tag.connect();
					tag.setTimeout(10000);
				}

				return tag.transceive(cmd);
			} catch (Exception e) {
			}
		}
		return null;
	}

	private final Activity activity;
	private NfcAdapter nfcAdapter;
	private PendingIntent pendingIntent;
	private TagListener tagListener;

	private static String[][] TECHLISTS;
	private static IntentFilter[] TAGFILTERS;

	static {
		try {
			TECHLISTS = new String[][] { { IsoDep.class.getName() } };

			TAGFILTERS = new IntentFilter[] { new IntentFilter(
					NfcAdapter.ACTION_TECH_DISCOVERED, "*/*") };
		} catch (Exception e) {
		}
	}
}