/*
 * Vis5d+/Gtk user interface 
 * Copyright (C) 2001 James P Edwards
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <glib.h>
#include "procedure.h"

/* define enumeration values to be returned for specific symbols */
/* a SCOPE_SYMBOL is one which will change the current parser scope */

enum {
  SCOPE_SYMBOL_IMAGE  = G_TOKEN_LAST + 1,
  SYMBOL_NAME,
  SCOPE_SYMBOL_HSLICE,
    SYMBOL_VAR,
    SYMBOL_MIN,
    SYMBOL_MAX,
    SYMBOL_HEIGHT,
    SYMBOL_INT,
    SYMBOL_STIPPLE,
    SYMBOL_WIDTH,
    SYMBOL_COLOR,
  SCOPE_SYMBOL_CHSLICE,
    SYMBOL_COLOR_TABLE,
  NULL_SYMBOL    /* should always be last - just a pointer to the end*/
};


/* symbol array */
typedef const struct {
  gchar *symbol_name;
  guint  symbol_token;
  guint  symbol_context;
} Symbols; 

Symbols symbols[] = {
  { "image", SCOPE_SYMBOL_IMAGE, 0,},
  { "name",  SYMBOL_NAME,  SCOPE_SYMBOL_IMAGE,},
  { "hslice", SCOPE_SYMBOL_HSLICE,  SCOPE_SYMBOL_IMAGE,},
    { "var", SYMBOL_VAR,  SCOPE_SYMBOL_HSLICE,},
    { "min", SYMBOL_MIN,  SCOPE_SYMBOL_HSLICE,},
    { "max", SYMBOL_MAX,  SCOPE_SYMBOL_HSLICE,},
    { "height", SYMBOL_HEIGHT,  SCOPE_SYMBOL_HSLICE,},
    { "interval", SYMBOL_INT,  SCOPE_SYMBOL_HSLICE,},
    { "stipple", SYMBOL_STIPPLE, SCOPE_SYMBOL_HSLICE,},
    { "width", SYMBOL_WIDTH, SCOPE_SYMBOL_HSLICE,},
    { "color", SYMBOL_COLOR, SCOPE_SYMBOL_HSLICE,},
  { "chslice", SCOPE_SYMBOL_CHSLICE,  SCOPE_SYMBOL_IMAGE,},
    { "var", SYMBOL_VAR,  SCOPE_SYMBOL_CHSLICE,},
    { "min", SYMBOL_MIN,  SCOPE_SYMBOL_CHSLICE,},
    { "max", SYMBOL_MAX,  SCOPE_SYMBOL_CHSLICE,},
    { "height", SYMBOL_HEIGHT,  SCOPE_SYMBOL_CHSLICE,},
    { "color_table", SYMBOL_COLOR_TABLE, SCOPE_SYMBOL_CHSLICE,},
  { NULL, 0, 0,},
};

static int context_depth=0, context[8];

static guint 
token_string(GScanner *scanner, gchar **str)
{
  g_scanner_get_next_token (scanner);
  if(scanner->token != G_TOKEN_STRING)
	 return G_TOKEN_STRING;
  *str = g_strdup(scanner->value.v_string);
  /* Next token should be a semi-colon */
  g_scanner_get_next_token (scanner);
  if(scanner->token!=';')
	 return ';';

  return G_TOKEN_NONE;
}

static guint
token_float(GScanner *scanner, float *fval)
{
  gboolean negate = FALSE;


  /* convert G_TOKEN_INT to G_TOKEN_FLOAT */
  scanner->config->int_2_float = TRUE;


	 /* feature optional '-' */
  g_scanner_peek_next_token (scanner);
  if (scanner->next_token == '-')
	 {
		g_scanner_get_next_token (scanner);
		negate = !negate;
	 }
  
  /* expect a float (ints are converted to floats on the fly) */
  g_scanner_get_next_token (scanner);

  /* convert G_TOKEN_INT to G_TOKEN_FLOAT */
  scanner->config->int_2_float = FALSE;

  if (scanner->token != G_TOKEN_FLOAT)
	 return G_TOKEN_FLOAT;

  *fval = negate ? - scanner->value.v_float : scanner->value.v_float;
  
  /* make sure the next token is a ';' strip in any case*/
  if (g_scanner_get_next_token (scanner) != ';')
	 {
		/* not so, eat up the non-semicolon and error out */
		return ';';
	 }
  return G_TOKEN_NONE;
}

