// [License]
// 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.
// [License]

#include "AribaModule.h"

// boost/std includes
#include <assert.h>
#include <boost/regex.hpp>
#include <boost/foreach.hpp>

// ariba includes
#include "ariba/SideportListener.h"
#include "ariba/utility/misc/Helper.h"
#include "ariba/utility/misc/StringFormat.h"
#include "ariba/communication/BaseCommunication.h"
#include "ariba/communication/EndpointDescriptor.h"

using namespace ariba::utility::Helper;
using ariba::communication::BaseCommunication;
using ariba::communication::EndpointDescriptor;

namespace ariba {

use_logging_cpp(AribaModule);

AribaModule::AribaModule()
	: base_comm(NULL), sideport_sniffer(NULL), started(false) {

	endpoints = "tcp{41322};rfcomm{10};";

	//srand( time(NULL) );
	//uint16_t tcpport = (rand() + 1024) % 50000;
	//uint16_t udpport = (rand() + 1024) % 50000;
}

AribaModule::~AribaModule() {
}

string AribaModule::getBootstrapHints(const Name& spoVNetName) const {
	std::ostringstream o;
	BOOST_FOREACH( const BootstrapInfo& info, bootstrapNodes ) {
		o << info.spovnetName.toString() << "{";
		int i=0;
		BOOST_FOREACH( const BootstrapNode& node, info.nodes ) {
			if (i!=0) o << ",";
			if( node.desc != NULL ) o << node.desc->toString();
			i++;
		}
		o << "}";
	}
	return o.str();
}

void AribaModule::addBootstrapHints(string boot_info) {
	using namespace boost::xpressive;
	using namespace ariba::utility::string_format;
	using namespace ariba::utility::Helper;
	using namespace std;

	smatch match;
	if (regex_search(boot_info, match, robjects)) {
		regex_nav nav = match;
		for (int i = 0; i < nav.size(); i++) {
			string type = nav[i][robject_id].str();
			string data = nav[i][robject_data].str();
			data = data.substr(1, data.size() - 2);
			Name name(type);
			EndpointDescriptor* desc = EndpointDescriptor::fromString(data);
			logging_debug("Added bootstap info for " << type << ": " << desc->toString() );
			addBootstrapNode(name, desc);
		}
	}
}

void AribaModule::addBootstrapNode(const Name& spovnet,
		communication::EndpointDescriptor* desc) {

	// set bootstrap node
	BootstrapNode node;
	node.timestamp = 0;
	node.desc = desc;
	bool added = false;

	// add node to existing bootstrap list
	BOOST_FOREACH( BootstrapInfo& info, bootstrapNodes ){
		if (info.spovnetName == spovnet) {
			info.nodes.push_back(node);
			added = true;
			break;
		}
	}

	// create new entry
	if (!added) {
		BootstrapInfo info;
		info.spovnetName = spovnet;
		info.nodes.push_back(node);
		bootstrapNodes.push_back(info);
	}

	logging_debug( "Added bootstrap info: " << getBootstrapHints() );
}

const communication::EndpointDescriptor* AribaModule::getBootstrapNode(
		const Name& spovnet) const {
	BOOST_FOREACH( const BootstrapInfo& info, bootstrapNodes ) {
		if( info.spovnetName == spovnet ) {
			BOOST_FOREACH( const BootstrapNode& node, info.nodes ) {
				if( node.desc != NULL ) return node.desc;
			}
		}
	}
	return NULL;
}

void AribaModule::registerSideportListener(SideportListener* sideport){
	sideport_sniffer = sideport;
}

// @see Module.h
void AribaModule::initialize() {

	// preconditions
	assert(!started);

	// init variables
	base_comm = NULL;
}

// @see Module.h
void AribaModule::start() {

	// preconditions
	assert(base_comm == NULL);
	assert(!started);

	// create the base communication component
	started = true;
	base_comm = new BaseCommunication();
	base_comm->setEndpoints(endpoints);
}

// @see Module.h
void AribaModule::stop() {

	// preconditions
	assert(base_comm != NULL);
	assert(started);

	// delete base communication component
	started = false;
	delete base_comm;
}

// @see Module.h
string AribaModule::getName() const {
	return "ariba";
}

// @see Module.h
void AribaModule::setProperty(string key, string value) {
	if (key == "endpoints") endpoints = value;
	else if (key == "bootstrap.hints") addBootstrapHints(value);
}

// @see Module.h
const string AribaModule::getProperty(string key) const {
	if (key == "endpoints") return endpoints; // TODO: return local endpoints
	else if (key == "bootstrap.hints") return getBootstrapHints();
}

// @see Module.h
const vector<string> AribaModule::getProperties() const {
	vector<string> properties;
	properties.push_back("endpoints");
	properties.push_back("bootstrap.hints");
	return properties;
}

} // namespace ariba
