//----------------------------------------------------------------------------
//	Copyright (C) 2002, 2004 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_Parallel_Simulation.cpp

	\author Ralf Gerstenberger

	\date created at 2002/12/14

	\brief Example for multiple (parallel) simulations

	This example shows the techniques for realising
	multiple (parallel) simulations in one program.

	\since 1.0
*/
/** \example Example_Parallel_Simulation.cpp
	Multiple (parallel) simulations in one program are
	introduced.

	As shown in the example for simulations inside simulations, it is
	possible to have multiple simulations in one program. These simulations
	can be computed one after another or 'parallel'. In this example both
	situations are presented.
*/

#include <iostream>
using namespace std;

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

//
// In this example we demonstrate multiple parallel simulations. We have
// two simulations (SimA and SimB) which are at first computed one after
// another and then parallel.
//
// SimA contains two processes (Master and Slave) which cooperate through
// an instance of WaitQ. Master uses the function 'coopt()' which returns a
// waiting slave. Slave calls the function 'wait()' to show its readiness.
// Both functions block the execution of the active process if an immediate
// interaction between a slave and a master is not possible.
//
class SimA : public Simulation {
	// process types of SimA
	class Master : public Process {
	public:
		Master(SimA& sim)
			: Process(sim, "Master") {}

	protected:
		virtual int main() {
			SimA& sim = dynamic_cast<SimA&>( this->getSimulation());
			Slave* s=0;

			cout << getLabel() << " coopt" << endl;
			s = dynamic_cast<Slave*>(sim.getQueue()->coopt());

			//
			// The function 'activateAfter()' causes the activation
			// of a process after another.
			//
			s->activateAfter(this);

			cout << getLabel() << " coopt finished" << endl;

			return 0;
		}
	};

	class Slave : public Process {
	public:
		Slave(SimA& sim)
			: Process(sim, "Slave") {}

	protected:
		virtual int main() {
			SimA& sim = dynamic_cast<SimA&>(getSimulation());

			cout << getLabel() << " wait" << endl;
			sim.getQueue()->wait();
			cout << getLabel() << " wait finished" << endl;

			return 0;
		}
	};

	Master* m;
	Slave* s;
	WaitQ* q;

public:
	SimA(SimulationObserver* o = 0)
		: Simulation("SimA", o), m(0), s(0), q(0) {}

	~SimA() {
		delete m;
		delete s;
		delete q;
	}

	WaitQ* getQueue() {return q;}

protected:
	virtual void initSimulation() {
		m = new Master(*this);
		s = new Slave(*this);
		q = new WaitQ(*this, "queue");

		m->activate();
		s->activate();
	}
};

//
// SimB contains a process called Loop which 10 times writes
// its label and holds for 1 time unit.
//
class SimB : public Simulation {
	// process types of SimA
	class Loop : public Process {
	public:
		Loop(SimB& sim, ProcessObserver* o = 0)
			: Process(sim, "Loop", o) {}

	protected:
		virtual int main() {
			for (int i= 0; i<10; ++i) {
				cout << getLabel() << i << endl;
				holdFor(1);
			}

			return 0;
		}
	};

public:
	SimB(SimulationObserver* o = 0)
		: Simulation("SimB", o), l(0) {}

	~SimB() {
		delete l;
	}

	Loop* l;

protected:
	virtual void initSimulation() {
		l=new Loop(*this);

		l->activate();
	}
};

//
// The main function runs at first a simulation of type SimA,
// than a simulation of type SimB and finally a simulation of
// type SimA parallel to a simulation of type SimB (step by step).
//
int main(int argc, char* argv[]) {

	//
	// Run simulation of type SimA
	//
	cout << "Part 1" << endl;
	for (int i=0; i<5; ++i) {
		SimA s;
		s.run();
	}
	cout << "Simulation A has run 5 times.";

	//
	// Run simulation of type SimB
	//
	cout << endl << "Part 2" << endl;
	{
		SimB s;
		s.run();
	}
	cout << "Simulation B has run once.";

	//
	// Run simulations of type SimA and type SimB
	//
	cout << endl << "Part 3 -- Running two interleaved simulations" << endl;
	{
		SimA s1;
		SimB s2;

		s1.step();
		s2.step();

		while ( !(s1.isFinished() && s2.isFinished()) ) {

			if (!s1.isFinished())
				s1.step();

			if (!s2.isFinished())
				s2.step();
		}
	}

	cout << endl << "Finished" << endl;
	return 0;
}

