package org.ourfilesystem.core;

/*
OurFileSystem is a peer2peer file sharing program.
Copyright (C) 2012  Robert Gass

This program 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 2 of the License, or
(at your option) any later version.

This program 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 this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

*/

import java.io.File;
import java.security.SecureRandom;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.ourfilesystem.com.ComConnectionInterface;
import org.ourfilesystem.com.ComPeerInterface;
import org.ourfilesystem.com.ConnectionUpdateInterface;
import org.ourfilesystem.db.DataBaseComInterface;
import org.ourfilesystem.db.DataBaseTools;
import org.ourfilesystem.db.FileReference;
import org.ourfilesystem.db.LocalFileReference;
import org.ourfilesystem.db.LocalPost;
import org.ourfilesystem.db.Peer;
import org.ourfilesystem.db.Post;
import org.ourfilesystem.db.PostHoles;
import org.ourfilesystem.db.StorageInterface;
import org.ourfilesystem.db.TimeInterface;
import org.ourfilesystem.security.CryptoDataBaseInterface;
import org.ourfilesystem.security.KeySet;
import org.ourfilesystem.security.PublicKeySetSigned;

public class Core implements CoreUserInterface, CoreComInterface {
	
	//TODO: Create connection pass counts too!  Then we can see which connections do the best.
	// Some how better manage the failed connections stuff.  Right now we're clearing the failures
	// upon a successful connection, which doesn't mean we actually get data from it.
	
	private StorageInterface UserDataBase;
	private DataBaseComInterface ComDataBase;
	private ComConnectionInterface ComConnector;
	private CryptoDataBaseInterface Security;
	private TimeInterface Time;
	private LinkedList<EventInterface> Events;

	private int MaxPostQuery = 100;
	private int MaxDownloadAttempts = 100;
	private double BusyMultiplier = 1;
	
	private SecureRandom Random;
	
	private HashMap<Object, ComPeerInterface> OpenConnections;
	private HashMap<Object, Long> ConnectionFailures;
	private HashMap<Object, Integer> DownloadAttempts;
	private HashMap<Object, Integer> GetPostAttempts;
	
	public Core() {
		OpenConnections = new HashMap<Object, ComPeerInterface>();
		ConnectionFailures = new HashMap<Object, Long>();
		Random = new SecureRandom();
		DownloadAttempts = new HashMap<Object, Integer>();
		GetPostAttempts = new HashMap<Object, Integer>();
		Events = new LinkedList<EventInterface>();
	}
	
	//======================================================================================
	/**
	 * Dependency injects.
	 */
	public void addEventInterface(EventInterface ei) {
		synchronized (Events) {
			Events.add(ei);
		}
	}
	
	private LinkedList<EventInterface> getEventInterfaces() {
		LinkedList<EventInterface> te = new LinkedList<EventInterface>();
		synchronized (Events) {
			te.addAll(Events);
		}
		return te;
	}
	
	public StorageInterface getUserDataBase() {
		return UserDataBase;
	}

	public void setUserDataBase(StorageInterface userDataBase) {
		UserDataBase = userDataBase;
	}

	public DataBaseComInterface getComDataBase() {
		return ComDataBase;
	}

	public void setComDataBase(DataBaseComInterface comDataBase) {
		ComDataBase = comDataBase;
	}
	
	public ComConnectionInterface getComConnector() {
		return ComConnector;
	}

	public void setComConnector(ComConnectionInterface comConnector) {
		ComConnector = comConnector;
	}
	
