%{
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <dpComplex.h>
#include "dpuser.h"
#include "dpuser2c/dpuserType.h"
#include "dpuser2c/utils.h"
#include "dpuser2c/functions.h"
#include "dpuser.yacchelper.h"
#include "dpuser.procs.h"
#include "dpuser.pgplot.h"
#include "dpuser_funcs.h"
#include "dpuser_utils.h"
#include "svn_revision.h"
#include <signal.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_rng.h>
#include <fits.h>
#include <locale.h>

#ifdef WIN32
#pragma warning (disable: 4996) // disable warning for deprecated POSIX name
#ifdef CYGWIN
#include <sys/unistd.h>
#else
#include <direct.h>
#endif
#endif /* WIN32 */

/* prototypes */
nodeType *opr(int oper, int nops, ...);
nodeType *id(int i);
nodeType *con(long value);
void freeNode(nodeType *p);
conValue ex(nodeType *p);

#ifndef NO_READLINE
#ifdef __cplusplus
extern "C" {
#endif
int read_history(char *);
int write_history(char *);
void stifle_history(int);
#ifdef __cplusplus
}
#endif
#define DPHISTORY_SIZE 500
#endif /* !NO_READLINE */

#ifdef DPQT
/* extern Fits *defaultBuffer; */
#endif /* DPQT */

int yyparse();
int yylex();
int parseError;
extern int ScriptInterrupt;
double test;
extern dpStringList script;
%}

%union {
	long iValue;                 /* integer value */
	double dValue;
	dpComplex *cValue;
	int sIndex;                /* symbol table index */
	char *sValue;
	nodeType *nPtr;             /* node pointer */
};

%token <iValue> INTEGER
%token <iValue> FUNCTION
%token <dValue> REAL
%token <cValue> COMPLEX
%token <sValue> STRING
%token <sValue> FITSFILE
%token <sValue> HELP
%token <sValue> OPTION
%token <iValue> PGPLOT
%token <sIndex> VARIABLE
%token <sIndex> NEWVARIABLE
%token WHILE IF EXIT WRITEFITS RANGE SETRANGE FOR NEWFITS WHERE
%token RANGEPlusE RANGEMinusE RANGEMulE RANGEDivE RANGEPlusP RANGEMinusM
%nonassoc IFX
%nonassoc ELSE

%left AND OR
%left EQ NE
%left GE LE '>' '<'
%left '+' '-'
%left '%'
%left '*' '#' '/'
%left PlusE MinusE MulE DivE
%left PlusP MinusM
%left '^'
%nonassoc UMINUS
%nonassoc '!'
%nonassoc '[' ']'

%type <nPtr> stmt expr stmt_list funcarg_list rangearg_list rangeargs pgplot_arg boolean assignment varchange procedure

%%

function:
          function stmt {	parseError = 0;
							FitsInterrupt = 0;
							if (ex($2).type == typeUnknown) parseError = 1;
							freeNode($2);
							unprotect();
							nunderscores = 0;
							nfncpro = 0;
							freePointerlist(); }
		| /* NULL */
        ;

stmt:
          ';'                                { freePointerlist(); $$ = opr(';', 2, NULL, NULL); }
		| HELP                               { help($1); free($1); $$ = NULL; }
        | pgplot_arg                         { $$ = $1; }
		| procedure                          { $$ = $1; }
        | FOR '(' varchange ';' boolean ';' varchange ')' stmt   { $$ = opr(FOR, 4, $3, $5, $7, $9); }
		| FOR varchange ',' expr stmt        { $$ = opr(FOR, 3, $2, $4, $5); }
		| varchange ';'                      { $$ = $1; }
		| FITSFILE '=' expr                  { $$ = opr(WRITEFITS, 2, ffcon($1), $3); }
        | WHILE boolean stmt                 { $$ = opr(WHILE, 2, $2, $3); }
        | IF boolean stmt %prec IFX          { $$ = opr(IF, 2, $2, $3); }
        | IF boolean stmt ELSE stmt          { $$ = opr(IF, 3, $2, $3, $5); }
        | IF expr stmt %prec IFX             { $$ = opr(IF, 2, $2, $3); }
        | IF expr stmt ELSE stmt             { $$ = opr(IF, 3, $2, $3, $5); }
        | '{' stmt_list '}'                  { $$ = $2; }
		| error ';'                          { $$ = opr(';', 2, NULL, NULL); }
		| error '}'                          { $$ = opr(';', 2, NULL, NULL); }
		| EXIT                               { 
#ifndef NO_READLINE
		stifle_history(DPHISTORY_SIZE);
		write_history("dpuser.history");
#endif /* !NO_READLINE */

if (xpa != NULL) {
	XPAClose(xpa);
	xpa = NULL;
}

#ifdef DPQT
        return 0;
#else
		exit(0);
#endif
		}
        ;

