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

#ifndef ODEMX_PROTOCOL_SAP_INCLUDED
#define ODEMX_PROTOCOL_SAP_INCLUDED

#include <odemx/protocol/Pdu.h>
#include <odemx/synchronization/Port.h>

#include <deque>
#include <string>

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

namespace odemx {

namespace base { class Process; }

namespace protocol {

class ServiceProvider;

//---------------------------------------------------------- header declarations

/**
 * @brief Service access point (SAP) implementation for protocol service providers
 * @ingroup protocol
 * @author Ronald Kluth
 * @since 3.0
 * @see odemx::protocol::ServiceProvider odemx::protocol::Layer odemx::protocol::Stack
 *
 * In ODEMx, protocol services are modeled by ServiceProvider implementations.
 * SAPs provide a decoupled interface to the services offered by a protocol layer.
 * By offering access through the same interface, service providers can easily
 * be exchanged, allowing for top-down refinement by first using an abstract
 * Service implementation that may then be replaced by a more detailed Entity or
 * device. The communication of the application layer with the protocol stack
 * is also implemented with SAPs. Therefore, the protocol stack itself is
 * replaceable, for example by a simpler Service implementation.
 *
 * Sap is compatible with the wait-alert concept that is based on Memory
 * implementations. For this reason, class Sap has an internal buffer
 * that is derived from odemx::synchronization::PortHeadT.
 */
class Sap
{
public:

	/**
	 * @brief Internal buffer interface
	 *
	 * The SAP buffer is implemented using an odemx::synchronization::PortT. This class
	 * provides the read interface to the buffer. It is required in order
	 * to query the alerting PortHeadT for its owning SAP.
	 */
	class BufferHead: public synchronization::PortHeadT< PduPtr >
	{
	public:
		/// Smart pointer type to manage oject lifetime
		typedef std::tr1::shared_ptr< BufferHead > Ptr;

		/// Creation via static method to enfore shared_ptr usage
		static Ptr create( base::Simulation& sim, const data::Label& label, Sap& sap );

		/// SAP access
		Sap& getSap() const;

	private:
		/// Pointer to the owning SAP
		Sap* sap_;
	private:
		/// Construction private, use @c create instead
		BufferHead( base::Simulation& sim, const data::Label& label, Sap& sap );
	};

	/**
	 * @brief Construction
	 * @param sim The simulation context
	 * @param label The label of the owning service provider
	 * @param name The name to be used for accessing the SAP
	 *
	 * The constructor initializes the internal buffer, as well as its
	 * read and write interfaces (head and tail).
	 */
	Sap( base::Simulation& sim, const data::Label& label, const std::string& name );

	/// Destruction
	virtual ~Sap();

	/// Get the name of the SAP
	const std::string& getName() const;

	/**
	 * @brief Get a pointer to the buffer read interface
	 *
	 * This method is needed when SAPs are used in calls of Process::wait(),
	 * which expects a number of IMemory pointers to be passed to it. As soon
	 * as data becomes available at an SAP, the waiting Process is reactivated
	 * and can use the BufferHead to check which SAP awoke it.
	 */
	BufferHead* getBuffer() const;

	/**
	 * @brief Send interface for service users
	 * @param p The data unit to be written to the SAP
	 *
	 * This method is used to put data into the SAP's buffer. If there is
	 * a service provider waiting for input on this SAP, that process will
	 * immediately activated in order to consume the data.
	 */
	void write( PduPtr p );

	/**
	 * @brief Receive interface for service users
	 * @return The first data unit stored in the SAPs buffer
	 *
	 * This is a blocking call to the SAP's buffer. If there is no data
	 * available, the calling Process will be put into a wait state
	 * until @c write is called on the SAP.
	 */
	PduPtr read();

private:
	// The name must not be unique, i.e. no Label, because all ServiceProvider
	// objects expect to access SAPs by the same names, not unique ones.
	/// Name of the SAP for access by service providers
	std::string name_;
	/// Input buffer read interface
	BufferHead::Ptr queueHead_;
	/// Input buffer write interface
	BufferHead::TailPtr queueTail_;

public:
	// TODO: turn this into a comparator for std::set and replace SAP vectors with sets

	/// Functor for use with std algorithms to find SAPs by name
	struct MatchName
	{
		const std::string& name_;
		MatchName( const std::string& name ): name_( name ) {}
		bool operator()( Sap* sap ) const
		{
			return sap->getName() == name_;
		}
	};
};

} } // namespace odemx::protocol

#endif /* ODEMX_PROTOCOL_SAP_INCLUDED */
