/*
 * libretroshare/src/services/p3dsdv.h
 *
 * Network-Wide Routing Service.
 *
 * Copyright 2011 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */

#include <list>
#include <string>
#include <time.h>

//#include "serialiser/rsdsdvitems.h"
#include "services/p3dsdv.h"
#include "pqi/p3linkmgr.h"
#include "util/rsrandom.h"

#include <openssl/sha.h>

/****
 * #define DEBUG_DSDV		1
 ****/
#define DEBUG_DSDV		1

/* DEFINE INTERFACE POINTER! */
RsDsdv *rsDsdv = NULL;

/*****
 * Routing Services are provided for any service or peer that wants to access / receive info 
 * over the internal network.
 *
 * This is going to be based loosely on Ben's Algorithm...
 * Each Service / Peer is identified by a HASH.
 * This HASH is disguised by a temporary ANON-CHUNK.
 * DSDVID = Sha1(ANONCHUNK + HASH).
 * 
 * Each peer can advertise as many Services as they want.
 * The Anon-chunk should be rotated regularly to hide it well.
 * period to be defined.
 * 
 *
 * Once this Routing table has been established, Routes can be created - in a similar manner to turtle paths.
 * (Send Path request (Path Id + Origin + Destination))... path is established.
 *
 * Then we can do Onion Routing etc, on top of this.
 *
 ****/

p3Dsdv::p3Dsdv(p3LinkMgr *lm)
	:p3Service(RS_SERVICE_TYPE_DSDV), /* p3Config(CONFIG_TYPE_DSDV), */ mDsdvMtx("p3Dsdv"), mLinkMgr(lm) 
{
	addSerialType(new RsDsdvSerialiser());

	mSentTablesTime = 0;
	mSentIncrementTime = 0;
}

int	p3Dsdv::tick()
{
	processIncoming();
	sendTables();

	return 0;
}

int	p3Dsdv::status()
{
	return 1;
}

#define DSDV_BROADCAST_PERIOD		(60*10) // 10 Minutes.
#define DSDV_MIN_INCREMENT_PERIOD	5
#define DSDV_DISCARD_PERIOD			(DSDV_BROADCAST_PERIOD * 2)

int	p3Dsdv::sendTables()
{
	time_t now = time(NULL);
	time_t tt, it;
	bool   updateRequired = false;
	{
		RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
		tt = mSentTablesTime;
		it = mSentIncrementTime;
		updateRequired = mSignificantChanges;
	}

	if (now - tt > DSDV_BROADCAST_PERIOD)
	{

#ifdef DEBUG_DSDV
		std::cerr << "p3Dsdv::sendTables() Broadcast Time";
		std::cerr << std::endl;
#endif
		selectStableRoutes();
		clearOldRoutes();

		generateRoutingTables(false);

		printDsdvTable(std::cerr);

		RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
		mSentTablesTime = now;
		mSentIncrementTime = now;

		return true ;
	}

	/* otherwise send incremental changes */
	if ((updateRequired) && (now - it > DSDV_MIN_INCREMENT_PERIOD))
	{
		selectStableRoutes();

		generateRoutingTables(true);

		RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
		mSentIncrementTime = now;
	}

	return true;
}

#define RSDSDV_SEQ_INCREMENT	2

void 	p3Dsdv::advanceLocalSequenceNumbers()
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	time_t now = time(NULL);

	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		RsDsdvTableEntry &v = (it->second);
		if (v.mOwnSource)
		{
			v.mStableRoute.mSequence += RSDSDV_SEQ_INCREMENT;
			v.mStableRoute.mReceived = now;
		}
	}
}

void 	p3Dsdv::clearSignificantChangesFlags()
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
	
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		RsDsdvTableEntry &v = (it->second);
		if (v.mFlags & RSDSDV_FLAGS_SIGNIFICANT_CHANGE)
		{
			v.mFlags &= ~RSDSDV_FLAGS_SIGNIFICANT_CHANGE;
		}
	}
	mSignificantChanges = false;
}