static guint
token_float_list(GScanner *scanner, gint nitems, float *fval)
{
  gint i;
  gchar token;
  /* first token should be { */
  g_scanner_peek_next_token (scanner);
  if (scanner->next_token == '{')
	 g_scanner_get_next_token (scanner);
  else
	 return '{';

  /* convert G_TOKEN_INT to G_TOKEN_FLOAT */
  scanner->config->int_2_float = TRUE;


  for(i=0;i<nitems;i++){
	 gboolean negate = FALSE;
	 /* feature optional '-' */
	 g_scanner_peek_next_token (scanner);
	 if (scanner->next_token == '-')
		{
		  g_scanner_get_next_token (scanner);
		  negate = !negate;
		}
  
	 /* expect a float (ints are converted to floats on the fly) */
	 g_scanner_get_next_token (scanner);


	 if (scanner->token != G_TOKEN_FLOAT)
		return G_TOKEN_FLOAT;

	 fval[i] = negate ? - scanner->value.v_float : scanner->value.v_float;
  
	 /* make sure the next token is a ',' or '}' strip in any case*/
	 token = g_scanner_get_next_token (scanner);
	 if (i<nitems-1 && token != ',')
	 {
		/* not so, eat up the non-comma and error out */
		return ',';
	 }else if(i==nitems-1 && token != '}')
	 {
		return '}';
	 }
  }
  /* do not convert G_TOKEN_INT to G_TOKEN_FLOAT */
  scanner->config->int_2_float = FALSE;

  /* make sure the next token is a ';' strip in any case*/
  if (g_scanner_get_next_token (scanner) != ';')
	 {
		/* not so, eat up the non-semicolon and error out */
		return ';';
	 }
  return G_TOKEN_NONE;


}

static guint
token_int(GScanner *scanner, guint *fval)
{
  g_scanner_peek_next_token (scanner);

  g_scanner_get_next_token (scanner);
  if (scanner->token != G_TOKEN_INT)
	 return G_TOKEN_INT;

  *fval = scanner->value.v_int;
  
  /* make sure the next token is a ';' strip in any case*/
  if (g_scanner_get_next_token (scanner) != ';')
	 {
		/* not so, eat up the non-semicolon and error out */
		return ';';
	 }
  return G_TOKEN_NONE;
}

Image *image_add_item(Image *image, gpointer item, gint itemtype, gchar *imagename)
{
  if(image==NULL){
	 image =  g_new(Image,1);
	 image->vinfo_array = g_array_new(FALSE,TRUE,sizeof(v5d_var_info *)); 
	 image->items=g_ptr_array_new();
	 image->item_type=g_array_new(FALSE,TRUE,sizeof(gint));
  }
  if(item){  
	 g_ptr_array_add(image->items,(gpointer) item);
	 g_array_append_val(image->item_type,itemtype);
  }
  if(imagename){
	 image->name = g_strdup(imagename);
  }
  return image;
}


static guint
parse_hslice_symbol(GScanner *scanner, Image *oneimage, guint symbol)
{
  guint tmp;
  hslicecontrols *onehslice;
  if(g_array_index(oneimage->item_type,gint,oneimage->item_type->len-1)
	  != HSLICE){
	 g_print("Expected HSLICE found %d\n",oneimage->item_type->data[oneimage->item_type->len-1]);
	 return G_TOKEN_SYMBOL;
  }
  onehslice = (hslicecontrols *) g_ptr_array_index(oneimage->items,oneimage->items->len-1);
  
  switch (symbol){
  case SYMBOL_VAR:
	 return token_string(scanner,&onehslice->var);
  case SYMBOL_MIN:
	 return token_float(scanner, &onehslice->min);
  case SYMBOL_MAX:
	 return token_float(scanner, &onehslice->max);
  case SYMBOL_INT:
	 return token_float(scanner, &onehslice->interval);
  case SYMBOL_HEIGHT:
	 return token_float(scanner, &onehslice->height);
  case SYMBOL_STIPPLE:
	 symbol = token_int(scanner, &tmp);		
	 onehslice->stipple = (gushort) tmp;
	 return symbol;
  case SYMBOL_WIDTH:
	 return token_int(scanner, &onehslice->linewidth);
  case SYMBOL_COLOR:
	 {
		gfloat color[4];
		gint i;
		symbol = token_float_list(scanner, 4, color);
		for(i=0;i<4;i++)
		  onehslice->color[i]=(gdouble) color[i];
		return symbol;
	 }
	 
  default:
	 return G_TOKEN_SYMBOL;
  }	 
  return G_TOKEN_NONE;
}


