#include "PingPong.h"

#include "ariba/interface/UnderlayAbstraction.h"

namespace ariba {
namespace appplication {
namespace pingpong {

use_logging_cpp(PingPong);
ServiceID PingPong::PINGPONG_ID = ServiceID(111);

PingPong::PingPong() : pingid( 0 ) {
}

PingPong::~PingPong(){
}

void PingPong::setMode(bool startingNode){
	startping = startingNode;
}

void PingPong::startup(){
	abstraction = new UnderlayAbstraction();

	logging_info( "starting up PingPong service ... " );

	SpoVNetID spovnetid (Identifier(5000));

	setMode( !Configuration::instance().read<bool>("GENERAL_Initiator") );

	NodeID nodeid = (Configuration::instance().exists("BASE_nodeid") ?
			NodeID(Identifier(Configuration::instance().read<unsigned long>("BASE_nodeid"))) :
			NodeID::UNSPECIFIED);

	IPv4Locator* locallocator = (Configuration::instance().exists("BASE_localLocator") ?
					&(IPv4Locator::fromString(Configuration::instance().read<string>("BASE_localLocator"))) :
					NULL);

	uint16_t localport = Configuration::instance().exists("BASE_port") ?
				Configuration::instance().read<uint16_t>("BASE_port") :
				ARIBA_DEFAULT_PORT;

	if( !startping ){

		context = abstraction->createSpoVNet(
			spovnetid, nodeid, locallocator, localport );

	} else {

		if( !Configuration::instance().exists("BASE_bootstrapIP")){
			logging_fatal( "no bootstrap address found" );
			return;
		}

		logging_info( "using bootstrap point " <<
			Configuration::instance().read<string>("BASE_bootstrapIP") );

		EndpointDescriptor bootstrap(
		new IPv4Locator(IPv4Locator::fromString(
			Configuration::instance().read<string>("BASE_bootstrapIP"))));

		context = abstraction->joinSpoVNet(
			spovnetid, bootstrap, nodeid, locallocator, localport );

	}

	overlay = &context->getOverlay();
	overlay->bind( this, PingPong::PINGPONG_ID );

	logging_info( "PingPong started up" );

	// trigger the creation of the pathload measurement module
	//PathloadMeasurement::instance( overlay );
}

void PingPong::shutdown(){
	logging_info( "shutting down PingPong service ..." );

	overlay->unbind( this, PingPong::PINGPONG_ID );
	Timer::stop();

	if( !startping ) abstraction->destroySpoVNet( context );
	else             abstraction->leaveSpoVNet( context );

	delete abstraction;

	logging_info( "PingPong service shut down" );
}

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

	PingPongMessage* incoming = ((Message*)message)->decapsulate<PingPongMessage>();

	logging_info( "received ping message on link " << link.toString() <<
				 " from node with id " << (int)incoming->getid());

// 	if( ((int)incoming->getid() % 5) == 0 )
// 		PathloadMeasurement::instance().measure( node, this );
}

void PingPong::eventFunction(){

	logging_info( "pinging our remote nodes" );

	RemoteNodes::iterator i = remoteNodes.begin();
	RemoteNodes::iterator iend = remoteNodes.end();

	pingid++;

	for( ; i != iend; i++ ){
		logging_info( "     -> pinging " << i->first );

		PingPongMessage pingmsg( pingid );
		overlay->sendMessage( &pingmsg, i->second );
	}
}

void PingPong::onMeasurement(NodeID node, double mbps){
	logging_info( "pingpong received measurement for node " << node.toString() << " with result " << mbps );
}

void PingPong::onJoinSuccess( const SpoVNetID& spovnetid ){
}

void PingPong::onOverlayCreate( const SpoVNetID& id ){
}

void PingPong::onOverlayDestroy( const SpoVNetID& id ){
}

bool PingPong::isJoinAllowed( const NodeID& nodeid, const SpoVNetID& spovnetid ){
	return true;
}

void PingPong::onNodeJoin( const NodeID& nodeid, const SpoVNetID& spovnetid ){

	if( !startping ){

		logging_info( "establishing link to node " << nodeid.toString() );
		const LinkID link = overlay->establishLink( nodeid, PingPong::PINGPONG_ID );

		logging_info( "adding node to registered nodes in pingpong: " << nodeid.toString() );
		remoteNodes.insert( make_pair(nodeid,link) );

		Timer::setInterval( 2000 );
		Timer::start();
	}
}

void PingPong::onNodeLeave( const NodeID& id, const SpoVNetID& spovnetid ){
	RemoteNodes::iterator i = remoteNodes.find( id );
	if( i != remoteNodes.end() ) remoteNodes.erase( i );
}

void PingPong::onJoinFail( const SpoVNetID& spovnetid ){
}

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

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

void PingPong::onLinkChanged( const LinkID& link, const NodeID& local, const NodeID& remote ){
}

void PingPong::onLinkFail(const LinkID& id, const NodeID& local, const NodeID& remote){
}

void PingPong::onLinkQoSChanged(const LinkID& id, const NodeID& local, const NodeID& remote , const QoSParameterSet& qos){
}

}}} // namespace ariba, appplication, pingpong