stmt_list:
          stmt                               { $$ = $1; }
        | stmt_list stmt                     { $$ = opr(';', 2, $1, $2); }
        ;

assignment:
		  VARIABLE '=' expr                  { $$ = opr('=', 2, id($1), $3); }
        | VARIABLE '=' boolean               { $$ = opr('=', 2, id($1), $3); }
		| VARIABLE '=' assignment            { $$ = opr('=', 2, id($1), $3); } 
		;

varchange:
		  VARIABLE PlusP                     { $$ = opr(PlusP, 1, id($1)); }
		| VARIABLE MinusM                    { $$ = opr(MinusM, 1, id($1)); }
		| VARIABLE PlusE expr                { $$ = opr(PlusE, 2, id($1), $3); }
		| VARIABLE MinusE expr               { $$ = opr(MinusE, 2, id($1), $3); }
		| VARIABLE MulE expr                 { $$ = opr(MulE, 2, id($1), $3); }
		| VARIABLE DivE expr                 { $$ = opr(DivE, 2, id($1), $3); }
		| VARIABLE rangeargs '=' expr        { $$ = opr(SETRANGE, 3, id($1), $2, $4); }
		| VARIABLE rangeargs PlusE expr      { $$ = opr(RANGEPlusE, 3, id($1), $2, $4); }
		| VARIABLE rangeargs MinusE expr     { $$ = opr(RANGEMinusE, 3, id($1), $2, $4); }
		| VARIABLE rangeargs MulE expr       { $$ = opr(RANGEMulE, 3, id($1), $2, $4); }
		| VARIABLE rangeargs DivE expr       { $$ = opr(RANGEDivE, 3, id($1), $2, $4); }
		| VARIABLE rangeargs PlusP           { $$ = opr(RANGEPlusP, 2, id($1), $2); }
		| VARIABLE rangeargs MinusM          { $$ = opr(RANGEMinusM, 2, id($1), $2); }
		| assignment                         { $$ = $1; }
		;

funcarg_list:
		  FUNCTION '(' expr                  { $$ = fnc($1, 1, $3); }
		| FUNCTION '(' boolean               { $$ = fnc($1, 1, $3); }
		| funcarg_list ',' expr              { $$ = addfncarg($1, 1, $3); }
		| funcarg_list ',' assignment        { $$ = addfncarg($1, 1, $3); }
		| funcarg_list OPTION                { $$ = addfncopt($1, $2); }
		;

rangeargs:
          rangearg_list ']'                  { $$ = $1; }
		;

rangearg_list:
          '[' expr                           { $$ = rng(2, $2, NULL); }
		| '[' '*'                            { $$ = rng(2, NULL, NULL); }
		| rangearg_list ':' expr             { $$ = addrngarg($1, 0, $3); }
		| rangearg_list ',' expr             { $$ = addrngarg($1, 2, $3, NULL); }
		| rangearg_list ',' '*'              { $$ = addrngarg($1, 2, NULL, NULL); }
		;

procedure:
          PGPLOT                             { $$ = pgp($1, 0); }
		| PGPLOT ','                         { $$ = pgp($1, 0); }
		;

pgplot_arg:
		  procedure expr                     { $$ = addpgplotarg($1, 1, $2); }
		| procedure boolean                  { $$ = addpgplotarg($1, 1, $2); }
		| pgplot_arg ',' expr                { $$ = addpgplotarg($1, 1, $3); }
		| pgplot_arg ',' boolean             { $$ = addpgplotarg($1, 1, $3); }
		| pgplot_arg ',' assignment          { $$ = addpgplotarg($1, 1, $3); }
		| pgplot_arg OPTION                  { $$ = addprocopt($1, $2); }
		;

