//------------------------------------------------------------------------------
//	Copyright (C) 2009, 2010 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//
//	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
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser 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
//------------------------------------------------------------------------------

/**
 * @file Service.cpp
 * @author Ronald Kluth
 * @date created at 2009/10/12
 * @brief Implementation of class odemx::protocol::Service
 * @sa Service.h
 * @since 3.0
 */

#include <odemx/protocol/Service.h>
#include <odemx/protocol/ErrorModel.h>
#include <odemx/protocol/Layer.h>
#include <odemx/util/DeletePtr.h>

namespace odemx {
namespace protocol {

//------------------------------------------------------static member definition

Service::AddressMap Service::services_;
std::tr1::shared_ptr< ErrorModel > Service::errorModel_;

//------------------------------------------------------construction/destruction

Service::Service( base::Simulation& sim, const data::Label& label )
:	ServiceProvider( sim, label )
,	receiveSap_( 0 )
{
	receiveSap_ = addSap( "odemx_internal_service_receive_SAP" );
	assert( receiveSap_ );
}

Service::~Service()
{
	std::for_each( outputSaps_.begin(), outputSaps_.end(), DeletePtr< Sap >() );
}

//-------------------------------------------------------------user registration

bool Service::registerService( const AddressType& address, Service& service )
{
	service.setAddress( address );
	return services_.insert( AddressMap::value_type( address, &service ) ).second;
}

bool Service::removeService( const AddressType& address )
{
	AddressMap::iterator found = services_.find( address );
	if( found != services_.end() )
	{
		found->second->address_.clear();
		services_.erase( found );
		return true;
	}
	return false;
}

const Service::AddressMap& Service::getAddressMap()
{
	return services_;
}

const Service::AddressType& Service::getAddress() const
{
	return address_;
}

void Service::setAddress( const AddressType& addr )
{
	ODEMX_TRACE << log( "set address" )
			.detail( "value", addr )
			.scope( typeid(Service) );

	address_ = addr;
}

//--------------------------------------------------------service implementation

Service* Service::getPeer( const AddressType& peer )
{
	AddressMap::const_iterator found = services_.find( peer );
	if( found != services_.end() )
	{
		return found->second;
	}
	return 0;
}

bool Service::send( const AddressType& peerAddress, PduPtr p ) const
{
	Service* peer = getPeer( peerAddress );
	if( peer != 0 )
	{
		ODEMX_TRACE << log( "send" )
				.detail( "peer", peerAddress )
				.scope( typeid(Service) );

		peer->receive( p );
		return true;
	}
	else
	{
		warning << log( "Service::send(): peer not registered" )
				.detail( "address", peerAddress )
				.scope( typeid(Service) );
	}
	return false;
}

bool Service::pass( const std::string& sapName, PduPtr p )
{
	Sap* sap = 0;
	if( layer_ )
	{
		sap = layer_->getUpperLayer()->getSap( sapName );
	}
	else
	{
		sap = getReceiveSap( sapName );
	}

	if( sap )
	{
		ODEMX_TRACE << log( "pass" )
				.detail( "SAP", sapName )
				.scope( typeid(Service) );

		sap->write( p );
		return true;
	}
	else
	{
		warning << log( "Service::pass(): output SAP not found" )
				.detail( "name", sapName )
				.scope( typeid(Service) );
	}
	return false;
}

Sap* Service::addReceiveSap( const std::string& name )
{
	SapVec::iterator found = std::find_if(
			outputSaps_.begin(), outputSaps_.end(), Sap::MatchName( name ) );
	
	if( found != outputSaps_.end() )
	{
		warning << log( "Service::addReceiveSap(): SAP already exists" )
				.detail( "name", name )
				.scope( typeid(Service) );
		return *found;
	}

	ODEMX_TRACE << log( "add receive SAP" )
			.detail( "name", name )
			.scope( typeid(Service) );

	Sap* sap = new Sap( getSimulation(), getLabel(), name );
	outputSaps_.push_back( sap );
	return sap;
}

Sap* Service::getReceiveSap( const std::string& name )
{
	SapVec::iterator found = std::find_if(
			outputSaps_.begin(), outputSaps_.end(), Sap::MatchName( name ) );

	if( found != outputSaps_.end() )
	{
		return *found;
	}
	return 0;
}

void Service::receive( PduPtr p )
{
	bool pass = true;

	if( errorModel_ )
	{
		pass = errorModel_->apply( p );
	}

	if( pass )
	{
		ODEMX_TRACE << log( "receive success" ).scope( typeid(Service) );

		receiveSap_->write( p );
	}
	else
	{
		ODEMX_TRACE << log( "receive failure" ).scope( typeid(Service) );
	}
}

void Service::setErrorModel( std::tr1::shared_ptr< ErrorModel > em )
{
	errorModel_ = em;
}

//------------------------------------------------------------process life cycle

void Service::handleInput( const std::string& sap, PduPtr pdu )
{
	if( sap == receiveSap_->getName() )
	{
		ODEMX_TRACE << log( "handle receive" ).scope( typeid(Service) );

		handleReceive( pdu );
	}
	else
	{
		ODEMX_TRACE << log( "handle send" ).detail( "input SAP", sap )
						.scope( typeid(Service) );

		handleSend( sap, pdu );
	}
}

} } // namespace odemx::protocol
