//----------------------------------------------------------------------------
//	Copyright (C) 2007, 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 Example_Wait_Timer_Port.cpp

	\author Ronald Kluth

	\date created at 2007/10/01

	\brief A short usage example of Ports and Timers

	\since 2.0
*/

/**
 * @example Example_Wait_Timer_Port.cpp
 * The typical usage of the ODEMx process wait concept is demonstrated.
 *
 * A receiver process has both, a timer and a port. When a timeout occurs,
 * the receiver process finishes its run. A event is used to put three messages
 * into the receiver's input port, and while there is data, the receiver
 * reads it from the port. Eventually, it runs out of data and a timeout
 */

#include <iostream>
#include <string>
using namespace std;

#include <odemx/odemx.h>
using namespace odemx::base;
using namespace odemx::synchronization;

//---------------------------------------------------------------------------Msg
//
// Since we want to use the default ports provided by odemx, we need the
// port elements to be derived from PortData. Here, we define a simple
// message type that transports a string.
//
struct Msg:	public PortData
{
	Msg( const string& str ): content( str ) {}
	virtual Msg* clone() { return 0; }
	string content;
};

//----------------------------------------------------------------------Receiver
//
// The receiver of messages is a process that allows us to demonstrate the use
// of the concept wait-for-alert.
//
class Receiver: public Process
{
public:
	Receiver()
	:	Process( odemx::getDefaultSimulation(), "The Receiver" )
	,	timer_( new Timer( odemx::getDefaultSimulation(), "Receiver Message Timer" ) )
	,	port_( PortHead::create( odemx::getDefaultSimulation(), "Receiver Input Port" ) )
	{}
	//
	// The life cycle of this process uses wait() to stop its run until a
	// Memory object becomes available, i.e. the process gets alerted by it.
	// After checking who the alerter is, appropriate action is taken by either
	// logging the message and calling wait again, or by finishing its run if
	// a timeout occurs.
	//
	virtual int main()
	{
		//
		// Protect the receiver from deadlock by setting a timer.
		//
		SimTime timeLimit = 10;
		timer_->setIn( timeLimit );
		bool timeOut = false;

		while( ! timeOut )
		{
			info << log( "waiting for next message with timeout at" )
					.detail( "time", timer_->getExecutionTime() );
			//
			// Wait for message arrival at the port, or a timeout.
			//
			IMemory* who = wait( timer_.get(), port_.get() );
			//
			// When arriving at this point, the process was either alerted
			// by a Memory object or interrupted by some other object.
			//
			if( who == timer_.get() )
			{
				//
				// Timeout, stop message reception loop.
				//
				info << log( "alerted by timer" ).detail( "timeout at", getTime() );
				timeOut = true;
			}
			else if( who == port_.get() )
			{
				info << log( "alerted by port" );
				//
				// Restart the timer for the next waiting period.
				//
				timer_->reset( timeLimit );
				//
				// PortHead::get() return an auto_ptr wrapping the actual
				// pointer to the message. The returned data can be 0, if an
				// error occurred.
				//
				std::auto_ptr< PortData* > data = port_->get();
				if( data.get() != 0 )
				{
					//
					// We need to manage memory allocated by MessageArrival event
					//
					std::auto_ptr< Msg > msg( static_cast< Msg* >( *data ) );
					info << log( "received message" ).detail( "content", msg->content );
				}
				else
				{
					error << log( "PortHead::get() returned 0" );
				}
			}
		}
		info << log( "terminated at" ).detail( "time", getTime() );
		return 0;
	}

	PortHead::TailPtr getInputPort() { return port_->getTail(); }

private:
	std::auto_ptr< Timer > timer_;
	PortHead::Ptr port_;
};

//----------------------------------------------------------------MessageArrival
//
// The arrival of messages is modeled by a simple event class that puts
// messages into a receiver's input port.
//
class MessageArrival: public Event
{
public:
	MessageArrival( Receiver& receiver )
	:	Event( odemx::getDefaultSimulation(), "Message Arrival" )
	,	receiver_( &receiver )
	{}
	//
	// The event action simply models the arrival of three messages at a port
	//
	virtual void eventAction()
	{
		info << log( "triggered for" ).detail( "receiver", receiver_->getLabel() );
		receiver_->getInputPort()->put( new Msg( "message 1" ) );
		receiver_->getInputPort()->put( new Msg( "message 2" ) );
		receiver_->getInputPort()->put( new Msg( "message 3" ) );
	}

private:
	Receiver* receiver_;
};

//--------------------------------------------------------------------------main

int main( int argc, char* argv[] )
{
	using namespace odemx::data::output;

	SimTime arrivalTime = 8;
	Simulation& sim = odemx::getDefaultSimulation();

	Receiver receiver;
	MessageArrival arrival( receiver );
	arrival.scheduleAt( arrivalTime );
	receiver.activate();

	sim.addConsumer( odemx::data::channel_id::info, OStreamWriter::create( std::cout ) );
	sim.run();
	return 0;
}

//----------------------------------------------------------------program output
/*
ODEMx Log
=========
0 info: The Receiver(Receiver) waiting for next message with timeout at [Details: # time=10]
8 info: Message Arrival(MessageArrival) triggered for [Details: # receiver=The Receiver]
8 info: The Receiver(Receiver) alerted by port
8 info: The Receiver(Receiver) received message [Details: # content=message 1]
8 info: The Receiver(Receiver) waiting for next message with timeout at [Details: # time=18]
8 info: The Receiver(Receiver) alerted by port
8 info: The Receiver(Receiver) received message [Details: # content=message 2]
8 info: The Receiver(Receiver) waiting for next message with timeout at [Details: # time=18]
8 info: The Receiver(Receiver) alerted by port
8 info: The Receiver(Receiver) received message [Details: # content=message 3]
8 info: The Receiver(Receiver) waiting for next message with timeout at [Details: # time=18]
18 info: The Receiver(Receiver) alerted by timer [Details: # timeout at=18]
18 info: The Receiver(Receiver) terminated at [Details: # time=18]
*/
