/* 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 */
/*ParsePrint parse the print command  */
/*prt_constants print physical constants */
#include "cddefines.h"
#include "iso.h"
#include "iterations.h"
#include "physconst.h"
#include "called.h"
#include "elementnames.h"
#include "prt.h"
#include "h2.h"
#include "parse.h"
/*prt_constants print physical constants */
static void prt_constants(void);

void ParsePrint(char *chCard )
{
	int lgEOL;
	int ipISO ;
	long int i, 
	  j, 
	  nelem,
	  num1;
	double a;
	double sum;

#	ifdef DEBUG_FUN
	fputs( "<+>ParsePrint()\n", debug_fp );
#	endif

	/* >>chng 01 aug 91, had been series of if branches, and could hit more than
	 * one key - dangerous!  changed to else if so only one hit per line possible */
	if( lgMatch("AGES",chCard) )
	{
		/* print all estimates of cloud timescales */
		prt.lgPrnAges = TRUE;
	}

	else if( lgMatch("ARRA",chCard) )
	{
		/* print arrays for ionization balance of heavy elements */
		prt.lgPrtArry = TRUE;
	}

	else if( lgMatch("COLU",chCard) && lgMatch("DENS",chCard) )
	{
		if( lgMatch(" ON ",chCard) )
		{
			/* print column densities of elements - this is default */
			prt.lgPrintColumns = TRUE;
		}
		else if( lgMatch(" OFF",chCard) )
		{
			/* print column densities of elements */
			prt.lgPrintColumns = FALSE;
		}
	}

	/* option to print departure coefficients in addition to level pops 
	 * keywords He-like to do helike sequence element, else do h-like
	 * element name, if not recognized, does hydrogen
	 * so with no options prints hydrogen itself */
	else if( lgMatch("DEPA",chCard) )
	{
		if( lgMatch("HE-L",chCard) )
		{
			ipISO = ipHELIUM;
		}
		else
		{
			ipISO = ipHYDROGEN;
		}

		/* now check for element name */
		nelem = GetElem( chCard );
		/* return value is < 0 if no element recognized - in this case use H */
		nelem = MAX2(0,nelem);

		/* if no element specified but he-like iso, then use helium */
		if( nelem==0 && ipISO==ipHE_LIKE )
			nelem = ipHELIUM;

		if( nelem < ipISO )
		{
			fprintf(ioQQQ,"This iso-sequence (%s) and element (%s) are impossible.\n",
				elementnames.chElementName[ipISO],
				elementnames.chElementName[nelem]);
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}

		/* print departure coef instead of hydrogen level populations */
		iso.lgPrtDepartCoef[ipISO][nelem] = TRUE;
	}

	else if( lgMatch("CONS",chCard) )
	{
		/* print phycical constants, routine is file static */
		prt_constants();
	}

	else if( lgMatch("ERRO",chCard) )
	{
		/* print errors to special window */
		lgPrnErr = TRUE;
	}

	else if( lgMatch("HEAT",chCard) )
	{
		/* print heat arrays */
		prt.lgPrintHeating = TRUE;
	}

	/*else if( lgMatch("H-LI",chCard))*/
	else if( lgMatch("POPU",chCard))
	{
		if( lgMatch("HE-L",chCard) )
		{
			ipISO = ipHELIUM;
		}
		else
		{
			ipISO = ipHYDROGEN;
		}

		/* now check for element name */
		nelem = GetElem( chCard );
		/* return value is < 0 if no element recognized - in this case use H */
		nelem = MAX2(0,nelem);

		/* if no element specified but he-like iso, then use helium */
		if( nelem==0 && ipISO==ipHE_LIKE )
			nelem = ipHELIUM;

		if( nelem < ipISO )
		{
			fprintf(ioQQQ,"This iso-sequence (%s) and element (%s) are impossible.\n",
				elementnames.chElementName[ipISO],
				elementnames.chElementName[nelem]);
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}

		/* print hydrogenic H-like level populations */
		iso.lgPrtLevelPops[ipISO][nelem] = TRUE;

		/* option to set number of levels to print */
		i = 5;
		iso.numPrintLevels[ipISO][nelem] = (long)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			/* no number on line so use default, note that the current
			 * value of iso.numLevels_max[ipH_LIKE][nelem] may be the default, or have been reset
			 * with hydrogenic command.  So the effects of the following
			 * will depend on the order of the commands, if both this command
			 * and hydrogenic levels occur in the same input stream */
			iso.numPrintLevels[ipISO][nelem] = iso.numLevels_max[ipISO][nelem] - 1;
		}
		/* make sure at least three, since we will assert such in PrtHydro */
		if( iso.numPrintLevels[ipISO][nelem] < 3 )
		{
			fprintf(ioQQQ,"Sorry, I simply must print at least three levels.\n");
			iso.numPrintLevels[ipISO][nelem] = 3;
		}
		ASSERT( iso.numPrintLevels[ipISO][nelem] < iso.numLevels_max[ipISO][nelem] );
	}

	/* option to only print last iteration */
	else if( lgMatch("LAST",chCard) )
	{
		prt.lgPrtLastIt = TRUE;
	}

	/* the print line command as several options */
	else if( lgMatch("LINE",chCard) )
	{
		if( lgMatch(" ALL",chCard) )
		{
			/* turn on all printed componenets */
			prt.lgPrnPump = TRUE;
			prt.lgPrnColl = TRUE;
			prt.lgPrnHeat = TRUE;
		}

		else if( lgMatch(" H2",chCard) && lgMatch("ELEC",chCard) )
		{
			/* print H2 electronic lines too - -1 since number of electronic
			 * levels is not yet known, will set when H2 actually called */
			h2.nElecLevelOutput = -1;
		}

		else if( lgMatch("SORT",chCard) )
		{
			/* >>chng 01 aug 18, print sort command works after all these years,
			 * sort by wavelength or intensity */
			/* turn on sorting with respect to wavelength */
			prt.lgSortLines = TRUE;
			if( lgMatch("WAVE",chCard) )
			{
				/* sort by wavelength */
				/* remember which one to do */
				prt.lgSortLineIntensity = FALSE;
				prt.lgSortLineWavelength = TRUE;

				/* wavelength has range option */
				/* option to only certain print range of lines */
				if( lgMatch("RANG",chCard) )
				{
					i = 5;
					prt.wlSort1 = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
					/* line was entered, look for possible micron or cm label */
					if( chCard[i-1] == 'M' )
					{
						/* microns */
						prt.wlSort1 *= 1e4;
					}
					else if( chCard[i-1] == 'C' )
					{
						/* microns */
						prt.wlSort1 *= 1e8;
					}
					prt.wlSort2 = (float)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
					/* line was entered, look for possible micron or cm label */
					if( chCard[i-1] == 'M' )
					{
						/* microns */
						prt.wlSort2 *= 1e4;
					}
					else if( chCard[i-1] == 'C' )
					{
						/* microns */
						prt.wlSort2 *= 1e8;
					}
					if( lgEOL )
					{
						fprintf( ioQQQ, " There must be two numbers for the RANGE option, the lower and upper wavelength.  Sorry.\n" );
						puts( "[Stop in ParsePrint" );
						cdEXIT(EXIT_FAILURE);
					}
					if( prt.wlSort1 <0. || prt.wlSort2 <0. || 
						prt.wlSort1 >= prt.wlSort2 )
					{
						fprintf( ioQQQ, " The lower and upper wavelength must be positive and in the correct order.  Sorry.\n" );
						puts( "[Stop in ParsePrint" );
						cdEXIT(EXIT_FAILURE);
					}
				}
				else
				{
					prt.wlSort1 = -1;
					prt.wlSort2 = 1e30f;
				}
			}
			else if( lgMatch("INTE",chCard) )
			{
				/* sort by intensity/luminosity */
				/* remember which one to do */
				prt.lgSortLineIntensity = TRUE;
				prt.lgSortLineWavelength = FALSE;
			}
			else
			{
				fprintf( ioQQQ, "I can sort by wavelength or intensity - one must be specified.\nSorry.\n" );
				puts( "[Stop in ParsePrint" );
				cdEXIT(EXIT_FAILURE);
			}

		}
		else if( lgMatch("PUMP",chCard) )
		{
			/* also print pump contributions */
			prt.lgPrnPump = TRUE;
		}

		else if( lgMatch("INWA",chCard) )
		{
			/* also print inward contributions */
			prt.lgPrnInwd = TRUE;
		}

		else if( lgMatch("COLL",chCard) )
		{
			/* also print collisional contributions */
			prt.lgPrnColl = TRUE;
		}

		else if( lgMatch("HEAT",chCard) )
		{
			/* also print heating contributions */
			prt.lgPrnHeat = TRUE;
		}

		else if( lgMatch("OPTI",chCard) && lgMatch("DEPT",chCard) )
		{
			/* print line optical depths, with option for smallest to print */
			if( lgMatch(" OFF",chCard) )
			{
				/* turn off or on printing of optical depths - default off */
				prt.lgPrtTau = FALSE;
			}
			else
			{
				prt.lgPrtTau = TRUE;
			}
			if( lgMatch("FAIN",chCard) )
			{
				/* log of faintest optical depth */
				i = 5;
				prt.PrtTauFnt = (float)pow(10.,FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL));
				if( lgEOL )
				{
					fprintf( ioQQQ, " There must be a number for the FAINT option.  They are HEAD and ZONE.  Sorry.\n" );
					puts( "[Stop in ParsePrint" );
					cdEXIT(EXIT_FAILURE);
				}
			}
		}

		else if( lgMatch(" SUM",chCard) )
		{
			/* option to read in set of lines to sum over */
			sum = PrtLineSum( "READ" );
			/* sum is not used anywhere, following makes lint shut up */
			if( FALSE ) fprintf(ioQQQ,"%.2e\n", sum);
		}

		else if( lgMatch("COLU",chCard) )
		{
			/* option to print main line array as a single column */
			prt.lgPrtLineArray = FALSE;
			/* this also has an option - liNEAR - to print linear quantity 
			 * in exponential format */
			if( lgMatch("NEAR",chCard) )
			{
				prt.lgPrtLineLog = FALSE;
			}
		}

		else if( lgMatch("FAIN",chCard) )
		{
			/* print line faint
			 * option to adjust intensity of faintest line to print */
			/* >> 01 feb 01, move print faint into print line faint */
			/* faintest line, rel to norm line, to print; either linear of log */
			i = 5;
			a = FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

			/* option for, if no number, keyword=" OFF", to print all lines */
			if( lgEOL )
			{
				if( lgMatch(" OFF",chCard) )
				{
					prt.lgFaintOn = FALSE;
				}
				else
				{
					fprintf( ioQQQ, 
						" There faintest line to print must be on this line, sorry.\n" );
					puts( "[Stop in ParsePrint" );
					cdEXIT(EXIT_FAILURE);
				}
			}

			prt.lgFntSet = TRUE;
			if( a <= 0. )
			{
				prt.TooFaint = (float)pow(10.,a);
			}
			else
			{
				prt.TooFaint = (float)a;
			}
		}

		else if( lgMatch("FLUX",chCard) && lgMatch("EART",chCard))
		{
			/* print line flux seen at earth */
			prt.lgPrintFluxEarth = TRUE;
		}

		else if( lgMatch("SURF",chCard) && lgMatch("BRIG",chCard) )
		{
			/* print surface brightness rather than 4pi J */
			prt.lgSurfaceBrightness = TRUE;
			/* default is per sr, arcsec option changes to sq arcsec */
			if( lgMatch("ARCS",chCard ) )
			{
				/* use sr */
				prt.lgSurfaceBrightness_SR = FALSE;
			}
			else
			{
				/* use sq arsec */
				prt.lgSurfaceBrightness_SR = TRUE;
			}
		}
		else
		{
			fprintf( ioQQQ, "One of the keys should have appeared.  \nPlease consult Hazy.\nSorry.\n" );
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	/* print maser lines when TAV is called */
	else if( lgMatch("MASE",chCard) )
	{
		prt.lgPrtMaser = TRUE;
	}

	else if( lgMatch("ONLY",chCard) )
	{
		if( lgMatch("ZONE",chCard) )
		{
			prt.lgOnlyZone = TRUE;
		}

		else if( lgMatch("HEAD",chCard) )
		{
			prt.lgOnlyHead = TRUE;
		}

		else
		{
			fprintf( ioQQQ, " There must be a keyword for the ONLY option.  They are HEAD and ZONE.  Sorry.\n" );
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	else if( lgMatch("STAR",chCard) )
	{
		/* start printout at specified zone */
		called.lgTalk = FALSE;
		prt.lgPrtStart = TRUE;
		i = 5;
		prt.nstart = (long int)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			fprintf( ioQQQ, 
				" The zone on which the print is to start MUST be entered on this line.  Sorry.\n" );
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}
	}

	/* print cotninuum command */
	else if( lgMatch("CONT",chCard) )
	{
		/* >>chng 01 jun 30, logic of keys swapped around, so no keyword gets nFnu info */
		/* print continuum diffuse turns on nFnu continuum at many energies */
		if( lgMatch("BLOC",chCard) )
		{
			/* option to print emergent continuum at end of calculation*/
			prt.lgPrtCont = TRUE;
		}
		else
		{
			/* option to print continuum points within emission lines block */
			prt.lgPrnDiff = TRUE;
		}
	}

	else if( lgMatch("COOL",chCard) )
	{
		/* print cooling array for a specified one */
		i = 5;
		prt.nzdump = (long int)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);

		/* dumpt all zones if argument is zero or not present  */
		if( lgEOL )
		{
			prt.nzdump = 0;
		}
	}

	else if( lgMatch("QUIE",chCard) || (lgMatch(" OFF",chCard) && 
		!lgMatch("FAIN" ,chCard)) )
	{
		/* in above, there is also a 'print faint off' command
		 * QUIET or OFF means turn off printout */
		called.lgTalk = FALSE;
	}

	else if( lgMatch(" ON ",chCard) )
	{
		/* on means turn on printout, lgTalkIsOK is set true in cdInit, but set
		 * false in dooptimize.  this keeps printout quiet during optimize,
		 * even when init files are parsed */
		/* called.lgTalkForcedOff was set true with cdTalk(FALSE), if this was
		 * set then do not respect this command.  this is to prevent print on at end
		 * of init file from turning on print in grids when print is turned off */
		if( called.lgTalkIsOK && !called.lgTalkForcedOff )
		{
			called.lgTalk = TRUE;
		}
	}

	else if( lgMatch("SHOR",chCard) )
	{
		/* make short printout, don't print last */
		prt.lgPrtShort = TRUE;
		if( !prt.lgFntSet )
			prt.TooFaint = 0.001f;
	}

	else if( lgMatch("EVER",chCard) )
	{
		/* print every nth zone */
		i = 5;
		num1 = (long int)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
		if( lgEOL )
		{
			fprintf( ioQQQ, " The number of zones to print MUST be entered on this line.  Sorry.\n" );
			puts( "[Stop in ParsePrint" );
			cdEXIT(EXIT_FAILURE);
		}

		iterations.IterPrnt[0] = MAX2(num1,1);

		for( j=1; j < ITRDIM; j++ )
		{
			iterations.IterPrnt[j] = (long int)FFmtRead(chCard,&i,INPUT_LINE_LENGTH,&lgEOL);
			if( lgEOL )
			{
				iterations.IterPrnt[j] = iterations.IterPrnt[j-1];
			}
		}
	}

	/* check if no keywords were recognized. */
	else
	{
		fprintf( ioQQQ, " There MUST be a keyword on the following line.  Sorry.\n" );
		fprintf( ioQQQ, " The PRINT FAINT command is now the PRINT LINE FAINT command.\n" );
		fprintf( ioQQQ, " %80.80s\n", chCard );
		puts( "[Stop in ParsePrint]" );
		cdEXIT(EXIT_FAILURE);
	}

#	ifdef DEBUG_FUN
	fputs( " <->ParsePrint()\n", debug_fp );
#	endif
	return;
}