int 	p3Dsdv::generateRoutingTables(bool incremental)
{
	/* we ping our peers */
	/* who is online? */
	std::list<std::string> idList;
	mLinkMgr->getOnlineList(idList);

#ifdef DEBUG_DSDV
	std::cerr << "p3Dsdv::generateRoutingTables(" << incremental << ")";
	std::cerr << std::endl;
#endif

	if (!incremental)
	{
		/* now clear significant flag */
		advanceLocalSequenceNumbers();
	}

	/* prepare packets */
	std::list<std::string>::iterator it;
	for(it = idList.begin(); it != idList.end(); it++)
	{
#ifdef DEBUG_DSDV
		std::cerr << "p3Dsdv::generateRoutingTables() For: " << *it;
		std::cerr << std::endl;
#endif

		generateRoutingTable(*it, incremental);
	}


	/* now clear significant flag */
	clearSignificantChangesFlags();
	return 1;
}


int p3Dsdv::generateRoutingTable(const std::string &peerId, bool incremental)
{
	RsDsdvRouteItem *dsdv = new RsDsdvRouteItem();
	dsdv->PeerId(peerId);

	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		RsDsdvTableEntry &v = (it->second);

		/* discard/ignore criterion */
		if (!v.mIsStable)
		{
			continue;
		}

		if (v.mStableRoute.mDistance >= RSDSDV_MAX_DISTANCE) 
		{
			continue;
		}

		if (incremental)
		{
			if (v.mFlags & RSDSDV_FLAGS_SIGNIFICANT_CHANGE)
			{
				// Done elsewhere.
				//v.mFlags &= ~SIGNIFICANT_CHANGE;
			}
			else
			{
				/* ignore non-significant changes */
				continue;
			}
		}

		RsTlvDsdvEntry entry;

		entry.endPoint.idType = v.mDest.mIdType;
		entry.endPoint.anonChunk = v.mDest.mAnonChunk;
		entry.endPoint.serviceId = v.mDest.mHash;
		entry.sequence = v.mStableRoute.mSequence;
		entry.distance = v.mStableRoute.mDistance;

		dsdv->routes.entries.push_back(entry);

		if (dsdv->routes.entries.size() > RSDSDV_MAX_ROUTE_TABLE)
		{
			sendItem(dsdv);
			dsdv = new RsDsdvRouteItem();
			dsdv->PeerId(peerId);
		}
	}
	sendItem(dsdv);
	return 1;
}


/****************************************************************************
 ****************************************************************************/

int	p3Dsdv::processIncoming()
{
	/* for each packet - pass to specific handler */
	RsItem *item = NULL;
	while(NULL != (item = recvItem()))
	{
		switch(item->PacketSubType())
		{
			default:
				break;
			case RS_PKT_SUBTYPE_DSDV_ROUTE:
			{
				handleDSDV((RsDsdvRouteItem *) item);
			}
				break;
		}

		/* clean up */
		delete item;
	}
	return true ;
} 


