/* This file is part of Cloudy and is copyright (C)1978-2006 by Gary J. Ferland
 * For conditions of distribution and use see copyright notice in license.txt */
/*main program that reads input and calls cloudy to compute a single model, or
 * try to optimize an observed model.  Routine returns 0 if model is ok, 
 * and 1 if problems occurred. */
#include "cddefines.h"
#include "cddrive.h"
#include "input.h"
#include "prt.h"
#include "punch.h"
#include "assertresults.h"
#include "grains.h"

int main( int argc, char *argv[] )
{
	/* these will be used to count number of various problems */
	long int NumberWarnings, 
	  NumberCautions, 
	  NumberNotes, 
	  NumberSurprises, 
	  NumberTempFailures, 
	  NumberPresFailures,
	  NumberIonFailures, 
	  NumberNeFailures ;

	int lgAbort_exit,
		lgBadExit;
	/* number of lines we can still read in */
	int nread=0 ;

	int i,
		fileio,
		lenp,
		leno,
		leni,
		lena;
	char *s, 
		*prefix = NULL, 
		*filename,
		outtag[] = ".out",
		intag[] = ".in";

	/* the length of the following vector will be the longest line image
	 * the code will be able to read here.  Cloudy itself will ignore anything 
	 * beyond INPUT_LINE_LENGTH, and checks that no information exists beyond it. 
	 * The code will stop if the input line is longer than INPUT_LINE_LENGTH
	 * since extra characters would become a new command line due to buffer overrun */
	char chLine[10000];

	/* change following to TRUE to write to debugging file */
	fileio=FALSE;

	/* 
	 * Handle argument input -- written generally, but presently handles
	 * only `-p prefix' or `-pprefix' to set input file as `prefix.in',
	 * output file as `prefix.out' and the punch prefix.
	 *
	 * */
	for (i=1; i<argc; i++) 
	{
		s = argv[i];
		if (*s != '-') 
		{
			fprintf(stderr,"%s: argument %d `%s' not understood\n",argv[0],i,s);
			exit(-1);
		} 
		else
		{
			while (s != NULL && *(++s)) {
				switch (*s) 
				{
				case 'p':
					if (s[1] != '\0')
					{
						prefix = s+1;
					}					
					else
					{
						if (++i == argc || argv[i][0] == '-') {
							fprintf(stderr,"%s: no argument given for -p flag\n",argv[0]);
							exit(-1);
						}
						prefix = argv[i];
					}
					s = NULL;
					fileio = TRUE;
					break;
				default:
					fprintf(stderr,"%s: argument %d, `%s': flag -%c not understood\n",argv[0],i,argv[i],*s);
					exit(-1);
				}
			}
		}
	}
					

	/* initialize the code for this run */
	cdInit();

	/* following should be set true to print to file instead of std output */
	if( fileio )
	{
		FILE *ioOut, *ioInp;
		if (prefix == NULL) 
		{
			/* this is an option to direct code to a file rather than stdout*/
			/* following used for passing output to temp file*/
			if( (ioOut = fopen("c:\\projects\\cloudy\\tests\\CallCloudy.txt", "w" ))==NULL)
				BadOpen();
			cdOutp(ioOut);
		}
		else
		{
			leno = (int)strlen(outtag);
			leni = (int)strlen(intag);
			lenp = (int)strlen(prefix);
			if (leno > leni)
				lena = lenp+leno+1;
			else
				lena = lenp+leni+1;
			filename = (char *)MALLOC((unsigned)lena*sizeof(char));
			if (filename == NULL) 
			{
				fprintf(stderr,"%s: can't allocate memory\n",argv[0]);
				exit(-1);
			}
			strcpy(filename,prefix);
			strcpy(filename+lenp,outtag);
			if( (ioOut = fopen(filename, "w" ))==NULL)
				BadOpen();
			cdOutp(ioOut);
			strcpy(filename+lenp,intag);
			if( (ioInp = fopen(filename, "r" ))==NULL)
				BadOpen();
			cdInp(ioInp);
			strcpy( punch.chFilenamePrefix , prefix );
			free(filename);
		}
	}

	nread = 1;
	/* keep reading input lines until end of file */
	while( fgets(chLine, (int)sizeof(chLine), ioStdin)!= NULL )
	{
		char *chChar;
		/* when running from command prompt, last line can be space or /n,
		 * so check for each here, and break if present,
		 * check on chLine[23] is for case where we are reading cloudy output */
		if(   (chLine[0]=='\n') || 
			( (chLine[0]==' ')&& (chLine[23]!='*') ) 
			) break;

		/* change underscore to space if no filename occurs on line */
		if( (chChar = strchr(chLine , '\"' ) ) ==NULL )
		{
			/* check for underscore, probably meant as a space */
			while( (chChar = strchr(chLine , '_' ) ) !=NULL )
			{
				*chChar = ' ';
				input.lgUnderscoreFound = TRUE;
			}
		}

		/* check for left or right bracket, probably meant as a space */
		while( (chChar = strchr(chLine , '[' ) ) !=NULL )
		{
			*chChar = ' ';
			input.lgBracketFound = TRUE;
		}
		while( (chChar = strchr(chLine , ']' ) ) !=NULL )
		{
			*chChar = ' ';
			input.lgBracketFound = TRUE;
		}

		/* this is trick so that cloudy input can read cloudy output */
		/* are first 25 char of input string equal to output? */
		if( strncmp(chLine,"                       * ",25) == 0 )
		{
			/* reading cloudy output, send in shifted input */
			nread = cdRead( chLine+25 );
		}
		else
		{
			/* stuff the command line into the internal stack */
			nread = cdRead( chLine  );
		}
	}
	if( nread <= 0 )
	{
		fprintf(ioQQQ," Warning: limit to number of lines exceeded, %i\n", nread);
	}

	/* actually call the code.  This routine figures out whether the code will do
	 * a single model or be used to optimize on a spectrum, by looking for the
	 * keyword VARY on command lines.  It will call routine cloudy if no vary commands
	 * occur, and optimize_do if VARY does occur.  
	 * cdDrive returns 0 if calculation is ok, 1 if problems happened */
	lgBadExit = cdDrive();

	/* the last line of output will contain some interesting information about the model*/
	cdNwcns(
     /* abort status, this better be falase, 0 */
	 &lgAbort_exit,
	 /* the number of warnings, cautions, notes, and surprises */
     &NumberWarnings, 
     &NumberCautions, 
     &NumberNotes, 
     &NumberSurprises, 
     /* the number of temperature convergence failures */
     &NumberTempFailures, 
     /* the number of pressure convergence failures */
     &NumberPresFailures,
     /* the number of ionzation convergence failures */
     &NumberIonFailures, 
     /* the number of electron density convergence failures */
     &NumberNeFailures );

	fprintf( ioQQQ, 
		" Cloudy ends:%4ld zone" ,
		nzone);

	/* put an s in interation if more than one */
	if( nzone > 1 )
	{
		fprintf( ioQQQ,"s");
	}

	fprintf( ioQQQ, 
		", %3ld iteration" ,
		iteration );

	/* put an s in interation if more than one */
	if( iteration > 1 )
	{
		fprintf( ioQQQ,"s");
	}

	if( lgAbort_exit )
	{
		fprintf( ioQQQ, 
			", ABORT ABORT" );
	}

	if( NumberWarnings > 0 )
	{
		fprintf( ioQQQ, 
			",%3ld warning", 
			NumberWarnings);

		/* put an s in interation if more than one */
		if( NumberWarnings > 1 )
		{
			fprintf( ioQQQ,"s");
		}
		/* this indicates error */
		lgBadExit = 1;
	}

	if( NumberCautions > 0 )
	{
		fprintf( ioQQQ, 
			",%3ld caution", 
			NumberCautions);

		/* put an s in interation if more than one */
		if( NumberCautions > 1 )
		{
			fprintf( ioQQQ,"s");
		}
	}

	/* this flag was set in lgCheckAsserts*/
	if( !lgAssertsOK )
	{
		fprintf(ioQQQ,", ");
		/* some botches were three sigma */
		if( lgBigBotch  )
			fprintf(ioQQQ,"BIG ");
		fprintf(ioQQQ,"BOTCHED ASSERTS!!!");
		/* this indicates error */
		lgBadExit = 1;
	}

	if( NumberTempFailures+NumberPresFailures +NumberIonFailures+NumberNeFailures >0 )
	{
		fprintf( ioQQQ, 
			".  Failures:%3ld temp,%3ld press, %3ld ioniz,%3ld elec den\n", 
			NumberTempFailures, 
			NumberPresFailures ,
			NumberIonFailures, 
			NumberNeFailures);
	}

	/* NB DO NOT CHANGE ANY ASPECT OF THE FOLLOWING STRINGS - THEY ARE USED TO RECORD
	 * EXEC TIME BY A PERL SCRIPT */
	if( prt.lgPrintTime )
	{
		/* print execution time [s] by default,
		 * need spaces areound number so that logging perl script picks up correct number 
		 * ir_extime.pl script will delete through "ExecTime(s)" and remainder of line must be number */
		fprintf( ioQQQ, ".  ExecTime(s) %.2f",	cdExecTime());
	}

	fprintf( ioQQQ, "\n");

	if( lgBadExit )
	{
		fprintf(ioQQQ, " [Stop in maincl, something went wrong]\n" );
	}
	else
	{
		fprintf(ioQQQ, " [Stop in maincl, Cloudy exited OK]\n" );
	}
	
	/* return memory used by the grains */
	ReturnGrainBins();

	/* cdDrive returned 1 if something bad happened, and 0 if everything is ok.  We will
	 * return 0 if everything is ok, and 1 if something bad happened.*/
	return(lgBadExit);
}