expr:
          INTEGER                            { $$ = con($1); }
		| REAL                               { $$ = dcon($1); }
		| COMPLEX                            { $$ = ccon($1); }
		| FITSFILE                           { $$ = ffcon($1); }
		| STRING                             { $$ = scon($1); }
        | VARIABLE                           { $$ = id($1); }
        | VARIABLE rangeargs                 { $$ = iddereference(RANGE, $1, $2); }
        | '-' expr %prec UMINUS              { $$ = opr(UMINUS, 1, $2); }
        | expr '+' expr                      { $$ = opr('+', 2, $1, $3); }
        | expr '-' expr                      { $$ = opr('-', 2, $1, $3); }
        | expr '*' expr                      { $$ = opr('*', 2, $1, $3); }
        | expr '/' expr                      { $$ = opr('/', 2, $1, $3); }
		| expr '^' expr                      { $$ = opr('^', 2, $1, $3); }
		| expr '#' expr                      { $$ = opr('#', 2, $1, $3); }
		| expr '%' expr                      { $$ = opr('%', 2, $1, $3); }
		| boolean '?' expr ':' expr          { $$ = opr('?', 3, $1, $3, $5); }
        | '(' expr ')'                       { $$ = $2; }
        | FUNCTION '(' ')'                   { $$ = fnc($1, 0); }
		| WHERE '(' boolean ')'              { $$ = opr(WHERE, 1, $3); }
		| WHERE '(' boolean ',' expr ')' { $$ = opr(WHERE, 2, $3, $5); }
		| funcarg_list      ')'              { $$ = $1; }
		| expr rangeargs                     { $$ = opr(RANGE, 2, $1, $2); }
		| rangeargs                          { $$ = opr(NEWFITS, 1, $1); }
		| expr AND expr                      { $$ = opr(AND, 2, $1, $3); }
		| expr OR expr                       { $$ = opr(OR, 2, $1, $3); }
		| '%' stmt '%' VARIABLE              { $$ = opr(';', 2, $2, id($4)); }
        ;


boolean:
          expr '<' expr                      { $$ = opr('<', 2, $1, $3); }
        | expr '>' expr                      { $$ = opr('>', 2, $1, $3); }
        | expr GE expr                       { $$ = opr(GE, 2, $1, $3); }
        | expr LE expr                       { $$ = opr(LE, 2, $1, $3); }
        | expr NE expr                       { $$ = opr(NE, 2, $1, $3); }
        | expr EQ expr                       { $$ = opr(EQ, 2, $1, $3); }
		| '(' boolean ')'                    { $$ = $2; }
		| boolean AND boolean                { $$ = opr(AND, 2, $1, $3); }
		| boolean OR boolean                 { $$ = opr(OR, 2, $1, $3); }
		| '!' boolean                        { $$ = opr('!', 1, $2); }
		;

%%

//extern int nfunctions;
//extern std::vector<FunctionDeclaration> funcs;
//extern function_declarations funcs[];

char *info_startup[] = {
DPUSERVERSION2 DP_VERSION,
GetRevString(),
#ifdef DBG
"DEBUG Version",
#endif
"Written since 1999 by Thomas Ott",
"Inspired by the original MPE dp_user speckle data reduction software",
" ",
"For basic information, type \"help\" at the DPUSER> prompt.",
"Online documentation available at: http://www.mpe.mpg.de/~ott/dpuser",
""
};

void controlcsignal(int signum) {
	dp_output("Interrupt encountered.\n");
	FitsInterrupt = 1;
	ScriptInterrupt = 1;
}

/* install new error handler */
void dpuser_gsl_error_handler(const char *reason, const char *file, int line, int gsl_errno) {
//	dp_output("GSL error: %s - %s\n", reason, gsl_strerror(gsl_errno));
}

