// [Licence]
// The Ariba-Underlay Copyright
//
// Copyright (c) 2008-2009, Institute of Telematics, Universität Karlsruhe (TH)
//
// Institute of Telematics
// Universität Karlsruhe (TH)
// Zirkel 2, 76128 Karlsruhe
// Germany
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE INSTITUTE OF TELEMATICS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARIBA PROJECT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation
// are those of the authors and should not be interpreted as representing
// official policies, either expressed or implied, of the Institute of
// Telematics.
// [Licence]

#include "OneHop.h"
#include "ariba/overlay/BaseOverlay.h"

namespace ariba {
namespace overlay {

use_logging_cpp( OneHop );

OneHop::OneHop(BaseOverlay& _baseoverlay, const NodeID& _nodeid, OverlayStructureEvents* _eventsReceiver)
	: 	OverlayInterface( _baseoverlay, _nodeid, _eventsReceiver ),
		state		( OneHopStateInvalid ),
		bootstrapLink	( LinkID::UNSPECIFIED ),
		pendingLinks	( 0 ) {

	//
	// insert us as the first node in the overlay
	//
	overlayNodes.insert( make_pair(_nodeid, LinkID::UNSPECIFIED) );
}

OneHop::~OneHop(){

	deleteOverlay();
}

const EndpointDescriptor& OneHop::resolveNode(const NodeID& node){

	OverlayNodeMapping::const_iterator i = overlayNodes.find( node );
	if (i == overlayNodes.end()) return EndpointDescriptor::UNSPECIFIED;

	const EndpointDescriptor& ep = baseoverlay.getEndpointDescriptor( i->second );

	logging_debug( "resolved node " << node.toString() << " to endpoint " << ep.toString() );
	return ep;
}

void OneHop::routeMessage(const NodeID& destnode, Message* msg){

	// in the fullmesh overlay we know every other node
	// so we also have a link to each other node

	logging_debug( "routing message to node " << destnode.toString() );

	OverlayNodeMapping::const_iterator i = overlayNodes.find( destnode );
	if (i == overlayNodes.end()) {
		logging_error( "not able to route message to node " << destnode.toString() );
		return;
	}

	baseoverlay.sendMessage( msg, i->second );
}

void OneHop::createOverlay(){

	// don't need to bootstrap against ourselfs.
	// the create and join process is completed now.

	logging_info( "creating onehop overlay structure" );
	state = OneHopStateCompleted;
}

void OneHop::deleteOverlay(){

	logging_info( "deleting onehop overlay structure" );
	state = OneHopStateInvalid;
	pendingLinks = 0;
}

OverlayInterface::NodeList OneHop::getKnownNodes() const {

	OverlayInterface::NodeList retlist;

	OverlayNodeMapping::const_iterator i = overlayNodes.begin();
	OverlayNodeMapping::const_iterator iend = overlayNodes.end();

	for( ; i != iend; i++ )
		retlist.push_back( i->first );

	return retlist;
}

void OneHop::joinOverlay(const EndpointDescriptor& bootstrapEp){

	logging_info( "joining onehop overlay structure through endpoint " <<
			(bootstrapEp == EndpointDescriptor::UNSPECIFIED ?
					"local" : bootstrapEp.toString()) );

	state = OneHopStateJoinInitiated;
	pendingLinks = 0;

	if( bootstrapEp == EndpointDescriptor::UNSPECIFIED ){

		// we are the initiator and we are to bootstrap against
		// ourselfs. in the onehop overlay this is not an issue
		// and we can just ignore this call.

		state = OneHopStateCompleted;


	} else {

		bootstrapLink = baseoverlay.establishLink( bootstrapEp,
					OverlayInterface::OVERLAY_SERVICE_ID );
	}
}

void OneHop::leaveOverlay(){

	logging_info( "leaving onehop overlay structure" );

	//
	// close all links, this will indicate the other nodes that we left
	//

	OverlayNodeMapping::iterator i = overlayNodes.begin();
	OverlayNodeMapping::iterator iend = overlayNodes.end();

	for( ; i != iend; i++){
		if( i->first != nodeid && i->second != LinkID::UNSPECIFIED )
			baseoverlay.dropLink( i->second );
	}

	state = OneHopStateInvalid;
	pendingLinks = 0;
}

void OneHop::onLinkDown( const LinkID& link, const NodeID& local, const NodeID& remote ){

	//
	// node went down, remove from overlay mapping
	//

	logging_debug( "node " << remote.toString() << " left overlay structure" );

	OverlayNodeMapping::iterator i = overlayNodes.begin();
	OverlayNodeMapping::iterator iend = overlayNodes.end();

	for( ; i != iend; i++ ){
		if( i->second == link ){
			overlayNodes.erase( i );
			break;
		}
	}
}

void OneHop::onLinkUp(const LinkID& link, const NodeID& local, const NodeID& remote) {

	//
	// as soon as a link goes up, we always request the node listing.
	// and try to get connections to as much nodes as possible in a greedy way.
	//

	if( link != bootstrapLink ){
		if( pendingLinks > 0 )  pendingLinks--;
		if( pendingLinks == 0 ) state = OneHopStateCompleted;
	}

	logging_debug( "link is up, sending out node listing request" );

	NodeListingRequest requestmsg;
	OneHopMessage onemsg( OneHopMessage::OneHopMessageTypeListingRequest );
	onemsg.encapsulate( &requestmsg );

	state = OneHopStateJoinListingRequested;
	baseoverlay.sendMessage( &onemsg, link );
}

bool OneHop::receiveMessage(const Message* message, const LinkID& link, const NodeID& node){

	OneHopMessage* onemsg = const_cast<Message*>(message)->decapsulate<OneHopMessage>();
	if( onemsg == NULL ) return false;

	//
	// handle node listing request
	//

	if( onemsg->isType( OneHopMessage::OneHopMessageTypeListingRequest) ){

		NodeListingRequest* request = onemsg->decapsulate<NodeListingRequest>();

		logging_info( "onehop received node listing request from node " << node.toString() );

		//
		// first, insert the nodes and the link into our mapping
		//

		overlayNodes.insert( make_pair(node, link) );

		//
		// send back a message with all nodes
		// and their current EndpointDescriptor
		//

		OneHopMessage onehopReply( OneHopMessage::OneHopMessageTypeListingReply );
		NodeListingReply listingReply;

		OverlayNodeMapping::iterator i = overlayNodes.begin();
		OverlayNodeMapping::iterator iend = overlayNodes.end();

		logging_debug( "sending out node listing reply with the following items" );

		for( ; i != iend; i++ ){

			const NodeID node = i->first;
			const LinkID link = i->second;
			const EndpointDescriptor& endpoint = baseoverlay.getEndpointDescriptor( link );

			logging_debug( "node: " + node.toString() + ", endp: " + endpoint.toString());
			listingReply.add( node, const_cast<EndpointDescriptor*>(new EndpointDescriptor(endpoint)) );
		}

		onehopReply.encapsulate( &listingReply );
		baseoverlay.sendMessage( &onehopReply, link );

		//
		// now that we know the node, we can tell the baseoverlay
		// that the node has joined our overlay structure
		//

		eventsReceiver->onNodeJoin( node );

	} // if( request != NULL )

	//
	// handle node listing reply
	//

	if( onemsg->isType( OneHopMessage::OneHopMessageTypeListingReply) ){

		NodeListingReply* reply = onemsg->decapsulate<NodeListingReply>();

		logging_debug( "received node listing reply from node " << node.toString()
					<< " with all overlay nodes. connecting to all of them" );

		//
		// get out all the EndpointDescriptors from the
		// overlay nodes and connect to all nodes where
		// we don't have a link yet
		//

		const NodeListingReply::NodeEndpointList& endpoints = reply->getList();
		logging_debug( "received " << endpoints.size() << " nodes in listing" );
		pendingLinks = 0;

		NodeListingReply::NodeEndpointList::const_iterator i = endpoints.begin();
		NodeListingReply::NodeEndpointList::const_iterator iend = endpoints.end();

		for( ; i != iend; i++ ){

			//
			// don't connect to nodes that we already have
			// a link to and don't connect to ourself
			//

			const NodeID& node = (*i).first;
			if( overlayNodes.find(node) != overlayNodes.end() ) continue;
			if( node == nodeid ) continue;

			logging_debug( "building up link to node in overlay " << node.toString() );
			const LinkID link = baseoverlay.establishLink( *((*i).second),
							OverlayInterface::OVERLAY_SERVICE_ID );

			overlayNodes.insert( make_pair(node, link) );
			pendingLinks++;

		} // for( ; i != iend; i++ )

	} // if( reply != NULL )

}

}} // namespace ariba, overlay