	//========================================================================================
	private void newConnectionEvent(ConnectionUpdateInterface p) {
		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			EventInterface e = i.next();
			p.addEventInterface(e);
			e.connectionEvent(p);
		}
		
	}
	
	private void connectionFailureEvent(Peer p) {
 		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			i.next().connectionFailure(p);
		}
	}
	
	private void newPeerReceivedEvent(Peer p) {
		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			i.next().newPeerReceived(p);
		}		
	}
	
	private void newFileDownloadedEvent(LocalFileReference lf) {
		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			i.next().newFileDownloaded(lf);
		}
	}
	
	private void newPostReceivedEvent(LocalPost lp) {
		Iterator<EventInterface> i = getEventInterfaces().iterator();
		while (i.hasNext()) {
			i.next().newPostReceived(lp);
		}		
	}
	
	//========================================================================================

	private synchronized ComPeerInterface getConnection(Object pid) {
		Peer p = getComDataBase().getPeer(pid);
		if (p != null && !p.equals(getComDataBase().getMyPeer())) {
			ComPeerInterface c = null;
			//Note, even though an open connection may not be ok, we still
			//return what we have in case it simply hasn't connected yet.
			synchronized (OpenConnections) {
				c = OpenConnections.get(pid);
			}
			if (c != null) return c;
			c = getComConnector().getComPeerInterface(p);
			if (c != null) {
				newConnectionEvent((ConnectionUpdateInterface) c);
				synchronized (OpenConnections) {
					OpenConnections.put(pid, c);
				}
				return c;
			}
			connectionFailureEvent(p);
		}
		return null;
	}
	
	private ComPeerInterface getRandomPeerConnection(Set<Object> peers) {
		if (peers.size() > 0) {
			Object pa[] = peers.toArray();
			return getConnection(pa[Random.nextInt(pa.length)]);
		}
		return null;
	}
	
	private ComPeerInterface getPeerConnection(Object pid) {
		ComPeerInterface c = null;
		synchronized (OpenConnections) {
			c = OpenConnections.get(pid);
		}
		if (c != null && c.isOk()) {
			synchronized (ConnectionFailures) {
				ConnectionFailures.remove(pid);
			}
			return c;
		}
		else {
			return getConnection(pid);
		}
	}
	
	private ComPeerInterface getAPeerConnection(Set<Object> peers) {
		Set<Object> notconnected = new HashSet<Object>();
		Iterator<Object> i = peers.iterator();
		ComPeerInterface leastbusy = null;
		while (i.hasNext()) {
			Object pid = i.next();
			ComPeerInterface ci = null;
			synchronized (OpenConnections) {
				ci = OpenConnections.get(pid);
			}
			if (ci != null) {
				if (ci.isOk()) {
					synchronized (ConnectionFailures) {
						ConnectionFailures.remove(pid);
					}
					if (leastbusy == null) {
						leastbusy = ci;
					}
					else if (leastbusy.getPendingRequests() > ci.getPendingRequests()) {
						leastbusy = ci;
					}
				}
				else {
					notconnected.add(pid);
				}
			}
			else {
				notconnected.add(pid);
			}
		}
		if (leastbusy != null && leastbusy.getPendingRequests() == 0) {
			// We're not busy at all!  Use me!
			return leastbusy;
		}
		else {
			// Check if any we're not connected to
			// get least failing peer we're not connected to.
			Object leastfailed = null;
			long leastfails = 0;
			i = notconnected.iterator();
			while (i.hasNext()) {
				Object pid = i.next();
				Long numfails = 0L;
				synchronized (ConnectionFailures) {
					numfails = ConnectionFailures.get(pid);
				}
				if (numfails != null) {
					if (leastfailed == null) {
						leastfailed = pid;
						leastfails = numfails;
					}
					else if (numfails < leastfails) {
						leastfailed = pid;
						leastfails = numfails;						
					}
				}
			}
			if (leastfailed == null) {
				//there's no fail data.  Pick one of the not connected nodes at random.
				ComPeerInterface r = getRandomPeerConnection(notconnected);
				if (r == null) {
					r = leastbusy;
				}
				return r;
			}
			else {
				// if there was no existing connections use the leastfailed.
				if (leastbusy == null) {
					return getConnection(leastfailed);					
				}
				// Evalute if we want to try to use the leastfailed or the leastbusy
				// We randomly pick a busy node or try to connect to a new node based open
				// how busy a node is compared to the number of times it's failed. 
				double numfails = (double)leastfails;
				double numbusy = (double)leastbusy.getPendingRequests() * getBusyMultiplier();
				double d = (numfails + numbusy);
				double probofusingbusy = 0.5D;
				if (d > 0D) { 
					probofusingbusy = numfails/d;
				}
				if (Random.nextFloat() < probofusingbusy) {
					return leastbusy;
				}
				else {
					return getConnection(leastfailed);
				}
			}
		}
	}
	
	@Override
	public HashSet<Peer> getConnections() {
		HashSet<Peer> r = new HashSet<Peer>();
		synchronized (OpenConnections) {
			Iterator<Entry<Object, ComPeerInterface>> i = OpenConnections.entrySet().iterator();
			while (i.hasNext()) {
				Entry<Object, ComPeerInterface> e = i.next();
				Peer p = UserDataBase.getPeer(e.getKey());
				if (p != null) {
					r.add(p);
				}
			}
		}
		return r;
	}

	@Override
	public void generateNewKeys() {
		KeySet ks = Security.generateNewKeys();
		this.getUserDataBase().saveMyKeySet(ks);
		Peer np = getUserDataBase().getMyPeerData();
		if (np == null) {
			np = new Peer();
		}
		PublicKeySetSigned ps = new PublicKeySetSigned();
		ps.setPublicEncryptionKey(ks.getPublicKeySet().getPublicEncryptionKey());
		ps.setPublicSigningKey(ks.getPublicKeySet().getPublicSigningKey());
		np.setPeerKeysAndIdentity(ps);
		Security.selfSignMyPeerId(np, ks.getPrivateSigningKey());
		Security.signLocation(np, ks.getPrivateSigningKey());
		getUserDataBase().saveMyPeerData(np);
	}
	
	@Override
	/**
	 * 1) First set the unsigned key if no keys exist.
	 * 2) Second we get back our signed key.  Check the signature if from a peer that
	 * we have signed.  Note, this means that we must sign keys for networks we are joining
	 * even though they may have already been signed.  This keeps someone from just signing
	 * our key and getting access.
	 * 3) Then we save the data.
	 */
	public void setMyLocation(Object location) {
		Peer p = this.getUserDataBase().getMyPeerData();
		if (p == null) {
			p = new Peer();
		}
		p.setUpdateCount(p.getUpdateCount()+1);
		p.setLocation(location);
		KeySet ks = getUserDataBase().getMyKeySet();
		if (ks != null) {
			Security.signLocation(p, ks.getPrivateSigningKey());
		}
		getUserDataBase().saveMyPeerData(p);
		newPeerReceivedEvent(p);
	}

	public void setMyNickname(String nickname) {
		Peer p = this.getUserDataBase().getMyPeerData();
		if (p == null) {
			p = new Peer();
		}
		p.setUpdateCount(p.getUpdateCount()+1);
		p.setNickname(nickname);
		KeySet ks = getUserDataBase().getMyKeySet();
		if (ks != null) {
			Security.signLocation(p, ks.getPrivateSigningKey());
		}
		getUserDataBase().saveMyPeerData(p);
		newPeerReceivedEvent(p);
	}

	@Override
	public void setMyIntroduction(String intro) {
		Peer p = this.getUserDataBase().getMyPeerData();
		if (p == null) {
			p = new Peer();
		}
		p.setUpdateCount(p.getUpdateCount()+1);
		p.setIntroduction(intro);
		KeySet ks = getUserDataBase().getMyKeySet();
		Security.signLocation(p, ks.getPrivateSigningKey());
		getUserDataBase().saveMyPeerData(p);	
		newPeerReceivedEvent(p);
	}

	@Override
	public Peer signPeer(Peer peerdata) {
		if (peerdata.getPeerKeysAndIdentity() != null) {
			if (peerdata.getPeerKeysAndIdentity().getSignature() != null) {
				getUserDataBase().removeBadPeer(peerdata.getPeerKeysAndIdentity().getSignature().getDigest());
			}
		}
		KeySet ks = getUserDataBase().getMyKeySet();
		Peer myp = getUserDataBase().getMyPeerData();
		Object peerid = null;
		if (myp.getPeerKeysAndIdentity().getSignature() != null) {
			peerid = myp.getPeerKeysAndIdentity().getSignature().getDigest();
		}
		if (Security.verifyLocation(peerdata, peerdata.getPeerKeysAndIdentity().getPublicSigningKey())) {
			peerdata = Security.signPeer(peerdata, peerid, ks.getPrivateSigningKey());
			getUserDataBase().savePeer(peerdata);
			newPeerReceivedEvent(peerdata);
		}
		return peerdata;
	}

	@Override
	public LocalFileReference addLocalFile(File f, Object message) {
		Object dig = Security.digestFile(f);
		FileReference fr = new FileReference();
		fr.setFile(f);
		fr.setUnsignedDigest(dig);
		LocalFileReference lfr = new LocalFileReference();
		lfr.setFileReference(fr);
		lfr.setLocalDate(Time.getTime());
		getUserDataBase().saveFile(lfr);
		addLocalPost(message, dig);
		newFileDownloadedEvent(lfr);
		return lfr;
	}

	@Override
	public LocalPost addLocalPost(Object message, Object fileref) {
		Post p = new Post();
		LocalFileReference lf = getUserDataBase().getFileReference(fileref);
		if (lf != null && lf.getFileReference() != null && lf.getFileReference().getFile().exists()) {
			p.setPosterHasFile(true);
		}
		p.setMessage(message);
		p.setFileReferenceDigest(fileref);
		LocalPost lp = new LocalPost();
		lp.setPost(p);
		lp.setLocalDate(Time.getTime());
		//This is synchronized to keep the post numbers unique and sequential.
		//Clients never expect gaps.
		StorageInterface store = getUserDataBase();
		synchronized (store) {
			Long id = store.getLastPostNumber(getMyPeerData());
			if (id == null) {
				id = 0L;
			}
			id = id + 1;
			p.setPostNumber(id);
			Peer myp = store.getMyPeerData();
			p = Security.signPost(p, myp.getPeerKeysAndIdentity().getSignature().getDigest(), store.getMyKeySet().getPrivateSigningKey());
			store.savePost(lp);
		}
		newPostReceivedEvent(lp);
		List<ComPeerInterface> slist = new LinkedList<ComPeerInterface>();
		//TODO: Connect to all peers
		synchronized (OpenConnections) {
			slist.addAll(OpenConnections.values());
		}
		Iterator<ComPeerInterface> i = slist.iterator();
		while (i.hasNext()) {
			ComPeerInterface com = i.next();
			com.sendPost(p);
		}
		return lp;
	}

	@Override
	public void downloadFile(Object digest) {
		LocalFileReference lf = getUserDataBase().getFileReference(digest);
		if (lf == null || lf.getFileReference() == null || lf.getFileReference().getFile() == null || 
				(!lf.getFileReference().getFile().exists())) {
			int page = 0;
			Set<Object> peers = new HashSet<Object>();
			List<LocalPost> p = getUserDataBase().getFilePosts(digest, page, getMaxPostQuery());
			while (p.size() > 0) {
				Iterator<LocalPost> i = p.iterator();
				while (i.hasNext()) {
					LocalPost lp = i.next();
					if (lp.getPost().isPosterHasFile()) {
						peers.add(lp.getPost().getSignedDigest().getPeerIdentifier());
					}
				}
				page++;
				p = getUserDataBase().getFilePosts(digest, page, getMaxPostQuery());
			}
			ComPeerInterface com = getAPeerConnection(peers);
			if (com != null) {
				com.requestsFile(digest);
			}
		}
		else {
			newFileDownloadedEvent(lf);
		}
	}

	@Override
	public void updatePeers() {
		List<Peer> pl = getUserDataBase().getPeerList();
		Iterator<Peer> i = pl.iterator();
		while (i.hasNext()) {
			Peer p = i.next();
			ComPeerInterface com = getPeerConnection(p.getPeerKeysAndIdentity().getSignature().getDigest());
			if (com != null) {
				com.requestPeers();
			}
		}
	}

	@Override
	public void updatePosts() {
		Object myid = getUserDataBase().getMyPeerData().getPeerKeysAndIdentity().getSignature().getDigest();
		List<Peer> pl = getUserDataBase().getPeerList();
		Iterator<Peer> i = pl.iterator();
		while (i.hasNext()) {
			Peer p = i.next();
			if (!p.getPeerKeysAndIdentity().getSignature().getDigest().equals(myid)) {
				Long lastnum = getUserDataBase().getLastPostNumber(p);
				if (lastnum == null) {
					lastnum = 0L;
				}
				ComPeerInterface com = getPeerConnection(p.getPeerKeysAndIdentity().getSignature().getDigest());
				if (com != null) {
					com.requtestPosts(p.getPeerKeysAndIdentity().getSignature().getDigest(), lastnum+1, Long.MAX_VALUE);
				}
				List<PostHoles> holes = getUserDataBase().getPostHoles(p);
				if (holes != null) {
					Iterator<PostHoles> i2 = holes.iterator();
					while (i2.hasNext()) {
						PostHoles ph = i2.next();
						com.requtestPosts(p.getPeerKeysAndIdentity().getSignature().getDigest(), ph.getFirstNumber(), ph.getLastNumber());
					}
				}
			}
		}
	}

	
	//====================================================================================
	/**
	 * Callback for when a new connection is received.
	 */

	@Override
	public void newConnection(ComPeerInterface com) {
		synchronized (OpenConnections) {
			newConnectionEvent((ConnectionUpdateInterface) com);
			OpenConnections.put(com.getPeer().getPeerKeysAndIdentity().getSignature().getDigest(), com);
		}
	}

	//====================================================================================
	/**
	 * Callbacks from connections when connections are lost or requests failed.
	 * Note, the connection failure should be noted before this is called.  So the
	 * failed peer is indicated prior to choosing the next peer connection to use.
	 */
	@Override
	public void requestPostsFailed(Object conpeer, Object peerid, long fromnumber, long tonumber) {
		boolean tryagain = false;
		synchronized (GetPostAttempts) {
			Integer attempts = GetPostAttempts.get(peerid);
			if (attempts == null) {
				attempts = 0;
			}
			attempts = attempts + 1;
			if (attempts > this.getMaxDownloadAttempts()) {
				tryagain = false;
			}
			else {
				tryagain = true;
			}			
			GetPostAttempts.put(peerid, attempts);
		}
		if (tryagain) {
			Set<Object> peerids = new HashSet<Object>();
			List<Peer> pl = this.getUserDataBase().getPeerList();
			Iterator<Peer> i = pl.iterator();
			while (i.hasNext()) {
				peerids.add(i.next().getPeerKeysAndIdentity().getSignature().getDigest());
			}
			ComPeerInterface com = this.getAPeerConnection(peerids);
			if (com != null) {
				com.requtestPosts(peerid, fromnumber, tonumber);
			}
		}
	}

	@Override
	public void requestFileFailed(Object conpeer, Object filedig) {
		boolean tryagain = false;
		synchronized (DownloadAttempts) {
			Integer attempts = DownloadAttempts.get(filedig);
			if (attempts == null) {
				attempts = 0;
			}
			attempts = attempts + 1;
			if (attempts > this.getMaxDownloadAttempts()) {
				tryagain = false;
			}
			else {
				tryagain = true;
			}
			DownloadAttempts.put(filedig, attempts);
		}
		if (tryagain) {
			downloadFile(filedig);
		}
		else {
			Iterator<EventInterface> i = getEventInterfaces().iterator();
			while (i.hasNext()) {
				i.next().downloadFailed(filedig);
			}			
		}
	}

	@Override
	public void requestPostSucceed(Object conpeer, Object peerid) {
		synchronized (GetPostAttempts) {
			GetPostAttempts.remove(peerid);
		}
	}

	@Override
	public void requestFileSucceed(Object conpeer, Object filedig) {
		synchronized(DownloadAttempts) {
			DownloadAttempts.remove(filedig);
		}
	}

	@Override
	public void connectionClosed(Object conpeer, boolean error) {
		synchronized (OpenConnections) {
			OpenConnections.remove(conpeer);
		}
		synchronized (ConnectionFailures) {
			Long failures = ConnectionFailures.get(conpeer);
			if (failures == null) {
				failures = 0L;
			}
			failures = failures + 1;
			ConnectionFailures.put(conpeer, failures);
			Peer p = UserDataBase.getPeer(conpeer);
			connectionFailureEvent(p);
		}
	}


	
	//=====================================================================================
	/**
	 * Settings/Configuration.
	 */
	
	public int getMaxPostQuery() {
		return MaxPostQuery;
	}

	public void setMaxPostQuery(int maxPostQuery) {
		MaxPostQuery = maxPostQuery;
	}

	public double getBusyMultiplier() {
		return BusyMultiplier;
	}

	public void setBusyMultiplier(double busyMultiplier) {
		BusyMultiplier = busyMultiplier;
	}

	public int getMaxDownloadAttempts() {
		return MaxDownloadAttempts;
	}

	public void setMaxDownloadAttempts(int maxDownloadAttempts) {
		MaxDownloadAttempts = maxDownloadAttempts;
	}

	public CryptoDataBaseInterface getSecurity() {
		return Security;
	}

	public void setSecurity(CryptoDataBaseInterface security) {
		Security = security;
	}

	public TimeInterface getTime() {
		return Time;
	}

	public void setTime(TimeInterface time) {
		Time = time;
	}
	
	//=====================================================================================
	/**
	 * Storage interface. 
	 */
	
	@Override
	public Peer getMyPeerData() {
		return UserDataBase.getMyPeerData();
	}

	@Override
	public List<Peer> getPeerList() {
		return UserDataBase.getPeerList();
	}

	@Override
	public List<LocalPost> getPeerPostsByPage(Peer p, int page, int pagesize) {
		return UserDataBase.getPeerPosts(p, page, pagesize);
	}

	@Override
	public List<LocalPost> getPeerPostsByNumber(Peer p, long start, long end) {
		return UserDataBase.getPeerPosts(p, start, end);
	}

	@Override
	public List<LocalPost> getFilePosts(Object dig, int page, int pagesize) {
		return UserDataBase.getFilePosts(dig, page, pagesize);
	}

	@Override
	public List<LocalPost> getPosts(Date fromdate) {
		return UserDataBase.getPosts(fromdate);
	}

	@Override
	public LocalFileReference getFileReference(Object dig) {
		return UserDataBase.getFileReference(dig);
	}

	@Override
	public List<LocalFileReference> getFileReferences(int page, int pagesize) {
		return UserDataBase.getFileReferences(page, pagesize);
	}

	@Override
	public List<LocalFileReference> getFileReferences(Date fromdate) {
		return UserDataBase.getFileReferences(fromdate);
	}

	@Override
	public void CloseConnections() {
		ComConnectionInterface c = getComConnector();
		if (c != null) {
			c.Close();
		}
		List<ComPeerInterface> tl = new LinkedList<ComPeerInterface>();
		synchronized (OpenConnections) {
			tl.addAll(OpenConnections.values());
		}
		Iterator<ComPeerInterface> i = tl.iterator();
		while (i.hasNext()) {
			ComPeerInterface e = i.next();
			try {
				e.Close();
			}
			catch (Exception e2) {
			}
		}
	}

	@Override
	public void setBadPeer(Peer p) {
		getUserDataBase().saveBadPeer(p);
		getUserDataBase().removePeer(p);
		ComPeerInterface com = null;
		synchronized (OpenConnections) {
			com = OpenConnections.get(p.getPeerKeysAndIdentity().getSignature().getDigest());
		}
		if (com != null) {
			com.Close();
			connectionFailureEvent(p);
		}
		Peer me = getMyPeerData();
		List<Peer> lst = getUserDataBase().getPeerList();
		Iterator<Peer> i = lst.iterator();
		while (i.hasNext()) {
			Peer tp = i.next();
			if (!DataBaseTools.verifyPeerSignature(tp, getUserDataBase(), getSecurity())) {
				if (tp.equals(me)) {
					KeySet ks = UserDataBase.getMyKeySet();
					Security.selfSignMyPeerId(me, ks.getPrivateSigningKey());
					getUserDataBase().saveMyPeerData(me);				
				}
				else {
					getUserDataBase().removePeer(tp);
					
					synchronized (OpenConnections) {
						com = OpenConnections.get(tp.getPeerKeysAndIdentity().getSignature().getDigest());
					}
					if (com != null) {
						com.Close();
						//We've removed the peer, so the callback on close won't send the event.
						connectionFailureEvent(tp);
					}
				}
			}
		}
	}


}