#ifdef DPQT
int init_dpuser() {
#else
int main(int argc, char *argv[]) {
#endif /* DPQT */
	int i;

	setlocale(LC_NUMERIC, "C");
	init_functions();
	init_procedures();
	CreateHelpMaps();
	gsl_set_error_handler(&dpuser_gsl_error_handler);
	i = 0;
	while (strlen(info_startup[i]) > 0) {
		dp_output("%s\n", info_startup[i++]);
	}

	nvariables = 100;
	variables = (conValue *)malloc(nvariables * sizeof(conValue));
	for (i = 0; i < nvariables; i++) {
		svariables += "";
//		fvariables += "";
		variables[i].svalue = NULL;
		variables[i].cvalue = NULL;
		variables[i].fvalue = NULL;
		variables[i].ffvalue = NULL;
		variables[i].arrvalue = NULL;
                variables[i].dparrvalue = NULL;
                variables[i].type = typeUnknown;
	}
	nprotected = 100;
	protectedpntrs = (void **)malloc(nprotected * sizeof(void *));
	for (i = 0; i < nprotected; i++) protectedpntrs[i] = NULL;

/*
 * set up global variables
 */

/* speed of light, m/s */
	svariables[0] = "c";
	variables[0].dvalue = 299792458.0;
	variables[0].type = typeDbl;

/* pi */
	svariables[1] = "pi";
	variables[1].dvalue = 2.0 * acos(0.0);
	variables[1].type = typeDbl;

/* e */
	svariables[2] = "e";
	variables[2].dvalue = exp(1.0);
	variables[2].type = typeDbl;

/* naxis1, naxis2 */
	svariables[3] = "naxis1";
	variables[3].lvalue = 256;
	variables[3].type = typeCon;

	svariables[4] = "naxis2";
	variables[4].lvalue = 256;
	variables[4].type = typeCon;

/* plot device */
	svariables[5] = "plotdevice";
#if (defined(Q_OS_MACX) && defined(DPQT))
	variables[5].svalue = CreateString("/QT");
#elif (defined(WIN32) || defined(CYGWIN))
	variables[5].svalue = CreateString("/WINDOWS");
#else
	variables[5].svalue = CreateString("/XSERVE");
#endif /* WIN32 || CYGWIN */
	variables[5].type = typeStr;

/* method for some functions */
	svariables[6] = "method";
	variables[6].lvalue = 0;
	variables[6].type = typeCon;

/* amount of temporary memory for some libfits functions */
	svariables[7] = "tmpmem";
	variables[7].lvalue = MEDIAN_MEM;
	variables[7].type = typeCon;

	nglobals = 8;

#ifndef DPQT
/* command line arguments, if applicable */
	if (argc > 1) {
		svariables[8] = "argv";
		variables[8].arrvalue = CreateStringArray();
		variables[8].type = typeStrarr;
		for (i = 1; i < argc; i++) *variables[8].arrvalue += argv[i];
	}
#endif /* DPQT */

/* add QFitsView default variable */
#ifdef DPQT
/*	svariables[8] = "buffer1";
	variables[8].fvalue = defaultBuffer;
	variables[8].type = typeFits; */
#endif /* DPQT */

/* set number of functions */
	nfunctions = 0;
	nfunctions = funcs.size();
//	while (strlen(funcss[nfunctions].name) > 0) nfunctions++;
	dp_output("%i functions registered.\n", nfunctions);
	
/* set number of pgplot routines */
	npg = 0;
	npg = procs.size();
//	while (strlen(pgs[npg].name) > 0) npg++;
	dp_output("%i procedures registered.\n", npg - 126);
#ifdef HAS_PGPLOT
	dp_output("126 pgplot procedures registered.\n");
#else
  dp_output("This version was compiled without PGPLOT support.\n");
#endif /* HAS_PLPLOT */

#ifndef DPQT
	signal(SIGINT, &controlcsignal);
#endif /* !DPQT */

#ifndef NO_READLINE
	read_history("dpuser.history");
#endif /* !NO_READLINE */

#ifdef WIN32
/* On Windows, set up directory for PGPLOT */
/* TO: Commented out 02072014: Not needed anymore with our own modified pgplot
	if (getenv("PGPLOT_DIR") == NULL) {
		char *env, *cwd;
		
		cwd = (char *)malloc(256 * sizeof(char));
		if (cwd != NULL) {
			getcwd(cwd, 255);
			env = (char *)malloc((strlen(cwd) + 12) * sizeof(char));
			if (env != NULL) {
				sprintf(env, "PGPLOT_DIR=%s", cwd);
				putenv(env);
				dp_output("Environment variable %s set.\n", env);
			}
			free(cwd);
		}
	}
*/
#endif /* WIN32 */

/* Set up random number generator */
	const gsl_rng_type * T;

	gsl_rng_env_setup();

	gsl_rng_default_seed = time(NULL);
	T = gsl_rng_default;
	gsl_rng_r = gsl_rng_alloc (T);
	
//	idum = -time(NULL);
//	ran1(&idum);

/* run startup script, if available */
	silence = 1;
	script.clear();
	script += "{startup_=getenv(\"DPUSER\");if startup_==\"\" startup_=getenv(\"HOME\");if(startup_!=\"\"){if startup_[strlen(startup_)]!=\"/\" startup_+=\"/\";}startup_+=\"startup.dpuser\";if fileexists(startup_)==1 run startup_}";
	script += "echo 1";

	yyparse();

	if (xpa != NULL) {
		XPAClose(xpa);
		xpa = NULL;
	}

#ifdef DPQT
	return 0;
#else
	exit(0);
#endif
}