static guint
parse_chslice_symbol(GScanner *scanner, Image *oneimage, guint symbol)
{
  guint tmp;
  hslicecontrols *onehslice;
  if(g_array_index(oneimage->item_type,gint,oneimage->item_type->len-1)
	  != CHSLICE){
	 g_print("Expected CHSLICE found %d\n",oneimage->item_type->data[oneimage->item_type->len-1]);
	 return G_TOKEN_SYMBOL;
  }
  onehslice = (hslicecontrols *) g_ptr_array_index(oneimage->items,oneimage->items->len-1);
  
  switch (symbol){
  case SYMBOL_VAR:
	 return token_string(scanner,&onehslice->var);
  case SYMBOL_MIN:
	 return token_float(scanner, &onehslice->min);
  case SYMBOL_MAX:
	 return token_float(scanner, &onehslice->max);
  case SYMBOL_HEIGHT:
	 return token_float(scanner, &onehslice->height);
  case SYMBOL_COLOR_TABLE:
	 if(!onehslice->sample)
		onehslice->sample = g_new0(preview_area, 1);	
	 return token_string(scanner,&(onehslice->sample->name));
  default:
	 return G_TOKEN_SYMBOL;
  }	 
  return G_TOKEN_NONE;
}


static guint
parse_symbol (GScanner *scanner, GList **ProcedureList)
{
  guint symbol;
  guint token;
  Image *oneimage;
  hslicecontrols *onehslice;
  
  /* expect a valid symbol */
  g_scanner_get_next_token (scanner);
  symbol = scanner->token;

  if(scanner->token == '}'){    
	 g_scanner_set_scope(scanner,context[--context_depth]);

	 return G_TOKEN_NONE;
  }
  
  if (symbol < SCOPE_SYMBOL_IMAGE ||
		symbol >= NULL_SYMBOL)
	 return G_TOKEN_SYMBOL;


  g_scanner_get_next_token (scanner);


  switch (context[context_depth]){
  case 0:
	 if(scanner->token != '{')
		return '{';
	 context[++context_depth] = symbol;
	 g_scanner_set_scope(scanner,context[context_depth]);

	 oneimage = image_add_item(NULL, NULL, 0, NULL);
    *ProcedureList = g_list_append(*ProcedureList, (gpointer) oneimage);
	 
	 return G_TOKEN_NONE;
	 break;
  case SCOPE_SYMBOL_IMAGE:
	 if(*ProcedureList==NULL)
		return SCOPE_SYMBOL_IMAGE;
	 oneimage = (Image *) g_list_last(*ProcedureList)->data;

	 switch (symbol){
	 case SYMBOL_NAME:
		if(scanner->token != '=')
		  return '=';
		return token_string(scanner,&oneimage->name);
		break;
	 case SCOPE_SYMBOL_HSLICE:
	 case SCOPE_SYMBOL_CHSLICE:
		if(scanner->token != '{')
		  return '{';
		context[++context_depth] = symbol;
		g_scanner_set_scope(scanner,context[context_depth]);
		onehslice = g_new0(hslicecontrols,1);
		onehslice->var=NULL;
		if(symbol==SCOPE_SYMBOL_HSLICE){
		  image_add_item(oneimage, (gpointer) onehslice, HSLICE, NULL);
		}else{
		  image_add_item(oneimage, (gpointer) onehslice, CHSLICE, NULL);
		}
		return G_TOKEN_NONE;
		break;
	 default:
		return G_TOKEN_SYMBOL;
		break;
	 }
  case SCOPE_SYMBOL_HSLICE:
	 /* expect '=' */
	 if(scanner->token != '=')
		return '=';
    oneimage = (Image *) g_list_last(*ProcedureList)->data;
	 return parse_hslice_symbol(scanner, oneimage, symbol);
	 break;
  case SCOPE_SYMBOL_CHSLICE:
	 /* expect '=' */
	 if(scanner->token != '=')
		return '=';
    oneimage = (Image *) g_list_last(*ProcedureList)->data;
	 return parse_chslice_symbol(scanner, oneimage, symbol);
	 break;
  default:
	 /* flag an unrecognized context */
	 printf("bad context %d\n",symbol);
  }
  return G_TOKEN_NONE;
}

void 
print_hslicecontrols(FILE *fp, hslicecontrols *hslice)
{
  if(! hslice->var)
	 {
		g_print("hslicecontrols is invalid without a variable name\n");
		return;
	 }
  if(hslice->interval){
	 /* this is a contour isoline slice */
	 fprintf(fp,"  hslice {\n");
	 fprintf(fp,"    interval = %g;\n",hslice->interval);
	 fprintf(fp,"    stipple = 0x%x;\n",(unsigned short) hslice->stipple);
	 fprintf(fp,"    width = %d;\n",hslice->linewidth);
	 fprintf(fp,"    color = {%g, %g, %g, %g};\n"
				,hslice->color[0],hslice->color[1],
				hslice->color[2],hslice->color[3]);
  }else{
	 /* this is a contour color filled slice */
	 fprintf(fp,"  chslice {\n");
	 if(hslice->sample && hslice->sample->name)
		fprintf(fp,"    color_table = \"%s\";\n",hslice->sample->name);
  }
  fprintf(fp,"    var = \"%s\";\n",hslice->var);
  fprintf(fp,"    min = %g;\n",hslice->min);
  fprintf(fp,"    max = %g;\n",hslice->max);
  fprintf(fp,"    height = %g;\n",hslice->height);

  fprintf(fp,"  }\n");
}

