/* Translation to C++ by Tim Molteno

	Based on the C port by N. Kyriazis
 	Including some pieces from additional work by
		Jeroen Vreeken <pe1rxq@amsat.org>
	
	Fixed a few bugs in the process.
	
	Using the std vector library in preparation
	for moving to ATLAS for the matrix and vector
	operations.

		Debian Build Instructions
		
	apt-get install atlas3-base atlas3-headers atlas3-base-dev
	apt-get install refblas3-dev lapack3-dev lapack3-doc
	
	For more information on using LAPACK for doing 
	efficient computation, see
		http://seehuhn.de/comp/linear.html
*/

/* Original disclaimer that came with the FORTRAN code */

/******* Translated to the C language by N. Kyriazis  20 Aug 2003 *******/
/*									*/
/* Program NEC(input,tape5=input,output,tape11,tape12,tape13,tape14,	*/
/* tape15,tape16,tape20,tape21)						*/
/*									*/
/* Numerical Electromagnetics Code (NEC2)  developed at Lawrence	*/
/* Livermore lab., Livermore, CA.  (contact G. Burke at 415-422-8414	*/
/* for problems with the NEC code. For problems with the vax implem- 	*/
/* entation, contact J. Breakall at 415-422-8196 or E. Domning at 415 	*/
/* 422-5936) 								*/
/* file created 4/11/80. 						*/
/*									*/
/*                ***********Notice********** 				*/
/* This computer code material was prepared as an account of work 	*/
/* sponsored by the United States government.  Neither the United 	*/
/* States nor the United States Department Of Energy, nor any of 	*/
/* their employees, nor any of their contractors, subcontractors, 	*/
/* or their employees, makes any warranty, express or implied, or	*/
/* assumes any legal liability or responsibility for the accuracy, 	*/
/* completeness or usefulness of any information, apparatus, product 	*/
/* or process disclosed, or represents that its use would not infringe 	*/
/* privately-owned rights. 						*/
/*									*/
/************************************************************************/


#include "nec2cpp.h"
#include "nec_exception.h"
#include <signal.h>
#include <vector>
#include <string>

using namespace std;

#include "nec_context.h"

#ifndef _WIN32
/* Signal handler */
static void sig_handler( int signal );
#endif


/*-------------------------------------------------------------------*/

int nec_main( int argc, char **argv, nec_output_file& s_output );

/*	New main() function

	This places an exception handler around the old main loop to 
	allow errors to be nicely caught!
*/
int main( int argc, char **argv )
{
	nec_output_file s_output;
	
	
	try
	{
		// This is an nec_3vector testharness
		{
			nec_3vector x(1,2,3);
			nec_3vector y(3,5,1);
			
			nec_3vector a = x + y;
			ASSERT(a == nec_3vector(4,7,4));
		
			a = x * y;
			ASSERT(a == nec_3vector(-13,8,-1));
			a = y * x;
			ASSERT(a == nec_3vector(13,-8,1));
			
			a = x + 3.0;
			ASSERT(a == nec_3vector(4,5,6));
			
			nec_float ip = x.dot(y);
			
			ASSERT(ip == 16);
		}
	
		nec_main(argc, argv, s_output);
	}
	catch (const char* message)
	{
		nec_error_mode nem(s_output);
		s_output.line("NEC++ Runtime Error: ");
		s_output.line(message);
		exit(1);
	}
	catch (nec_exception* nex)
	{
		nec_error_mode nem(s_output);
		s_output.line("NEC++ Runtime Error: ");
		s_output.line(nex->get_message().c_str());
		exit(1);
	}
}

#include "c_geometry.h"
void benchmark();

void benchmark()
{
	cout << "The nec2++ benchmark." << endl;
	cout << "nec2++ version " nec_version << endl << endl;
	
	nec_float bench = nec_context::benchmark();
	cout << "Your computer's score is: " << bench << " NEC's" << endl;
}

int readmn(FILE* input_fp, FILE* output_fp, char *gm, int *i1, int *i2, int *i3, int *i4, nec_float *f1,
	nec_float *f2, nec_float *f3, nec_float *f4, nec_float *f5, nec_float *f6);