int p3Dsdv::handleDSDV(RsDsdvRouteItem *dsdv)
{
	/* iterate over the entries */
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	time_t now = time(NULL);
	
#ifdef	DEBUG_DSDV
	std::cerr << "p3Dsdv::handleDSDV() Received Pkt from: " << dsdv->PeerId();
	std::cerr << std::endl;
	//dsdv->print(std::cerr);
	//std::cerr << std::endl;
#endif

	std::list<RsTlvDsdvEntry>::iterator it;
	for(it = dsdv->routes.entries.begin(); it != dsdv->routes.entries.end(); it++)
	{
		/* check for existing */
		RsTlvDsdvEntry &entry = *it;
		uint32_t realDistance = entry.distance + 1; // metric.

	
		/* find the entry */
		std::map<std::string, RsDsdvTableEntry>::iterator tit;
		tit = mTable.find(entry.endPoint.serviceId);
		if (tit == mTable.end())
		{
#ifdef	DEBUG_DSDV
			std::cerr << "p3Dsdv::handleDSDV() Adding Entry for New ServiceId: ";
			std::cerr << entry.endPoint.serviceId;
			std::cerr << std::endl;
#endif
			/* new entry! */
			RsDsdvTableEntry v;
			v.mDest.mIdType = entry.endPoint.idType;
			v.mDest.mAnonChunk = entry.endPoint.anonChunk;
			v.mDest.mHash = entry.endPoint.serviceId;

			/* add as a possible route */
			RsDsdvRoute newRoute;

			newRoute.mNextHop = dsdv->PeerId();
			newRoute.mReceived = now;
			newRoute.mSequence = entry.sequence;
			newRoute.mDistance = realDistance;
			newRoute.mValidSince = now;

			v.mAllRoutes[dsdv->PeerId()] = newRoute;
			v.mIsStable = false;

			v.mFlags = RSDSDV_FLAGS_NEW_ROUTE;
			v.mOwnSource = false;
			v.mMatched = false;

			// store in table.
			mTable[v.mDest.mHash] = v;
		}
		else
		{
			RsDsdvTableEntry &v = tit->second;
			if (v.mOwnSource)
			{
#ifdef	DEBUG_DSDV
				std::cerr << "p3Dsdv::handleDSDV() Ignoring OwnSource Entry:";
				std::cerr << entry.endPoint.serviceId;
				std::cerr << std::endl;
#endif
				continue; // Ignore if we are source.
			}
			
			/* look for this in mAllRoutes */
			std::map<std::string, RsDsdvRoute>::iterator rit;
			rit = v.mAllRoutes.find(dsdv->PeerId());
			if (rit == v.mAllRoutes.end())
			{
				/* add a new entry in */
				RsDsdvRoute newRoute;

				newRoute.mNextHop = dsdv->PeerId();
				newRoute.mReceived = now;
				newRoute.mSequence = entry.sequence;
				newRoute.mDistance = realDistance;
				newRoute.mValidSince = now;

				v.mAllRoutes[dsdv->PeerId()] = newRoute;

				/* if we've just added it in - can't be stable one */
#ifdef	DEBUG_DSDV
				std::cerr << "p3Dsdv::handleDSDV() Adding NewRoute Entry:";
				std::cerr << entry.endPoint.serviceId;
				std::cerr << std::endl;
#endif
			}
			else
			{
				if (rit->second.mSequence >= entry.sequence)
				{
#ifdef	DEBUG_DSDV
				std::cerr << "p3Dsdv::handleDSDV() Ignoring OLDSEQ Entry:";
				std::cerr << entry.endPoint.serviceId;
				std::cerr << std::endl;
#endif
					/* ignore same/old sequence number??? */
					continue;
				}
#ifdef	DEBUG_DSDV
				std::cerr << "p3Dsdv::handleDSDV() Updating Entry:";
				std::cerr << entry.endPoint.serviceId;
				std::cerr << std::endl;
#endif

				/* update seq,dist,etc */
				if (rit->second.mSequence + 2 < entry.sequence)
				{
					/* skipped a sequence number - reset timer */
					rit->second.mValidSince = now;
				}

				//rit->second.mNextHop; // unchanged.
				rit->second.mReceived = now;
				rit->second.mSequence = entry.sequence;
				rit->second.mDistance = realDistance;

				/* if consistent route... maintain */
				if ((v.mIsStable) &&
					(rit->second.mNextHop == v.mStableRoute.mNextHop))
				{
					v.mStableRoute = rit->second;
				}
				else
				{
					/* otherwise we need to wait - see if we get new update */
				}
			}
		}
	}
	return 1;
}