void
print_ProcedureList(GList *ProcedureList,gchar *filename)
{
  GList *nextimage;
  GArray *item_types;
  GPtrArray *items;
  FILE *fp;
  gchar *name;
  gint i;

  fp = fopen(filename,"w");

  nextimage = ProcedureList;
  
  while(nextimage){
	 fprintf(fp,"image { \n");
	 if( (name= ((Image *)nextimage->data)->name)){
		fprintf(fp,"  name=\"%s\";\n",name);
	 }
	 items = (GPtrArray *) ((Image *)nextimage->data)->items;
	 item_types = (GArray *) ((Image *)nextimage->data)->item_type;
	 for(i=0;i<items->len;i++){
		switch(g_array_index(item_types,gint,i)){
		case CHSLICE:
		case HSLICE:
		  print_hslicecontrols(fp, (hslicecontrols *) g_ptr_array_index(items,i));
		  break;
		default:
		  g_print("What is this %d\n",g_array_index(item_types,gint,i));
		}
	 }
	 fprintf(fp,"}\n");
	 nextimage = g_list_next(nextimage);
  }
  fclose(fp);
}




GList *procedure_parse_file(int filedescriptor)
{
  GScanner *scanner;
  guint expected_token;
  GList *ProcedureList=NULL;

  Symbols *symbol_p = symbols;  

  scanner = g_scanner_new (NULL);

  /* adjust lexing behaviour to suit our needs
	*/
  /* convert non-floats (octal values, hex values...) to G_TOKEN_INT */
  scanner->config->numbers_2_int = TRUE;
  /* don't return G_TOKEN_SYMBOL, but the symbol's value */
  scanner->config->symbol_2_token = TRUE;
  
  /* load symbols into the scanner */
  while (symbol_p->symbol_name)
	 {
		g_scanner_scope_add_symbol (scanner,symbol_p->symbol_context,
									 symbol_p->symbol_name,
									 GINT_TO_POINTER (symbol_p->symbol_token));
		symbol_p++;
	 }
  

  g_scanner_input_file(scanner,filedescriptor);

  /* give the error handler an idea on how the input is named */
  scanner->input_name = "test text";

  /* scanning loop, we parse the input until its end is reached,
	* the scanner encountered a lexing error, or our sub routine came
	* across invalid syntax
	*/
  do
	 {
		expected_token = parse_symbol (scanner, &ProcedureList);

		g_scanner_peek_next_token (scanner);
	 }
  while (expected_token == G_TOKEN_NONE &&
			scanner->next_token != G_TOKEN_EOF &&
			scanner->next_token != G_TOKEN_ERROR);
  
  /* give an error message upon syntax errors */
  if (expected_token != G_TOKEN_NONE )
	 g_scanner_unexp_token (scanner, expected_token, NULL, "symbol", NULL, NULL, TRUE);
  
  /* finish parsing */

  g_scanner_destroy (scanner);
  
  
  return ProcedureList;
}

void hslice_free(hslicecontrols *hslice){
  if(hslice){
	 if(hslice->var)
		g_free(hslice->var);
	 g_free(hslice);
  }
}



void procedure_free_image(Image *image)
{
  gint i;
  gint type;
  if(image){
	 if(image->name)
		g_free(image->name);
		

	 for(i=0;i<image->items->len;i++){
		type = g_array_index(image->item_type,gint,i);
		switch (type){
		case CHSLICE:
		case HSLICE:
		  hslice_free((hslicecontrols *) g_ptr_array_index(image->items,i));
		  break;
		default:
		  g_print("Unrecogized type in free %d\n",type);
		  
		}
	 }
	 g_array_free(image->item_type,TRUE);
	 
	 g_ptr_array_free(image->items,TRUE);
  }		 				

}

void procedure_free(GList *Procedure)
{
  GList *imagelist;
  Image *image;
 

  imagelist = g_list_first(Procedure);
  while(imagelist){
	 image = (Image *) imagelist->data;
	 
	 procedure_free_image(image);
	 
	 imagelist = g_list_next(imagelist);
  }
  g_list_free(Procedure);

}


GList *procedure_open_file(char *filename)
{
  int fd;
  GList *ProcedureList;

  fd = open(filename,O_RDONLY);
  ProcedureList = procedure_parse_file(fd);
  close(fd);
  return(ProcedureList);
}



syntax highlighted by Code2HTML, v. 0.9.1