//------------------------------------------------------------------------------
//	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.h
 * @author Ronald Kluth
 * @date created at 2009/10/12
 * @brief Declaration of class odemx::protocol::Service
 * @sa Service.cpp
 * @since 3.0
 */

#ifndef ODEMX_PROTOCOL_SERVICE_INCLUDED
#define ODEMX_PROTOCOL_SERVICE_INCLUDED

#include <odemx/protocol/ServiceProvider.h>
#include <odemx/protocol/Sap.h>

//----------------------------------------------------------forward declarations

namespace odemx {
namespace protocol {

class ErrorModel;
	
//-----------------------------------------------------------header declarations

/**
 * @brief Base class for modeling simple protocol services
 * @ingroup protocol
 * @author Ronald Kluth
 * @since 3.0
 * @see odemx::protocol::Sap odemx::protocol::Stack odemx::protocol::Layer odemx::protocol::ErrorModel
 *
 * A layer in the protocol stack usually offers a specific functionality to
 * its users as services. An access interface is provided through so-called
 * service access points (SAP). Users pass service primitives
 * (i.e. commands and data) to specific SAPs in order to make use of a
 * service. The functionality the service offers is implemented by service
 * providers, active objects within the layers of a stack. One such class is
 * Service.
 *
 * This class provides the basis for the implementation of a simple service.
 * Service is intended to be used inn one of two ways. It can either replace
 * a stack and provide a very simple communication layer for nodes, or it
 * can be used in the bottom layer of a protocol stack in order to simplify
 * the transmission aspect of the network model. All Service objects are
 * directly linked in order to abstract from the network topology.
 * All service objects in a simulation are associated with addresses upon
 * registration. Registering a service object means that a pointer to it
 * is stored in a map, and it can be accessed by providing the service address.
 * Subclasses need to define the pure virtual methods @c handleSend and
 * @c handleReceive in order to define the service behavior.
 *
 * Since Service objects may be used as a replacement for @c Stack objects,
 * they also provide the same functionality with regard to receive SAPs. Users
 * may create output SAPs they can use to listen for received data by calling
 * the method @c addReceiveSap.
 */
class Service
:	public ServiceProvider
{
public:
	/// Address type to identify users
	typedef std::string AddressType;
	/// Map type to associate user addresses with service objects
	typedef std::map< AddressType, Service* > AddressMap;

	/**
	 * @brief Construction
	 * @param sim The simulation context
	 * @param label The label of the object
	 *
	 * The constructor initializes an internal SAP that is needed for
	 * receiving data from peer service objects. Since services are service
	 * providers, they also just listen for input and wait. They must therefore
	 * be awakened by an internal SAP when data is received from a peer. This
	 * functionality is analogous to that of class Device.
	 */
	Service( base::Simulation& sim, const data::Label& label );

	/**
	 * @brief Destruction
	 *
	 * All receive SAPs that were added as listening interfaces by an
	 * application layer are automatically deleted.
	 */
	virtual ~Service();

	/**
	 * @name Service user registration and access
	 * @{
	 */
	/**
	 * @brief Register a user with the service in order to receive messages
	 * @param address The address to use for accessing the given service
	 * @param service The service object to be registered
	 * @return @c true if the map insertion was successful
	 *
	 * Service objects are always directly connected within a simulation.
	 * Therefore, they are all simply registered with a static map and can
	 * be accessed by their addresses.
	 */
	static bool registerService( const AddressType& address, Service& service );

	/**
	 * @brief Remove a service from the registration map to make a node unavailable
	 * @param address The address of the service to be removed from the map
	 * @return @c true of if the removal was successful
	 *
	 * Since the nodes of a network may dynamically enter and leave, this method
	 * provides a means to make Service peers unavailable by simply removing the
	 * map entries.
	 */
	static bool removeService( const AddressType& address );

	/// Get read access to the map holding all registered users
	static const AddressMap& getAddressMap();

	/**
	 * @brief Get a pointer to a registered Service object
	 * @param peer The address under which the peer service is registered
	 * @return Pointer to the peer object, or 0 if not found
	 *
	 * Since the service is an abstraction of the protocol stack or the
	 * transmission device, and not all configuration data may be contained
	 * in transmitted messages, it may sometimes be necessary to influence
	 * the state of a particular service object internally. This method
	 * provides the means to retrieve any registered service object.
	 */
	static Service* getPeer( const AddressType& peer );

	/**
	 * @brief Set the address of a Service object
	 *
	 * Normally, there is no need to use this method manually. Upon registration
	 * the service will automatically receive the address provided
	 * in the call to @c registerService.
	 */
	void setAddress( const AddressType& addr );

	/// Get the address of the service object
	const AddressType& getAddress() const;
	//@}

	/**
	 * @brief Add an SAP from which to receive data
	 * @param name The name of the SAP to be created
	 * @return A pointer to the SAP object
	 *
	 * When a Service class is used in place of a Stack, it must be usable
	 * in the same manner. Since stacks allow for the creation of listening
	 * SAPs, services do this as well.
	 *
	 * @note These objects are managed internally. There is no need
	 * call delete on them.
	 */
	Sap* addReceiveSap( const std::string& name );

	/// Get access to a receive SAP owned by the service
	Sap* getReceiveSap( const std::string& name );

	/**
	 * @brief Send a data unit to another registered Service object
	 * @param peer The service's address
	 * @param p The data unit to be transmitted
	 * @return @c true if the receiver was found in the map
	 *
	 * This method is used to directly send PDUs from one node to another.
	 * It looks up the peer in the registration map and calls @c receive
	 * on the service object. A warning will be issued
	 * if the peer is not found.
	 */
	bool send( const AddressType& peer, PduPtr p ) const;

	/**
	 * @brief Pass a message from the network to the upper layer
	 * @param sapName The SAP to which to pass the data unit
	 * @param p The data unit to pass on
	 *
	 * This method simply looks up the SAP in the upper layer, or the receive
	 * SAPs. It then writes the given data unit to it. A warning will be issued
	 * if the SAP is not found.
	 */
	bool pass( const std::string& sapName, PduPtr p );
	
	/**
	 * @brief Set the error model for the Service class
	 * @param em The error model to be used by the Service implementation
	 *
	 * Since this class is only a simple implementation of a transmission
	 * service, it can also only provide a simple means of modeling
	 * errors in the transmission activities.
	 *
	 * Only one ErrorModel implementation can be set per Service class
	 * because it is implemented with a static member. It is checked whenever
	 * a registered service object receives data from a peer. This way,
	 * PDUs can be filtered or altered during the transmission period.
	 */
	static void setErrorModel( std::tr1::shared_ptr< ErrorModel > em );
	
protected:
	/**
	 * @name Input handling, to be defined by subclasses
	 * @{
	 */
	/**
	 * @brief Implements the service functionality for sending data
	 * @param sapName The SAP used to input the data
	 * @param p The data unit to be transmitted to a peer
	 *
	 * Whenever data is provided by an SAP other than the internal receive
	 * SAP, this pure virtual method is called with the name of the SAP
	 * and the PDU to send. Every service must implement this method in
	 * order to specify what data is to be transmitted and how long the
	 * waiting period is. Usually, this method calls @c send.
	 */
	virtual void handleSend( const std::string& sapName, PduPtr p ) = 0;

	/**
	 * @brief Implement the service functionality for message reception
	 * @param p The data unit received from the medium
	 *
	 * This method gets called whenever data from a peer arrives through
	 * the internal receive SAP. The service must implement this method to
	 * define if and how data is to be passed to the upper layer or not.
	 * Usually, this method calls @c pass.
	 */
	virtual void handleReceive( PduPtr p ) = 0;

	/// Implementation of the service provider interface
	virtual void handleInput( const std::string& sap, PduPtr pdu );
	//@}

	/**
	 * @brief Put a data unit into the service's receive SAP
	 *
	 * This method should not be called manually. It is part of the interaction
	 * between peers. As such, it is called from with the method @c send.
	 */
	void receive( PduPtr p );

protected:
	/// Stores the address of a service object
	AddressType address_;
	/// Internal SAP for message reception from peers
	Sap* receiveSap_;
	/// Stores all registered output SAPs
	SapVec outputSaps_;
	/// Associates addresses of service users with pointers to their service objects
	static AddressMap services_;
	/// Manages a pointer to the error model, if one is set
	static std::tr1::shared_ptr< ErrorModel > errorModel_;
};

} } // namespace odemx::protocol

// how to implement global user storage for multiple service classes in the same simulation:
// make the static map dependent on the derived type of the service
// each derived type gets its own user storage generated by the compiler
// when the templates get instantiated
// service must be implemented via CRTP:
// class DerivedService: public ServiceT< DerivedService > {};
/*
template < typename > class ServiceUserStorage;

template < typename DerivedT >
class ServiceT
{
public: // service impl
	static ServiceUserStorage< DerivedT > userStorage_;
};

template < typename DerivedServiceT >
class ServiceUserStorage
{
public:
	typedef std::string AddressType;
	typedef std::map< AddressType, Service* > UserMap;
	static UserMap users_;
};

template < typename DerivedServiceT >
ServiceUserStorage< DerivedServiceT >::UserMap ServiceUserStorage< DerivedServiceT >::users_;
*/

#endif /* ODEMX_PROTOCOL_SERVICE_INCLUDED */
