//------------------------------------------------------------------------------
//	Copyright (C) 2002, 2003, 2004, 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_Process.cpp
 * @author Ralf Gerstenberger
 * @date created at 2002/08/26
 * @brief Example demonstrating basic ODEMx process simulation techniques
 * @since 1.0
*/

/**
 * @example Example_Process.cpp
 *
 * The basic simulation techniques required for ODEMx process simulations
 * are introduced.
 *
 * A process simulation contains a number of processes that describe
 * active elements in a model. A process contains a description of its behavior,
 * a sequence of actions. These actions are timeless. Time consumption is
 * realized with special time operations. In this example several of these
 * time operations are presented together with an example process.
 */

#include <odemx/base/Process.h>
#include <odemx/base/Simulation.h>
#include <odemx/base/DefaultSimulation.h>
using odemx::base::Process;
using odemx::base::Simulation;

#include <iostream>
using std::cout;
using std::endl;

//-------------------------------------------------------------------------Clock
//
// This example Process is a simple SimTime clock. It outputs a '*' every full
// tick in SimTime.
//
class Clock: public Process
{
public:
	//
	// We label all instances of this class 'Clock'. ODEMx ensures uniqueness
	// of labels on its own to prevent confusion. It will add a number to the
	// label if more than one Clock object is created in the same simulation
	// context.
	//
	// When we create any kind of Process object, we have to provide a reference
	// to the simulation context we want it to participate in. If the
	// constructor of Process is called without a simulation context, it will
	// use the default simulation context.
	//
	// Here, we choose to provide a simulation context.
	//
	Clock( Simulation& sim ): Process( sim, "Clock" ) {}
	//
	// The behavior of a user process must be defined by an implementation of
	// the pure virtual function 'int main()'. The return value of this
	// function is stored in the Process object and can be accessed via the
	// method 'getReturnValue()'. As long as 'main()' has not finished, the
	// return value is not valid. You can use the Process method 'hasReturned()'
	// to check this condition.
	//
	virtual int main()
	{
		//
		// Because our process has a recurring behavior pattern, we implement
		// its life cycle by using a loop.
		//
		while( true )
		{
			//
			// Since we are implementing a simple clock, we need a means to let
			// the SimTime pass. Here, we use the member function 'holdFor()',
			// which requires a time period as parameter. We could also use
			// 'activateIn()' instead. The difference between these two is seen
			// if there is more than one process scheduled at the same time.
			// 'holdFor()' would than schedule the current process in the last
			// position while 'activateIn()' would schedule the current process
			// as the first one at that particular time. Therefore, hold... and
			// activate... functions can be used to choose between fifo or lifo
			// scheduling.
			//
			holdFor( 1 );

			cout << " * ";
		}
		return 0;
	}
};

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

int main( int argc, char* argv[] )
{
	//
	// For this example we can use the DefaultSimulation. In more complex
	// applications it is recommended to provide a customized simulation class.
	//
	Simulation& sim = odemx::getDefaultSimulation();
	Clock clock( sim );
	//
	// The new process is just created at this point. It will not be executed
	// because it has not been activated yet. A process can be activated by any
	// of these methods:
	//
	// 'hold()'
	// 'holdFor()'
	// 'holdUntil()'
	// 'activate()'
	// 'activateIn()'
	// 'acitvateAt()'
	//
	// 'hold()' and 'activate()' are equal to 'holdFor( 0 )' and
	// 'activateIn( 0 )'. 'holdUntil( t )' and 'activateAt( t )'
	// are equal to 'holdFor( t - now )' and 'activateIn( t - now )'.
	//
	clock.activate();
	//
	// There are three ways to compute a simulation. Firstly, you can 'run()'
	// the simulation until it is finished. A simulation is finished if there
	// is no active process or event scheduled, or if it is stopped with
	// 'exitSimulation()'. Secondly, you can compute a simulation 'step()' by
	// step. Thirdly, you can run a simulation until a given SimTime is
	// reached with 'runUntil()'.
	//
	// Because Clock processes are running forever, we should not use
	// 'run()'. Instead we try both 'step()' and 'runUntil()'.
	//
	cout << "Basic Process Simulation Example\n";
	cout << "================================\n";

	for( int i = 1; i < 5; ++i )
	{
		sim.step();
		cout << "\n" << "Step " << i << ": time = " << sim.getTime() << "\n";
	}

	cout << "\n" << "continue until SimTime 6 is reached:";
	sim.runUntil( 6 );
	cout << "\n" << "time = " << sim.getTime() << "\n";

	cout << "================================" << endl;

	return 0;
}

//----------------------------------------------------------------program output
//
// Basic Process Simulation Example
// ================================
//
// Step 1: time = 0
//  *
// Step 2: time = 1
//  *
// Step 3: time = 2
//  *
// Step 4: time = 3
//
// continue until SimTime 6 is reached: *  *  *
// time = 6
// ================================
//
// Keep in mind that at step 1 our clock will not output a '*' because that is
// the first step until the 'holdFor( 1 )' statement is reached. Only after
// that, the clock starts ticking, so to speak.
//