int p3Dsdv::selectStableRoutes()
{
	/* iterate over the entries */
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	time_t now = time(NULL);
	
#ifdef	DEBUG_DSDV
	std::cerr << "p3Dsdv::selectStableRoutes()";
	std::cerr << std::endl;
#endif

	/* find the entry */
	std::map<std::string, RsDsdvTableEntry>::iterator tit;
	for(tit = mTable.begin(); tit != mTable.end(); tit++)
	{
	
#ifdef	DEBUG_DSDV
		std::cerr << "p3Dsdv::selectStableRoutes() For Entry: ";
		std::cerr << tit->second;
		std::cerr << std::endl;
#endif
		if (tit->second.mOwnSource)
		{
#ifdef	DEBUG_DSDV
			std::cerr << "p3Dsdv::selectStableRoutes() OwnSource... Ignoring";
			std::cerr << std::endl;
#endif
			continue; // Ignore if we are source.
		}		
		
		std::map<std::string, RsDsdvRoute>::iterator rit;
		uint32_t newest = 0;
		std::string newestId;
		uint32_t closest = RSDSDV_MAX_DISTANCE + 1;
		std::string closestId;
		time_t closestAge = 0;

		/* find newest sequence number */
		for(rit = tit->second.mAllRoutes.begin(); 
			rit != tit->second.mAllRoutes.end(); rit++)
		{
			if ((now - rit->second.mReceived <= DSDV_DISCARD_PERIOD) &&
					(rit->second.mSequence >= newest))
			{
				newest = rit->second.mSequence;
				newestId = rit->first;

				/* also becomes default for closest (later) */
				closest = rit->second.mDistance;
				closestId = rit->first;
				closestAge = now - rit->second.mValidSince;
			}
		}

		if (closest >= RSDSDV_MAX_DISTANCE + 1)
		{
#ifdef	DEBUG_DSDV
			std::cerr << "\tNo Suitable Route";
			std::cerr << std::endl;
#endif
			tit->second.mIsStable = false;
			continue;
		}

		uint32_t currseq = newest - (newest % 2); // remove 'kill'=ODD Seq.
	
#ifdef	DEBUG_DSDV
		std::cerr << "\t Newest Seq: " << newest << " from: " << newestId;
		std::cerr << std::endl;
#endif

		/* find closest distance - with valid seq & max valid time */
		for(rit = tit->second.mAllRoutes.begin(); 
			rit != tit->second.mAllRoutes.end(); rit++)
		{
			/* Maximum difference in Sequence number is 2*DISTANCE
			 * Otherwise it must be old.
			 */
			if (rit->second.mSequence + rit->second.mDistance * 2 < currseq)
			{
#ifdef	DEBUG_DSDV
				std::cerr << "\t\tIgnoring OLD SEQ Entry: " << rit->first;
				std::cerr << std::endl;
#endif

				continue; // ignore.
			}

			/* if we haven't received an update in ages - old */
			if (now - rit->second.mReceived > DSDV_DISCARD_PERIOD)
			{
#ifdef	DEBUG_DSDV
				std::cerr << "\t\tIgnoring OLD TIME Entry: " << rit->first;
				std::cerr << std::endl;
#endif

				continue; // ignore.
			}

			if (rit->second.mDistance < closest)
			{
				closest = rit->second.mDistance;
				closestId = rit->first;
				closestAge = now - rit->second.mValidSince;
#ifdef	DEBUG_DSDV
				std::cerr << "\t\tUpdating to Closer Entry: " << rit->first;
				std::cerr << std::endl;
#endif
			}
			else if ((rit->second.mDistance == closest) &&
				(closestAge < now - rit->second.mValidSince))
			{
				/* have a more stable (older) one */
				closest = rit->second.mDistance;
				closestId = rit->first;
				closestAge = now - rit->second.mValidSince;
#ifdef	DEBUG_DSDV
				std::cerr << "\t\tUpdating to Stabler Entry: " << rit->first;
				std::cerr << std::endl;
#endif
			}
			else
			{
#ifdef	DEBUG_DSDV
				std::cerr << "\t\tIgnoring Distant Entry: " << rit->first;
				std::cerr << std::endl;
#endif
			}

		}

		tit->second.mIsStable = true;
		rit = tit->second.mAllRoutes.find(closestId);
		tit->second.mStableRoute = rit->second;
		tit->second.mFlags &= ~RSDSDV_FLAGS_NEW_ROUTE; 

#ifdef	DEBUG_DSDV
		std::cerr << "\tStable Route: " << tit->second.mStableRoute;
		std::cerr << std::endl;
#endif
	}
	return 1;
}




