public class

ConnectionHandoverManager

extends Object
implements NdefHandler PrioritizedHandler
/*
 * Copyright (C) 2011 Stanford University MobiSocial Lab
 * http://mobisocial.stanford.edu
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mobisocial.nfc;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.util.Log;

public class ConnectionHandoverManager implements NdefHandler, PrioritizedHandler {
	public static final String USER_HANDOVER_PREFIX = "ndef://wkt:hr/";
	public static final String TAG = "connectionhandover";
	public static final int HANDOVER_PRIORITY = 5;
	private final Set<ConnectionHandover> mmConnectionHandovers = new LinkedHashSet<ConnectionHandover>();

	public ConnectionHandoverManager() {

	}
	
	public void addConnectionHandover(ConnectionHandover handover) {
		mmConnectionHandovers.add(handover);
	}
	
	public void clearConnectionHandovers() {
		mmConnectionHandovers.clear();
	}
	
	public boolean removeConnectionHandover(ConnectionHandover handover) {
		return mmConnectionHandovers.remove(handover);
	}

	/**
	 * Returns the (mutable) set of active connection handover
	 * responders.
	 */
	public Set<ConnectionHandover> getConnectionHandoverResponders() {
		return mmConnectionHandovers;
	}
	
	//@Override
	public final int handleNdef(NdefMessage[] handoverRequest) {
		// TODO: What does this mean?
		return doHandover(handoverRequest[0]);
	}

	public final int doHandover(NdefMessage handoverRequest) {
	    int handoverRecordPos = findHandoverRequest(handoverRequest);
		if (handoverRecordPos == -1) {
			return NDEF_PROPAGATE;
		}

		if (findUserspaceHandoverRequest(handoverRequest) != -1) {
		    Uri uri;
		    try {
		        uri = NdefFactory.parseUri(handoverRequest.getRecords()[0]);
		    } catch (FormatException e) {
		        Log.e(TAG, "Bad handover record.", e);
		        return NDEF_PROPAGATE;
		    }
			handoverRequest = NdefFactory.fromNdefUri(uri);
			handoverRecordPos = 0;
		}

		NdefRecord[] records = handoverRequest.getRecords();
		for (int i = handoverRecordPos + 2; i < records.length; i++) {
			Iterator<ConnectionHandover> handovers = mmConnectionHandovers.iterator();
			while (handovers.hasNext()) {
				ConnectionHandover handover = handovers.next();
				if (handover.supportsRequest(records[i])) {
					try {
						Log.d(TAG, "Attempting handover " + handover);
						handover.doConnectionHandover(handoverRequest, handoverRecordPos, i);
						return NDEF_CONSUME;
					} catch (IOException e) {
						Log.w(TAG, "Handover failed.", e);
						// try the next one.
					}
				}
			}
		}

		return NDEF_PROPAGATE;
	}

	//@Override
	public int getPriority() {
		return HANDOVER_PRIORITY;
	}

	/**
	 * Returns true if the given Ndef message contains a connection
	 * handover request.
	 */
	public static int findHandoverRequest(NdefMessage ndef) {
		NdefRecord[] records = (ndef).getRecords();

		for (int i = 0; i < records.length; i++) {
    		if (records[i].getTnf() == NdefRecord.TNF_WELL_KNOWN
    			&& Arrays.equals(records[i].getType(), NdefRecord.RTD_HANDOVER_REQUEST)) {
    			return i;
    		}
		}
		return findUserspaceHandoverRequest(ndef);
	}

	public static boolean isHandoverRequest(NdefMessage ndefMessage) {
	    NdefRecord ndef = ndefMessage.getRecords()[0];
            return (ndef.getTnf() == NdefRecord.TNF_WELL_KNOWN
                && Arrays.equals(ndef.getType(), NdefRecord.RTD_HANDOVER_REQUEST));
	}
	// User-space handover:
	// TODO: Support uri profile
	private static int findUserspaceHandoverRequest(NdefMessage ndef) {
		NdefRecord[] records = (ndef).getRecords();
		for (int i = 0; i < records.length; i++) {
		    try {
		        String uriStr = NdefFactory.parseUri(records[i]).toString();
		        if (uriStr.startsWith(USER_HANDOVER_PREFIX)) {
		            return i;
		        }
		    } catch (FormatException e)  { }
		}
		return -1;
	}
}