/*prt_constants print physical constants */
static void prt_constants(void)
{

#	ifdef DEBUG_FUN
	fputs( "<+>prt_constants()\n", debug_fp );
#	endif

	fprintf(ioQQQ,"\n\nPhysical constants used by Cloudy, taken from physconst.h\n");

	fprintf(ioQQQ,"ZerO\t%g\n",ZerO);
	fprintf(ioQQQ,"E\t%g\n",E);
	fprintf(ioQQQ,"PI\t%g\n",PI);
	fprintf(ioQQQ,"PI2\t%g\n",PI2);
	fprintf(ioQQQ,"PI4\t%g\n",PI4);
	fprintf(ioQQQ,"PI8\t%g\n",PI8);
	fprintf(ioQQQ,"SQRTPI\t%g\n",SQRTPI);
	fprintf(ioQQQ,"LN_TWO\t%g\n",LN_TWO);
	fprintf(ioQQQ,"LN_TEN\t%g\n",LN_TEN);
	fprintf(ioQQQ,"OPTDEP2EXTIN\t%g\n",OPTDEP2EXTIN);
	fprintf(ioQQQ,"SOLAR_MASS\t%g\n",SOLAR_MASS);
	fprintf(ioQQQ,"PARSEC\t%g\n",PARSEC);
	fprintf(ioQQQ,"ATOMIC_MASS_UNIT\t%g\n",ATOMIC_MASS_UNIT);
	fprintf(ioQQQ,"AU\t%g\n",AU);
	fprintf(ioQQQ,"ELECTRON_MASS\t%g\n",ELECTRON_MASS);
	fprintf(ioQQQ,"PROTON_MASS\t%g\n",PROTON_MASS);
	fprintf(ioQQQ,"BOLTZMANN\t%g\n",BOLTZMANN);
	fprintf(ioQQQ,"SPEEDLIGHT\t%g\n",SPEEDLIGHT);
	fprintf(ioQQQ,"HPLANCK\t%g\n",HPLANCK);
	fprintf(ioQQQ,"ELEM_CHARGE\t%g\n",ELEM_CHARGE);
	fprintf(ioQQQ,"RYD_INF\t%g\n",RYD_INF);
	fprintf(ioQQQ,"HIONPOT\t%g\n",HIONPOT);
	fprintf(ioQQQ,"H_BAR \t%g\n",H_BAR );
	fprintf(ioQQQ,"ELEM_CHARGE_ESU \t%g\n",ELEM_CHARGE_ESU );
	fprintf(ioQQQ,"ELECTRIC_CONST\t%g\n",ELECTRIC_CONST);
	fprintf(ioQQQ,"HION_LTE_POP\t%g\n",HION_LTE_POP);
	fprintf(ioQQQ,"SAHA2\t%g\n",SAHA2);
	fprintf(ioQQQ,"ERG1CM\t%g\n",ERG1CM);
	fprintf(ioQQQ,"T1CM\t%g\n",T1CM);
	fprintf(ioQQQ,"WAVNRYD\t%g\n",WAVNRYD);
	fprintf(ioQQQ,"RYDLAM\t%g\n",RYDLAM);
	fprintf(ioQQQ,"EN1RYD\t%g\n",EN1RYD);
	fprintf(ioQQQ,"TE1RYD\t%g\n",TE1RYD);
	fprintf(ioQQQ,"EVDEGK\t%g\n",EVDEGK);
	fprintf(ioQQQ,"EVRYD\t%g\n",EVRYD);
	fprintf(ioQQQ,"EN1EV\t%g\n",EN1EV);
	fprintf(ioQQQ,"FR1RYD\t%g\n",FR1RYD);
	fprintf(ioQQQ,"HNU3C2\t%g\n",HNU3C2);
	fprintf(ioQQQ,"FR1RYDHYD\t%g\n",FR1RYDHYD );
	fprintf(ioQQQ,"HBAReV\t%g\n",HBAReV );
	fprintf(ioQQQ,"RYDLAMHYD\t%g\n",RYDLAMHYD );
	fprintf(ioQQQ,"STEFAN_BOLTZ\t%g\n",STEFAN_BOLTZ);
	fprintf(ioQQQ,"FREQ_1EV\t%g\n",FREQ_1EV);
	fprintf(ioQQQ,"FINE_STRUCTURE\t%g\n",FINE_STRUCTURE);
	fprintf(ioQQQ,"TWO_PHOT_CONST\t%g\n",TWO_PHOT_CONST);
	fprintf(ioQQQ,"COLL_CONST2\t%g\n",COLL_CONST2);
	fprintf(ioQQQ,"COLL_CONST\t%g\n",COLL_CONST);
	fprintf(ioQQQ,"MILNE_CONST2\t%g\n",MILNE_CONST2);
	fprintf(ioQQQ,"MILNE_CONST\t%g\n",MILNE_CONST);
	fprintf(ioQQQ,"TRANS_PROB_CONST\t%g\n",TRANS_PROB_CONST);
	fprintf(ioQQQ,"\n\n\n");

#	ifdef DEBUG_FUN
	fputs( " <->prt_constants()\n", debug_fp );
#	endif
	return;
}