int p3Dsdv::clearOldRoutes()
{
	/* iterate over the entries */
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

#ifdef	DEBUG_DSDV
	std::cerr << "p3Dsdv::clearOldRoutes()";
	std::cerr << std::endl;
#endif

	/* find the entry */
	std::map<std::string, RsDsdvTableEntry>::iterator it, it2;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		if (it->second.mOwnSource)	
		{
			continue;
		}

		if (it->second.mIsStable)
		{
			continue;
		}

		if (it->second.mFlags & RSDSDV_FLAGS_NEW_ROUTE)
		{
			continue;
		}

		/* backstep iterator for loop, and delete original */
		it2 = it;
		it--;

#ifdef	DEBUG_DSDV
		std::cerr << "p3Dsdv::clearOldRoutes() Deleting OLD ServiceId: " << it2->first;
		std::cerr << std::endl;
#endif
		mTable.erase(it2);
	}
	return 1;
}


/*************** pqiMonitor callback ***********************/
void p3Dsdv::statusChange(const std::list<pqipeer> &plist)
{
	std::list<pqipeer>::const_iterator it;
	for(it = plist.begin(); it != plist.end(); it++)
	{
		/* only care about disconnected / not friends cases */
		if (	1	)
		{
			RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
	




		}
	}
}

int p3Dsdv::addTestService()
{
	RsDsdvId testId;

	int rndhash1[SHA_DIGEST_LENGTH / 4];
	int rndhash2[SHA_DIGEST_LENGTH / 4];
	std::string realHash;
	std::string seedHash;

	int i;
	for(i = 0; i < SHA_DIGEST_LENGTH / 4; i++)
	{
		rndhash1[i] = RSRandom::random_u32();
		rndhash2[i] = RSRandom::random_u32();
	}

	for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
	{
		rs_sprintf_append(realHash, "%02x", (uint32_t) ((uint8_t *) rndhash1)[i]);
		rs_sprintf_append(seedHash, "%02x", (uint32_t) ((uint8_t *) rndhash2)[i]);
	}


	uint8_t sha_hash[SHA_DIGEST_LENGTH];
	memset(sha_hash,0,SHA_DIGEST_LENGTH*sizeof(uint8_t)) ;
	SHA_CTX *sha_ctx = new SHA_CTX;
	SHA1_Init(sha_ctx);

	SHA1_Update(sha_ctx, realHash.c_str(), realHash.length());
	SHA1_Update(sha_ctx, seedHash.c_str(), seedHash.length());
	SHA1_Final(sha_hash, sha_ctx);
	delete sha_ctx;

	for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
	{
		rs_sprintf_append(testId.mHash, "%02x", (uint32_t) (sha_hash)[i]);
	}

	testId.mIdType = RSDSDV_IDTYPE_TEST;
	testId.mAnonChunk = seedHash;

	addDsdvId(&testId, realHash);
	return 1;
}


int p3Dsdv::addDsdvId(RsDsdvId *id, std::string realHash)
{

	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

#ifdef	DEBUG_DSDV
	std::cerr << "p3Dsdv::addDsdvId() ID: " << *id << " RealHash: " << realHash;
	std::cerr << std::endl;
#endif

	time_t now = time(NULL);
	
	/* check for duplicate */
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	it = mTable.find(id->mHash);
	if (it != mTable.end())
	{
		/* error */
		std::cerr << "p3Dsdv::addDsdvId() ERROR Duplicate ID";
		std::cerr << std::endl;

		return 0;
	}

	/* new entry! */
	RsDsdvTableEntry v;
	v.mDest = *id;

	v.mStableRoute.mNextHop = mLinkMgr->getOwnId();
	v.mStableRoute.mReceived = now;
	v.mStableRoute.mValidSince = now;
	v.mStableRoute.mSequence = 0;
	v.mStableRoute.mDistance = 0;
	v.mIsStable = true;

	v.mFlags = RSDSDV_FLAGS_OWN_SERVICE;
	v.mOwnSource = true;
	v.mMatched = true;
	v.mMatchedHash = realHash;

	// store in table.
	mTable[v.mDest.mHash] = v;

	return 1;
}




