%{
#ifdef WIN32
#pragma warning (disable: 4786) // disable warning for STL maps
#pragma warning (disable: 4996) // disable warning for deprecated POSIX name
#pragma warning (disable: 4018) // disable warning for comparing signed and unsigned
#endif /* WIN32 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <string>
#include <vector>

#include "../dpuser2c/tools.h"
#define YYSTYPE char*
#include "y.tab.h"
#include "dpuser_utils.h"
#include "dpstringlist.h"


int idl2cparse();
void idl2cerror(const char *s);

yy_buffer_state *yy_scan_string(const char *str);


std::string lineIdl, nameIdl;
int nargsIdl;
funcorproc_ID funcorprocIdl;
char *global_sIdl;      // in parser/dpuser.l exists also a char named s
char dpstringbufIdl[256];
extern char *idl2clval;

%}

%x DECLARATION
%x ARGUMENTS
%x COMMENT
%x STRINGCON
%x FITSCON

dig [0-9]
num1 {dig}+\.?([eEdD][-+]?{dig}+)?
num2 {dig}*\.{dig}+([eEdD][-+]?{dig}+)?
number {num1}|{num2}

bla ([0-9]+\.?([eEdD][-+]?[0-9]+)?)|([0-9]*\.[0-9]+([eEdD][-+]?[0-9]+)?)

%array

%%

"function "" "*[a-zA-Z][a-zA-Z0-9_]*	{
	idlcode = true;
	funcorprocIdl = funcID;
	nameIdl = std::string(yytext+9);
	nargsIdl = 0;
	tools_variables.push_back(extractVariable(yytext+9));
	lineIdl = std::string("dpuserType ") + (yytext+9) + "(";
	tools_variables.push_back("nparams");
	BEGIN DECLARATION;
}

"pro "" "*[a-zA-Z][a-zA-Z0-9_]*	{
	idlcode = true;
	funcorprocIdl = procID;
	nameIdl = yytext+4;
	nargsIdl = 0;
	lineIdl = std::string("void ") + (yytext+4) + "(";
	tools_variables.push_back("nparams");
	BEGIN DECLARATION;
}

<DECLARATION>" "*","" "*
<DECLARATION>[a-zA-Z][a-zA-Z0-9_]* {
	nargsIdl++;
	lineIdl += std::string("dpuserType &") + yytext;
	arguments.push_back(yytext);
	BEGIN ARGUMENTS;
}
<DECLARATION>" "*\n {
   lineIdl += ")";	
   createInfo(funcorprocIdl, nargsIdl, nameIdl, lineIdl);
	lineIdl += " {";
	addLineToOutput(lineIdl);
	bracketcounter = 1;
	lineIdl = "";
	BEGIN 0;
}

<ARGUMENTS>" "*","" "*
<ARGUMENTS>" "
<ARGUMENTS>[a-zA-Z][a-zA-Z0-9_]* {
	nargsIdl++;
	lineIdl += std::string(", dpuserType &") + yytext;
	arguments.push_back(yytext);
}
<ARGUMENTS>" "*\n {
   lineIdl += ")";	
   createInfo(funcorprocIdl, nargsIdl, nameIdl, lineIdl);
	lineIdl += " {";
	addLineToOutput(lineIdl);
	bracketcounter = 1;
	lineIdl = "";
	BEGIN 0;
}

[0-9]+[L]?      {
                idl2clval = strdup(yytext);
                return INTEGER;
            }

([0-9]+\.?([eEdD][-+]?[0-9]+)?)|([0-9]*\.[0-9]+([eEdD][-+]?[0-9]+)?)    {
								for (int _c_ = 0; _c_ < strlen(yytext); _c_++) {
									if (yytext[_c_] == 'd') yytext[_c_] = 'e';
									else if (yytext[_c_] == 'D') yytext[_c_] = 'E';
								}
								idl2clval = strdup(yytext);
								return REAL;
						}

([0-9]+"i")|(([0-9]+\.?([eEdD][-+]?[0-9]+)?)|([0-9]*\.[0-9]+([eEdD][-+]?[0-9]+)?)"i") {
								yytext[strlen(yytext)-1] = 0;
								idl2clval= strdup(yytext);
								return COMPLEX;
						}
