/*******************************************************************
*
*  DESCRIPTION: class StandAloneLoader
*
*  AUTHOR:    Amir Barylko & Jorge Beyoglonian 
*  Version 2: Daniel A. Rodriguez.
*
*  EMAIL: mailto://amir@dc.uba.ar
*         mailto://jbeyoglo@dc.uba.ar
*         mailto://drodrigu@dc.uba.ar
*
*  DATE: 27/6/1998
*  DATE: 29/4/1999
*
*******************************************************************/

/** include files **/
#include <fstream>
#include <strstream>     // class strstream
#include <time.h>
#include <math.h>
#include "stdaload.h"      // base class
#include "realprec.h"
#include "impresion.h"
#include "distri.h"

extern "C"
{
	#include "randlib.h"
}

#define	CTE_STR_W	"The format for the -w parameter is: -wXX-YY where XX and YY are integer numbers."

int getWidth( const char *str )
{
	char	aux[100];
	int	i = 0;

	while (*str != 0 && *str != '-')
	{
		aux[i++] = *str;
		str++;
	}
	MASSERTMSG(*str == '-', CTE_STR_W );

	aux[i] = 0;
	return atoi(aux);
}

int getPrecision( const char *str )
{
	char	aux[100];
	int	i = 0;

	while (*str != 0 && *str != '-')
		str++;

	MASSERTMSG(*str == '-', CTE_STR_W );
	str++;

	while (*str != 0)
	{
		aux[i++] = *str;
		str++;
	}
	aux[i] = 0;
	return atoi(aux);
}

/*******************************************************************
* Function Name: StandAloneLoader
* Description: Constructor
********************************************************************/
StandAloneLoader::StandAloneLoader( int argc, const char *argv[] )
: iniName( "model.ma" )
, outName( "/dev/null" )
, logName( "/dev/null" )
, evName( "" )
, evalDebugName( "/dev/null" )
, flatDebugName( "/dev/null" )
, parserFileName( "stdout" )
{
	stopTime( Time::Inf ) ;
	printParserInfo(false);
	preprocessor = 1;
	DebugCellRules().Active( false );
	UseQuantum().Active( false );

	Impresion::Default.Width( 12 );
	Impresion::Default.Precision( 5 );

	//////////////////////////////////////////////////////////////////////
	// Set the seed for the random number generator with the clock's value
	string	str;
	char	c;
	long	seed1, seed2;
	
	time_t seconds;
	time (&seconds);
	srand(seconds);
	for (register int i = 1; i <= 20; i++)
	{
		c = 50 + static_cast<int>( 50 * rand() / (RAND_MAX + 1) );
		str += c;
	}
	phrtsd( const_cast< char *> ( str.c_str() ), &seed1, &seed2);
	setall(seed1, seed2);
	/////////////////////////////////////////////////////////////////////	

	while( --argc )
	{
		if( argv[ argc ][ 0 ] == '-' )
		{
			switch( argv[ argc ][ 1 ] )
			{
				case 'm': // file de modelos a cargar
					iniName = argv[ argc ] + 2 ;
					break;

				case 't': // hora de finalizacion de la simulacion
					stopTime( Time( argv[ argc ] + 2 ) ) ;
					break;

				case 'l': // log name
					logName = argv[ argc ] + 2 ;
					break;

				case 'e': // event file
					evName = argv[ argc ] + 2 ;
					break;

				case 'o': // output stream
					outName = argv[ argc ] + 2 ;
					break;

				case 'v': // evaluate debug mode
					evalDebugName = argv[ argc ] + 2 ;
					break;

				case 'f': // evaluate debug mode
					flatDebugName = argv[ argc ] + 2 ;
					break;

				case 'h': // help
					cout << argv[ 0 ] << " [-ehlmotdpvbfrsqw]" << endl
					<< "\t" << "e: events file (default: none)" << endl
					<< "\t" << "h: show this help" << endl 
					<< "\t" << "l: message log file (default: /dev/null)" << endl
					<< "\t" << "m: model file (default : model.ma)" << endl
					<< "\t" << "o: output (default: /dev/null)" << endl
					<< "\t" << "t: stop time (default: Infinity)" << endl
					<< "\t" << "d: set tolerance used to compare real numbers" << endl
					<< "\t" << "p: print extra info when the parsing occurs (only for cells models)" << endl
					<< "\t" << "v: evaluate debug mode (only for cells models)" << endl
					<< "\t" << "b: bypass the preprocessor (macros are ignored)" << endl
					<< "\t" << "f: flat debug mode (only for flat cells models)" << endl
					<< "\t" << "r: debug cell rules mode (only for cells models)" << endl
					<< "\t" << "s: show the virtual time when the simulation ends (on stderr)" << endl
					<< "\t" << "q: use quantum to calculate cells values" << endl
					<< "\t" << "w: sets the width and precision (with form xx-yy) to show numbers" << endl;

					exit(0);
					break;

				case 'p': // Print extra parsing info
					printParserInfo(true);
					parserFileName = argv[ argc ] + 2;
					ParserDebug().Active( true );
					break;

				case 'r': // Debug Cells rules
					DebugCellRules().Active( true );
					break;

				case 's':  // Show virtual time when simulation ends
					ShowVirtualTimeWhenFinish().Active( true );
					break;

				case 'b': // bypass the preprocessor
					preprocessor = 0;
					break;

				case 'd': // Set the value of TOL
					RealPrecision::Tol.precision( atof( argv[argc] + 2 ) );
					break;

				case 'w': // set the width & precision to show real numbers
					Impresion::Default.Width( getWidth( argv[ argc ] + 2 ));
					Impresion::Default.Precision( getPrecision( argv[ argc ] + 2 ));
					break;

				case 'q': // Set the value of the Quantum
					UseQuantum().Value( atof( argv[argc] + 2 ) );
					UseQuantum().Active( true );
					break;

				default:
					cout << "Invalid parameter " << argv[ argc ] << "!" << endl ;
					exit(0);
			}
		} else
		{
			cout << "Invalid parameter " << argv[ argc ] << "!" << endl ;
			exit(0);
		}
	}
}

