//------------------------------------------------------------------------------
//	Copyright (C) 2007, 2008, 2009 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 PortImpl.h
 * @author Ronald Kluth
 * @date created at 2007/04/04
 * @brief Declaration and Implementation of odemx::synchronization::PortT
 * @see PortHeadImpl.h, PortTailImpl.h
 * @since 2.0
 */

#ifndef ODEMX_SYNC_PORT_IMPL_INCLUDED
#define ODEMX_SYNC_PORT_IMPL_INCLUDED

#include <odemx/data/Producer.h>
#include <odemx/util/TypeToString.h>

#include <list>

#ifdef _MSC_VER
#include <memory>
#else
#include <tr1/memory>
#endif


namespace odemx {

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

namespace base { class Simulation; }

namespace synchronization {

template < typename > class PortHeadT;
template < typename > class PortTailT;

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

/**
 * @ingroup synch
 * @author Ronald Kluth
 * @brief %PortT is implements the internal buffer for PortHeadT and PortTailT
 * @see PortHeadT, PortTailT
 *
 * This internal class manages the limited buffer that is the base
 * for a communications port. It maintains references to its port tail,
 * which describes the input interface, and its port head, which is the
 * output interface of a port.
 *
 * @note In order to use and access ports, you need to create either a
 * PortHeadT or PortTailT object, which, respectively, provide the read
 * and write interface to the buffer.
 *
 * @since 2.0
 */
template < typename ElementT >
class PortT
:	public data::Producer
{
public:
	/// Destruction
	virtual ~PortT()
	{
		if( ! buffer_.empty() )
		{
			warning << log( "PortT::~PortT(): port not empty" )
					.scope( typeid( PortT< ElementType > ) );
		}
	}

	/// Type of the buffer elements
	typedef ElementT ElementType;
	/// Storage type for the buffer
	typedef std::list< ElementType > StorageType;
	/// Storage type for the buffer
	typedef typename StorageType::size_type SizeType;
	/// Head pointer type
	typedef std::tr1::weak_ptr< PortHeadT< ElementType > > HeadPtr;
	/// Tail pointer type
	typedef std::tr1::weak_ptr< PortTailT< ElementType > > TailPtr;

private:
	friend class PortHeadT< ElementType >;
	friend class PortTailT< ElementType >;

	/// Construction, used only by head / tail
	PortT( base::Simulation& sim, const data::Label& label, SizeType limit )
	:	data::Producer( sim, label )
	,	maxCapacity_( limit )
	,	elementCount_( 0 )
	{
		// check for invalid port capacity
		if( limit == 0 )
		{
			error << log( "PortT(): attempt to create Port with invalid capacity, using default" )
					.scope( typeid( PortT< ElementType > ) );

			maxCapacity_ = defaultLimit;
		}

		statistics << param( "element type", odemx::typeToString( typeid( ElementType ) ) )
				.scope( typeid( PortT< ElementType > ) );
		statistics << param( "maximum capacity", maxCapacity_ )
				.scope( typeid( PortT< ElementType > ) );
	}

	/// Append an element to the buffer
	void put( const ElementType& element )
	{
		buffer_.push_back( element );
		++elementCount_;

		statistics << count( "put calls" ).scope( typeid( PortT< ElementType > ) );
		statistics << update( "buffer size", elementCount_ )
				.scope( typeid( PortT< ElementType > ) );
	}

	/// Get the first element from the buffer (and remove it)
	std::auto_ptr< ElementType > get()
	{
		std::auto_ptr< ElementType > rv( new ElementType( buffer_.front() ) );
		buffer_.pop_front();
		--elementCount_;

		statistics << count( "get calls" ).scope( typeid( PortT< ElementType > ) );
		statistics << update( "buffer size", elementCount_ )
				.scope( typeid( PortT< ElementType > ) );
		return rv;
	}
  
	/// Get a specific element from the buffer (and remove it)
	std::auto_ptr< ElementType > get(typename StorageType::iterator it)
	{
		std::auto_ptr< ElementType > rv( new ElementType( *it ) );
		buffer_.erase (it);
		--elementCount_;

		statistics << count( "get calls" ).scope( typeid( PortT< ElementType > ) );
		statistics << update( "buffer size", elementCount_ )
				.scope( typeid( PortT< ElementType > ) );
		return rv;
	}

	/// Put an element back at the front of the buffer
  /// TODO doesn't work, if we can take an arbitrary element
  /// XXX nochmal darüber nachdenken (generell)
	void unGet( const ElementType& element )
	{
		buffer_.push_front( element );
		++elementCount_;

		statistics << count( "unGet calls" ).scope( typeid( PortT< ElementType > ) );
		statistics << update( "buffer size", elementCount_ )
				.scope( typeid( PortT< ElementType > ) );
	}

  /// XXX get an iterator to the contained  buffer
  typename StorageType::iterator begin () {
    return buffer_.begin ();
  }

  /// XXX
  typename StorageType::iterator end () {
    return buffer_.end ();
  }

  /// TODO const? Reference?
  /// TODO note: no error checks! we assume the user isn't dumb
  typename StorageType::iterator inspectLastElement () {
    return --(buffer_.end ());
  }


	/// Check whether the buffer contains elements
	bool isEmpty() const
	{
		return buffer_.empty();
	}

	/// Check whether the internal buffer is already full
	bool isFull() const
	{
		return elementCount_ >= maxCapacity_;
	}

	/// Check if a head exists
	bool hasHead() const
	{
		return ! head_.expired();
	}

	/// Set a new head for this port
	void setHead( HeadPtr head )
	{
		head_ = head;
	}

	/// Get a pointer to the port's head, may be empty
	std::tr1::shared_ptr< PortHeadT< ElementType > > getHead() const
	{
		return head_.lock();
	}

	/// Check if a tail exists
	bool hasTail() const
	{
		return ! tail_.expired();
	}

	/// Set a new tail for the port
	void setTail( TailPtr tail )
	{
		tail_ = tail;
	}

	/// Get a pointer to the port's tail, may be empty
	std::tr1::shared_ptr< PortTailT< ElementType > > getTail() const
	{
		return tail_.lock();
	}

	/// Change the maximum capacity of the internal buffer
	void setCapacity( SizeType limit )
	{
		maxCapacity_ = limit;
	}

	/// Get the maximum allowed capacity of the internal buffer
	SizeType getCapacity() const
	{
		return maxCapacity_;
	}

	/// Check the number of elements currently stored in the buffer
	SizeType getElementCount() const
	{
		return elementCount_;
	}

	/// Move items from @c buffer to this port's storage buffer
	void spliceBuffer( StorageType& buffer, bool append )
	{
		elementCount_ += buffer.size();
		if( append )
		{
			buffer_.splice( buffer_.end(), buffer );
		}
		else
		{
			buffer_.splice( buffer_.begin(), buffer );
		}
		statistics << update( "buffer size", elementCount_ )
				.scope( typeid( PortT< ElementType > ) );
	}

	/// Get the storage buffer, allows write access for splicing
	StorageType& getBuffer()
	{
		return buffer_;
	}

private:
	SizeType maxCapacity_; ///< maximum buffer capacity
	SizeType elementCount_; ///< current number of elements in the buffer
	StorageType buffer_; ///< internal buffer for elements of type ElementType
	HeadPtr head_; ///< corresponding Head interface
	TailPtr tail_; ///< corresponding Tail interface
	/// Default value for maximum port capacity
	static const SizeType defaultLimit = 10000;
};

} } // namespace odemx::synchronization

#endif /* ODEMX_SYNC_PORT_IMPL_INCLUDED */