[0-9]+\.[0-9]*[dD] {
								yytext[strlen(yytext)-1] = 0;
								idl2clval = strdup(yytext);
								return REAL;
						}

","" "*"/"[a-zA-Z][a-zA-Z0-9]* {
	int i;
	i = 1;
	while (yytext[i] != '/') i++;
	i++;
	idl2clval = strdup(yytext + i);
	return OPTION;
}

"{" {
	bracketcounter++;
	return *yytext;
}

"}" {
	bracketcounter--;
	if (bracketcounter >= 1)
		return *yytext;
	else {
		flushOutput(nameIdl);
	}
}

[-()<>=+*/;:,\[\].^!?#%] {
	if (bracketcounter >= 1)
		return *yytext;
}

"&" unput(';');

">="|" ge "|" GE "       return GE;
"<="|" le "|" LE "       return LE;
"=="|" eq "|" EQ "       return EQ;
"!="|" ne "|" NE "       return NE;
"&&"|" and "|" AND "     return AND;
"||"|" or "|" OR "       return OR;
"lt"|"LT"			unput('<');
"gt"|"GT"			unput('>');
"mod"|"MOD"|"Mod"	unput('%');

" do "|" DO "|" Do "	;
"begin"|"BEGIN"|"Begin" {
	unput('{');
}

"endfor"|"ENDFOR"|"Endfor"|"end"|"END"|"endelse"|"endif" {
	unput('}');
}

"then" ;

"+="            return PlusE;
"-="            return MinusE;
"*="            return MulE;
"/="            return DivE;
"++"            return PlusP;
"--"            return MinusM;

"exit"          return EXIT;
"quit"          return EXIT;
"bye"           return EXIT;

[a-zA-Z][a-zA-Z0-9_]*" "*","" "*[a-zA-Z]       { 
								int i, fail;
								fail = 0;
								i = 2;
								while (yytext[yyleng-i] == ' ') { yytext[yyleng - i] = 0; i++; }
								if (yytext[yyleng-i] == ',') { yytext[yyleng - i] = 0; i++; }
								while (yytext[yyleng-i] == ' ') { yytext[yyleng - i] = 0; i++; }
								unput(yytext[yyleng-1]);
								unput(',');
									if (strcmp(yytext, "if") == 0) {
										return IF;
									}
									if (strcmp(yytext, "else") == 0) {
										return ELSE;
									}
									if (strcmp(yytext, "for") == 0) {
										return FOR;
									}
									idl2clval = strdup(yytext);
									if (isVariable(yytext)) return VARIABLE;
									if (strcmp(yytext, "return") == 0) {
										return RETVAL;
									}

									return PGPLOT;
            }

[a-zA-Z][a-zA-Z0-9_.]*       { 
//								int i;
								if (strcmp(yytext, "if") == 0) {
									return IF;
								}
								if (strcmp(yytext, "else") == 0) {
									return ELSE;
								}
								if (strcmp(yytext, "for") == 0) {
									return FOR;
								}
								idl2clval = strdup(yytext);
								if (isVariable(yytext)) {
								return VARIABLE;
								}
								if (strcmp(yytext, "return") == 0) {
									return RETVAL;
								}
								return PGPLOT;
							}

[a-zA-Z0-9][a-zA-Z0-9_]*" "*"("       { 
								int i;

								yytext[yyleng-1]=0;
								i = yyleng - 2;
								while (yytext[i] == ' ') yytext[i--] = 0;

									if (strcmp(yytext, "for") == 0) {
										unput('(');
										return FOR;
									}
									if (strcmp(yytext, "if") == 0) {
										unput('(');
										return IF;
									}
									if (strcmp(yytext, "else") == 0) {
										unput('(');
										return ELSE;
									}
									if (strcmp(yytext, "while") == 0) {
										unput('(');
										return WHILE;
									}
									if (strcmp(yytext, "where") == 0) {
										unput('(');
										return WHERE;
									}
									
									unput('(');
									idl2clval = strdup(yytext);
									return FUNCTION;
							}