#include "XGetopt.h"

int nec_main( int argc, char **argv, nec_output_file& s_output )
{
	nec_output_flags s_output_flags;
	FILE *input_fp=NULL;
	FILE *output_fp=NULL;
	
	string input_filename, output_filename;
	
	char ain[3], line_buf[81];
	
	/* input card mnemonic list */
	/* "XT" stands for "exit", added for testing */
	#define CMD_NUM  20
	char *atst[CMD_NUM] =
	{
		"FR", "LD", "GN", "EX", "NT", "TL", \
		"XQ", "GD", "RP", "NX", "PT", "KH", \
		"NE", "NH", "PQ", "EK", "CP", "PL", \
		"EN", "WG"
	};
	
	int itmp3, itmp2, itmp4;
	
	int ain_num;    /* ain mnemonic as a number */
	
	nec_float tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
	nec_float ex_timer;
	
	/* getopt() variables */
	extern char *optarg;
	int option;
	
#ifndef _WIN32
	/*** signal handler related code ***/
	/* new and old actions for sigaction() */
	struct sigaction sa_new, sa_old;
	
	
	/* initialize new actions */
	sa_new.sa_handler = sig_handler;
	sigemptyset( &sa_new.sa_mask );
	sa_new.sa_flags = 0;
	
	/* register function to handle signals */
	sigaction( SIGINT,  &sa_new, &sa_old );
	sigaction( SIGSEGV, &sa_new, 0 );
	sigaction( SIGFPE,  &sa_new, 0 );
	sigaction( SIGTERM, &sa_new, 0 );
	sigaction( SIGABRT, &sa_new, 0 );
#endif
	
	/*** command line arguments handler ***/
	if ( argc == 1 )
	{
		usage();
		exit(-1);
	}
	
	bool results_to_stdout = false;
	bool results_in_csv_format = false;
	
	/* process command line options */
	while( (option = XGetopt(argc, argv, "i:o:hvscgb") ) != -1 )
	{
		switch( option )
		{
		case 'i' : /* specify input file name */
			input_filename = optarg;
			break;
		
		case 'o' : /* specify output file name */
			output_filename = optarg;
			break;
		
		case 'g': /* return only the maximum gain to stdout */
			s_output_flags.set_gain_only(true);
			break;

		case 's': /* return output to stdout */
			results_to_stdout = true;
			break;

		case 'c': /* use CVS result data */
			results_in_csv_format = true;
			break;
	
		case 'h' : /* print usage and exit */
			usage();
			exit(0);
		
		case 'v' : /* print nec2++ version */
#ifdef _MSC_VER
			cout << ( "nec2++ " nec_version ) << " compiler: " << _MSC_VER << endl;
#else
			cout << ( "nec2++ " nec_version ) << (" compiler: " __VERSION__) << endl;
#endif
			exit(0);

		case 'b' : /* Run benchmark */
			benchmark();
			exit(0);
		
		default: /* print usage and exit */
			usage();
			exit(-1);
		
		}	
	}

	/*** open input file ***/
	if ( (input_fp = fopen(input_filename.c_str(), "r")) == NULL )
	{
		string mesg = "nec2++: "  + input_filename;
		
		perror( mesg.c_str() );
		exit(-1);
	}

	/* make an output file name if not */
	/* specified by user on invocation */
	if ( output_filename  == "" )
	{
		/* strip the input file name extension if there is one */
		output_filename = input_filename.substr(0, input_filename.find(".",0)) + ".out";
	}
	
	/* open output file */
	if ( (output_fp = fopen(output_filename.c_str(), "w")) == NULL )
	{
		string mesg = "nec2++: "  + output_filename;
		
		perror( mesg.c_str() );
		exit(-1);
	}
	
	s_output.set_file(output_fp);
    
	secnds( &ex_timer );

	// allocate a new nec_context;
	nec_context s_context;
	s_context.set_output(s_output, s_output_flags);
	s_context.set_results_stdout(results_to_stdout);
	if (results_in_csv_format)
		s_context.set_results_format(RESULT_FORMAT_CSV);
	s_context.initialize();
    
  /* main execution loop, exits at various points */
  /* depending on error conditions or end of jobs */
	while( true )
	{
	
		s_output.end_section();
		s_output.set_indent(31);
		s_output.line(" __________________________________________");
		s_output.line("|                                          |");
		s_output.line("| NUMERICAL ELECTROMAGNETICS CODE (nec2++) |");
		s_output.line("| Translated to 'C++' in Double Precision  |");
		s_output.line("|        Version " nec_version "        |");
		s_output.line("|__________________________________________|");
	
		/* read a line from input file */
		if ( load_line(line_buf, input_fp) == EOF )
			throw new nec_exception("Error reading input file.");
		
		/* separate card's id mnemonic */
		strncpy( ain, line_buf, 2 );
		ain[2] = '\0';
	
		/* If its an "XT" card, exit (used for debugging) */
		if ( strcmp(ain, "XT") == 0 )
		{
			nec_error_mode em(s_output);
			s_output.end_section();
			s_output.line("nec2++: Exiting after an \"XT\" command in main()");
			exit(0);
		}
	
		/* if its a "cm" or "ce" card start reading comments */
		if ( (strcmp(ain, "CM") == 0) ||
			(strcmp(ain, "CE") == 0) )
		{
			s_output.end_section();
			s_output.set_indent(31);
			s_output.line("---------------- COMMENTS ----------------");
			s_output.line(&line_buf[2]);
			while( strcmp(ain, "CM") == 0 )
			{
				/* read a line from input file */
				if ( load_line(line_buf, input_fp) == EOF )
					throw new nec_exception("Error reading input file (comments not terminated?)");
			
				/* separate card's id mnemonic */
				strncpy( ain, line_buf, 2 );
				ain[2] = '\0';
			
				/* write comment to output file */
				s_output.line(&line_buf[2]);
			}
		
			/* no "ce" card at end of comments */
			if ( strcmp(ain, "CE") != 0 )
			{
				throw new nec_exception("ERROR: INCORRECT LABEL FOR A COMMENT CARD");
			}
		}
		else
		{
			rewind( input_fp );
		}
	
		/* initializations etc from original fortran code */
		int data_card_count=0;
		
		/* set up geometry data in subroutine parse_geometry */
		c_geometry* geo = s_context.get_geometry();
		geo->parse_geometry(&s_context, input_fp);
		
		s_context.calc_prepare();
		s_output.end_section();
		
		/*
			Main input section, exits at various points
			depending on error conditions or end of job.
			This is called the card input loop.
		*/
				
		bool next_job = false; /* start next job (next structure) flag */
		while ( ! next_job )
		{
			int itmp1;
			
			/* main input section - standard read statement - jumps */
			/* to appropriate section for specific parameter set up */
			int parameter_count = readmn(input_fp, output_fp, ain, &itmp1, &itmp2, &itmp3, &itmp4,
				&tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6 );
			
			/* If its an "XT" card, exit */
			if ( strcmp(ain, "XT" ) == 0 )
			{
				nec_error_mode em(s_output);
				s_output.endl();
				s_output.line("nec2++: Exiting after an \"XT\" command in main()" );
				exit(0);
			}
			
			data_card_count++;
			fprintf( output_fp,
				"\n*****  DATA CARD N0. %3d "
				"%s %3d %5d %5d %5d %12.5E %12.5E %12.5E %12.5E %12.5E %12.5E",
				data_card_count, ain, itmp1, itmp2, itmp3, itmp4,
				tmp1, tmp2, tmp3, tmp4, tmp5, tmp6 );
			
			/* identify card id mnemonic (except "ce" and "cm") */
			for( ain_num = 0; ain_num < CMD_NUM; ain_num++ )
				if ( strncmp( ain, atst[ain_num], 2) == 0 )
					break;
		
			/* take action according to card id mnemonic */
			switch( ain_num )
			{
			case 0: /* "fr" card, frequency parameters */
				s_context.fr_card(itmp1, itmp2, tmp1, tmp2);
				continue;
		
			case 1: /* "ld" card, loading parameters */
				s_context.ld_card(itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3);
				continue;
		
			case 2: /* "gn" card, ground parameters under the antenna */
				s_context.gn_card(itmp1, itmp2, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue; 
		
			case 3: /* "ex" card, excitation parameters */
				s_context.ex_card((enum excitation_type)itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue; /* continue card input loop */
		
			case 4: /* "nt" card, network parameters */
				s_context.nt_card(itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue; /* continue card input loop */
		
			case 5: /* "tl" card, network parameters */
				if (parameter_count < 10)
				{
					nec_error_mode em(s_output);
					s_output.endl();
					s_output.line("nec2++: Missing parameters in \"TL\" card. Blank parameters should be specified as zero." );
					exit(0);
				}
				s_context.tl_card(itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue; /* continue card input loop */
		
			case 6: /* "xq" execute card - calc. including radiated fields */
				s_context.xq_card(itmp1);
				continue;
		
			case 7: /* "gd" card, ground representation */
				s_context.gd_card(tmp1, tmp2, tmp3, tmp4);
				continue; /* continue card input loop */
		
			case 8: /* "rp" card, standard observation angle parameters */
				{
					// pull out the XNDA parameters here...
					
					int XNDA = itmp4;
					int X = XNDA / 1000;
					int N = (XNDA / 100) % 10;
					int D = (XNDA / 10) % 10;
					int A = XNDA % 10;
					
					s_context.rp_card(itmp1, itmp2, itmp3, X, N, D, A, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				}
				continue; /* was break; followed by special code */
		
			case 9: /* "nx" card, do next job */
				next_job = true;
				continue; /* continue card input loop */
		
			case 10: /* "pt" card, print control for current */
				s_context.pt_card(itmp1, itmp2, itmp3, itmp4);
				continue; /* continue card input loop */
		
			case 11: /* "kh" card, matrix integration limit */
				s_context.kh_card(tmp1);
				continue; /* continue card input loop */
		
			case 12:  /* "ne" card, near field calculation parameters */
				s_context.ne_card(itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue;
			
			case 13:  /* "nh" card, near field calculation parameters */
				s_context.nh_card(itmp1, itmp2, itmp3, itmp4, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6);
				continue;
		
			case 14: /* "pq" card, print control for charge */
				s_context.pq_card(itmp1, itmp2, itmp3, itmp4);
				continue; /* continue card input loop */
		
			case 15: /* "ek" card,  extended thin wire kernel option */
				if (-1 == itmp1)
					s_context.set_extended_thin_wire_kernel(false);
				else
					s_context.set_extended_thin_wire_kernel(true);
				continue; /* continue card input loop */
		
			case 16: /* "cp" card, maximum coupling between antennas */
				s_context.cp_card(itmp1, itmp2, itmp3, itmp4);
				continue; /* continue card input loop */
		
			case 17: /* "pl" card, plot flags */
				{
					std::string ploutput_filename(input_filename);
					ploutput_filename += ".plt";
					try
					{
						s_context.pl_card(ploutput_filename.c_str(), itmp1, itmp2, itmp3, itmp4);
					}
					catch(...)
					{
						char mesg[88] = "nec2++: ";
					
						strcat( mesg, ploutput_filename.c_str() );
						perror( mesg );
						exit(-1);
					}
				}
				continue; /* continue card input loop */
		
			case 19: /* "wg" card, not supported */
				throw new nec_exception("\"WG\" card, not supported.");
		
			default:
				if ( ain_num != 18 ) // EN card
				{
					throw new nec_exception("FAULTY DATA CARD LABEL AFTER GEOMETRY SECTION.");
				}
			
				/******************************************************
				*** normal exit of nec2++ when all jobs complete ok ***
				******************************************************/
				s_context.all_jobs_completed();
				
				/* time the process */
				secnds( &tmp1 );
				tmp1 -= ex_timer;
				fprintf( output_fp, "\n\n  TOTAL RUN TIME: %d msec", (int)tmp1 );
				
				if( input_fp != NULL )
					fclose( input_fp );
				if( output_fp != NULL )
					fclose(output_fp);
					
				return(0);
			} /* switch( ain_num ) */
		
			/*
				End of the main input section. 
			
				far_field_flag is true if last card was XQ or RP
				
				This is no longer used, but I am leaving it in here while I iron this out
				properly. simulate() should be called by the xq card and the rp card.
			*/
			ASSERT(false == ((ain_num == 6) || (ain_num == 8)));
			s_context.simulate(false);
		} /* while( ! next_job ) */
	
	} /* while(true)  */

	return(0);
} /* end of nec_main() */


/*-----------------------------------------------------------------------*/
/*!\brief Read a line and fill in the parameter values. 
\return The number of parameters read
*/
int readmn(FILE* input_fp, FILE* output_fp, 
	char *gm, int *i1, int *i2, int *i3, int *i4,
	nec_float *f1, nec_float *f2, nec_float *f3,
	nec_float *f4, nec_float *f5, nec_float *f6 )
{
	int parameter_count = 0;
	char line_buf[134];
	int line_idx;
	int n_integer_params = 4, n_float_params = 6;
	int integer_array[4] = { 0, 0, 0, 0 };
	nec_float real_array[6] = { 0., 0., 0., 0., 0., 0. };
	
	/* read a line from input file */
	int eof = load_line( line_buf, input_fp );
	
	/* get line length */
	int line_length = (int) strlen(line_buf );
	
	/* abort if card's mnemonic too short or missing */
	if ( line_length < 2 )
	{
		if (EOF == eof)
		{
			// insert an EN card if we get to an end of file
			strncpy( gm, "EN", 2 );
			return 0;
		}
		else
		{
			fprintf( output_fp,
				"\n  COMMAND DATA CARD ERROR:"
				"\n  CARD'S MNEMONIC CODE TOO SHORT OR MISSING." );
			exit(-1);
		}
	}
	
	/* extract card's mnemonic code */
	strncpy( gm, line_buf, 2 );
	gm[2] = '\0';
	
	/* Exit if "XT" command read (for testing) */
	if ( strcmp( gm, "XT" ) == 0 )
	{
		fprintf( stderr,
			"\nnec2++: Exiting after an \"XT\" command in read_geometry_card()\n" );
		fprintf( output_fp,
			"\n\n  nec2++: Exiting after an \"XT\" command in read_geometry_card()" );
		exit(0);
	}
	
	/* Return if only mnemonic on card */
	if ( line_length == 2 )
	{
		*i1 = *i2 = *i3 = *i4 = 0;
		*f1 = *f2 = *f3 = *f4 = *f5 = *f6 = 0.0;
		return 0;
	}
	
	/* read integers from line */
	line_idx = 1;
	for (int i = 0; i < n_integer_params; i++ )
	{
		/* Find first numerical character */
		while( ((line_buf[++line_idx] <  '0')  ||
			(line_buf[  line_idx] >  '9')) &&
			(line_buf[  line_idx] != '+')  &&
			(line_buf[  line_idx] != '-') )
		if ( (line_buf[line_idx] == '\0') )
		{
			*i1= integer_array[0];
			*i2= integer_array[1];
			*i3= integer_array[2];
			*i4= integer_array[3];
			*f1= real_array[0];
			*f2= real_array[1];
			*f3= real_array[2];
			*f4= real_array[3];
			*f5= real_array[4];
			*f6= real_array[5];
			return parameter_count;
		}
		
		/* read an integer from line */
		integer_array[i] = atoi( &line_buf[line_idx] );
		parameter_count++;
		
		/* traverse numerical field to next ' ' or ',' or '\0' */
		line_idx--;
		while( (line_buf[++line_idx] != ' ') &&
			(line_buf[  line_idx] != ',') &&
			(line_buf[  line_idx] != '\0') )
		{
			/* test for non-numerical characters */
			if ( ((line_buf[line_idx] <  '0')  ||
				(line_buf[line_idx] >  '9')) &&
				(line_buf[line_idx] != '+')  &&
				(line_buf[line_idx] != '-') )
			{
				fprintf( output_fp,
				"\n  COMMAND DATA CARD \"%s\" ERROR:"
				"\n  NON-NUMERICAL CHARACTER '%c' IN INTEGER FIELD AT CHAR. %d\n",
				gm, line_buf[line_idx], (line_idx+1) );
				exit(-1);
			}		
		} /* while( (line_buff[++line_idx] ... */
		
		/* Return on end of line */
		if ( line_buf[line_idx] == '\0' )
		{
			*i1= integer_array[0];
			*i2= integer_array[1];
			*i3= integer_array[2];
			*i4= integer_array[3];
			*f1= real_array[0];
			*f2= real_array[1];
			*f3= real_array[2];
			*f4= real_array[3];
			*f5= real_array[4];
			*f6= real_array[5];
			return parameter_count;
		}		
	} /* for( i = 0; i < n_integer_params; i++ ) */
	
	/* read nec_floats from line */
	for (int i = 0; i < n_float_params; i++ )
	{
		/* Find first numerical character */
		while( ((line_buf[++line_idx] <  '0')  ||
			(line_buf[  line_idx] >  '9')) &&
			(line_buf[  line_idx] != '+')  &&
			(line_buf[  line_idx] != '-')  &&
			(line_buf[  line_idx] != '.') )
		if ( (line_buf[line_idx] == '\0') )
		{
			*i1= integer_array[0];
			*i2= integer_array[1];
			*i3= integer_array[2];
			*i4= integer_array[3];
			*f1= real_array[0];
			*f2= real_array[1];
			*f3= real_array[2];
			*f4= real_array[3];
			*f5= real_array[4];
			*f6= real_array[5];
			return parameter_count;
		}
		
		/* read a nec_float from line */
		real_array[i] = atof( &line_buf[line_idx] );
		parameter_count++;
		
		/* traverse numerical field to next ' ' or ',' */
		line_idx--;
		while( (line_buf[++line_idx] != ' ') &&
			(line_buf[  line_idx] != ',') &&
			(line_buf[  line_idx] != '\0') )
		{
			/* test for non-numerical characters */
			if ( ((line_buf[line_idx] <  '0')  ||
				(line_buf[line_idx] >  '9')) &&
				(line_buf[line_idx] != '.')  &&
				(line_buf[line_idx] != '+')  &&
				(line_buf[line_idx] != '-')  &&
				(line_buf[line_idx] != 'E')  &&
				(line_buf[line_idx] != 'e') )
			{
				fprintf( output_fp,
				"\n  COMMAND DATA CARD \"%s\" ERROR:"
				"\n  NON-NUMERICAL CHARACTER '%c' IN FLOAT FIELD AT CHAR. %d\n",
				gm, line_buf[line_idx], (line_idx+1) );
				exit(-1);
			}		
		} /* while( (line_buff[++line_idx] ... */
		
		/* Return on end of line */
		if ( line_buf[line_idx] == '\0' )
		{
			*i1= integer_array[0];
			*i2= integer_array[1];
			*i3= integer_array[2];
			*i4= integer_array[3];
			*f1= real_array[0];
			*f2= real_array[1];
			*f3= real_array[2];
			*f4= real_array[3];
			*f5= real_array[4];
			*f6= real_array[5];
			return parameter_count;
		}		
	} /* for( i = 0; i < n_float_params; i++ ) */
	
	*i1= integer_array[0];
	*i2= integer_array[1];
	*i3= integer_array[2];
	*i4= integer_array[3];
	*f1= real_array[0];
	*f2= real_array[1];
	*f3= real_array[2];
	*f4= real_array[3];
	*f5= real_array[4];
	*f6= real_array[5];
	
	return parameter_count;
}


/*-----------------------------------------------------------------------*/


#ifndef _WIN32
static void sig_handler(int signal )
{
	switch( signal )
	{
		case SIGINT :
			fprintf(stderr, "nec2++: exiting via user interrupt" );
			exit( signal );
	
		case SIGSEGV :
			fprintf(stderr, "nec2++: segmentation fault" );
			exit( signal );
	
		case SIGFPE :
			fprintf(stderr, "nec2++: floating point exception" );
			exit( signal );
	
		case SIGABRT :
			fprintf(stderr, "nec2++: abort signal received" );
			exit( signal );
	
		case SIGTERM :
			fprintf(stderr, "nec2++: termination request received" );
			exit( signal );
	}
}
#endif