int p3Dsdv::dropDsdvId(RsDsdvId *id)
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

#ifdef	DEBUG_DSDV
	std::cerr << "p3Dsdv::dropDsdvId() ID: " << *id;
	std::cerr << std::endl;
#endif

	/* This should send out an infinity packet... and flag for deletion */

	std::map<std::string, RsDsdvTableEntry>::iterator it;
	it = mTable.find(id->mHash);
	if (it == mTable.end())
	{
		/* error */
		std::cerr << "p3Dsdv::addDsdvId() ERROR Unknown ID";
		std::cerr << std::endl;

		return 0;
	}

	mTable.erase(it);

	return 1;
}


int p3Dsdv::printDsdvTable(std::ostream &out)
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	/* iterate over the entries */
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		RsDsdvTableEntry &v = it->second;
		out << v;
		out << std::endl;
	}
	return 1;
}

/*****************************************/

uint32_t p3Dsdv::getLocalServices(std::list<std::string> &hashes)
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	/* iterate over the entries */
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		if (it->second.mOwnSource)
		{
			hashes.push_back(it->first);
		}
	}
	return 1;
}


uint32_t p3Dsdv::getAllServices(std::list<std::string> &hashes)
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	/* iterate over the entries */
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	for(it = mTable.begin(); it != mTable.end(); it++)
	{
		hashes.push_back(it->first);
	}
	return 1;
}


int p3Dsdv::getDsdvEntry(const std::string &hash, RsDsdvTableEntry &entry)
{
	RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/

	/* iterate over the entries */
	std::map<std::string, RsDsdvTableEntry>::iterator it;
	it = mTable.find(hash);
	if (it == mTable.end())
	{
		return 0;
	}

	entry = it->second;

	return 1;
}

std::ostream &operator<<(std::ostream &out, const RsDsdvId &id)
{
	out << "[Type: " << id.mIdType << " AMZ: " << id.mAnonChunk << " THASH: " << id.mHash;
	out << "]";

	return out;
}


std::ostream &operator<<(std::ostream &out, const RsDsdvRoute &route)
{
	time_t now = time(NULL);
	out << "< Seq: " << route.mSequence << " Dist: " << route.mDistance;
	out << " NextHop: " << route.mNextHop;
	out << " recvd: " << now-route.mReceived;
	out << " validSince: " << now-route.mValidSince;
	out << " >";

	return out;
}

std::ostream &operator<<(std::ostream &out, const RsDsdvTableEntry &entry)
{
	out << "DSDV Route for: " << entry.mDest << std::endl;
	if (entry.mIsStable)
	{
		out << "\tStable: " << entry.mStableRoute << std::endl;
	}
	else
	{
		out << "\tNo Stable Route" << std::endl;
	}

	out << "\tOwnSource: " << entry.mOwnSource;
	out << " Flags: " << entry.mFlags; 
	if (entry.mMatched)
	{
		out << " Matched: " << entry.mMatchedHash;
	}
	else
	{
		out << " Non Matched";
	}
	out  << std::endl;
	if (entry.mAllRoutes.size() > 0)
	{
		out << "\tAll Routes:" << std::endl;
	}

	std::map<std::string, RsDsdvRoute>::const_iterator it;
	for(it = entry.mAllRoutes.begin(); it != entry.mAllRoutes.end(); it++)
	{
		out << "\t\t" << it->second << std::endl;
	}
	return out;
}