[a-zA-Z_][a-zA-Z0-9_]*" "*"="/[^=]		{
								int i;
								yytext[strlen(yytext)-1] = '\0';
								i = yyleng - 2;
								while (yytext[i] == ' ') yytext[i--] = 0;
                idl2clval = strdup(yytext);
								unput('=');
                return VARIABLE;
            }

\"             { BEGIN STRINGCON; global_sIdl = dpstringbufIdl; }
<STRINGCON>\\\\   { *global_sIdl++ = '\\'; }
<STRINGCON>\\t    { *global_sIdl++ = '\t'; }
<STRINGCON>\\n    { *global_sIdl++ = '\n'; }
<STRINGCON>\\\"   { *global_sIdl++ = '"'; }
<STRINGCON>\"     { *global_sIdl++ = '\0';
                 BEGIN 0;
								 idl2clval = strdup(dpstringbufIdl);
								 return STRING;
					  		}
<STRINGCON>\n     { global_sIdl[-1] = '\0';
                 BEGIN 0;
								 unput(';');
								 unput('\n');
								 dp_output("Warning: Unterminated string constant\n");
								 idl2clval = strdup(dpstringbufIdl);
								 return STRING;
					  		}
<STRINGCON>.       { *global_sIdl++ = *yytext; }

\'             { BEGIN FITSCON; global_sIdl = dpstringbufIdl; }
<FITSCON>\\\'   { *global_sIdl++ = '\''; }
<FITSCON>\'     { *global_sIdl++ = '\0';
                 BEGIN 0;
								 idl2clval = strdup(dpstringbufIdl);
								 return STRING;
					  		}
<FITSCON>\n     { global_sIdl[-1] = '\0';
                 BEGIN 0;
								 unput(';');
								 unput('\n');
								 idl2clval = strdup(dpstringbufIdl);
								 dp_output("Warning: Unterminated string constant\n");
								 return STRING;
					  		}
<FITSCON>.       { *global_sIdl++ = *yytext; }

[ \t\n+]        ;       /* ignore whitespace */

%%

int yywrap(void) {
	return 1;
}

void idl2cerror(const char *s) {
	dp_output("%s\n", s);
	throw dpuserTypeException(const_cast<char*>(s));
}

int main_idl2c(char *fname) {
	int retVal = -1;

	// open script-file for reading
	FILE *fd = fopen(fname, "rb");
	if (fd != NULL) {
		size_t length;
		char *input;
		dpString iinput;
		dpStringList lines;
		int n;

		// get length of input file
		fseek(fd, 0L, SEEK_END);
		length = ftell(fd);
//		length++;
		fseek(fd, 0L, SEEK_SET);

		input = (char *)calloc(length+1, sizeof(char));
		if (input != NULL) {
			// read script into input-variable
			fread(input, sizeof(char), length, fd);
			
			for (n = 0; n < length; n++) if (input[n] == '\r') input[n] = '\n';
			for (n = 0; n < length; n++) if (input[n] == '\t') input[n] = ' ';
			for (n = 0; n < length-2; n++) {
				if (input[n] == '$') {
					int m = n+1;

					while ((input[m] == ' ') && (m < length-2)) m++;
					if (input[m] == '\n') {
						input[n] = ' ';
						input[m] = ' ';
						if (input[m+1] == '\n') input[m+1] = ' ';
						n = m;
					}
				}
			}
			input[length] = 0;
			eatIDLComment(input);
			iinput = input;
			IDLtoDPUSERcommands(iinput);
			lines = dpStringList::split('\n', iinput);
			iinput = "";
			for (n = 0; n < lines.size(); n++) iinput += std::string(tweakInput((char *)(lines[n] + "   ").c_str())) + "\n";
			input = strdup(iinput.c_str());

			if (strlen(input) > 1) {
			// send input-string to idl2c-parser
			printf("idl2c ==> %s\n", input);
			yy_scan_string(input);
			idl2cparse();
			printf("idl2c ==> %s\n", input);
			}
			free(input);

			// everything was fine
			retVal = 0;
		}
		fclose(fd);

	} else {
		printf(">>> Could not open script-file for reading\n");
	}
   
	return retVal;
}