/*******************************************************************
* Function Name: ~StandAloneLoader
********************************************************************/
StandAloneLoader::~StandAloneLoader()
{
}

/** private functions **/

/*******************************************************************
* Function Name: loadData
********************************************************************/
SimLoader &StandAloneLoader::loadData()
{
	cout << "N-CD++: A Tool to Implement n-Dimensional Cell-DEVS models\n";
	cout << "----------------------------------------------------------\n";
	cout << "Version 2.0-R.45 December-1999\n";
	cout << "Daniel Rodriguez, Gabriel Wainer, Amir Barylko, Jorge Beyoglonian\n";
	cout << "Departamento de Computacion. Facultad de Ciencias Exactas y Naturales.\n";
	cout << "Universidad de Buenos Aires. Argentina.\n";
	cout << "\n";

	cout << "Loading models from " << iniName << endl ;
	cout << "Loading events from " << evName << endl ;
	cout << "Message log: " << logName << endl ;
	cout << "Output to: " << outName << endl ;
	cout << "Tolerance set to: " <<  RealPrecision::Tol.precision() << endl;

	MASSERTMSG( RealPrecision::Tol.precision() >= 0, "The precision can't be negative.");

	cout << "Configuration to show real numbers: Width = " << Impresion::Default.Width() << " - Precision = " <<  Impresion::Default.Precision() << "\n";

	cout << "Quantum: ";
	if (UseQuantum().Active())
		cout <<  UseQuantum().Value() << "\n";
	else
		cout << "Not used\n";

	cout << "Evaluate Debug Mode = ";
	if (evalDebugName == "/dev/null")
	{
		cout << "OFF\n";
		EvalDebug().Active( false );
	}
	else
	{
		cout << "ON\tOutput of evaluated data = " << evalDebugName << endl;
		EvalDebug().Active( true );
	}

	cout << "Flat Cell Debug Mode = ";
	if (flatDebugName == "/dev/null")
	{
		cout << "OFF\n";
		FlatDebug().Active( false );
	}
	else
	{
		cout << "ON\tOutput of flat cells debug data = " << flatDebugName << endl;
		FlatDebug().Active( true );
	}

	cout << "Debug Cell Rules Mode = " <<  (DebugCellRules().Active() ? "ON":"OFF") << "\n";

	if (!preprocessor)
	{
		cout << "Bypassing preprocessor.\n";
		models = new fstream( iniName.c_str(), ios::in );
	}
	else
	{
		MacroExpansion(iniName);
		models = new fstream( MacroExpansion().expand().c_str(), ios::in )  ;
		cout << "Temporary File created by Preprocessor = " << MacroExpansion().tempFileName() << "\n";
	}

	cout << "Printing parser information = ";
	if (printParserInfo())
	{
		cout << "ON" << "\tUsing file = " << parserFileName << "\n";
	}
	else
	{
		cout << "OFF\n";
	}

	cout << "\nStarting simulation. Stop at time: ";
	if (stopTime() == Time::Inf)
		cout << "Infinity.";
	else
		cout << stopTime().asString();
	cout << endl ;

	if( evName == "" )
		events = new strstream() ;
	else
		events = new fstream( evName.c_str(), ios::in );

	if( logName == "-" || logName == "" )
		log = &cout ;
	else
		log = new fstream( logName.c_str(), ios::out );

	if( outName == "-" || outName == "" )
		output = &cout ;
	else
		output = new fstream( outName.c_str(), ios::out ) ;

	if( evalDebugName == "-" || evalDebugName == "" )
		EvalDebug().Stream( &cout );
	else
		EvalDebug().Stream( new fstream( evalDebugName.c_str(), ios::out ) );

	if( flatDebugName == "-" || flatDebugName == "" )
		FlatDebug().Stream( &cout );
	else
		FlatDebug().Stream( new fstream( flatDebugName.c_str(), ios::out ) );

	if( parserFileName == "-" || parserFileName == "stdout" )
		ParserDebug().Stream( &cout );
	else
		ParserDebug().Stream( new fstream( parserFileName.c_str(), ios::out ) );

	return *this;   
}

/*******************************************************************
* Function Name: writeResults
********************************************************************/
SimLoader &StandAloneLoader::writeResults()
{
	cout << "Simulation ended!" << endl ;
	delete models ;  models = NULL;
	delete events ;  events = NULL; 

	if( logName != "-" )
	{
		delete log ;  
		log = NULL ;
	}

	if( outName != "-" )
	{
		delete output ;  
		output = NULL ;
	}

	if (preprocessor)
		delete &(MacroExpansion());

	return *this;   
}
