/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

/*=~=~=~=~ McGill-Computer-Vision-and-Robotics-Laboratory--C-Module ~=~=~=~=
 *		
 * MODULE NAME:	fileIO
 *
 * DESCRIPTION:	Functions to perform the saving and restoring of variables
 *		from a file.
 * 
 * AUTHOR/DATE:	John Lloyd, Summer 1987
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include <stdio.h>
#include <ctype.h>
#include "cdefs.h"
#include "types.h"
#include "symbols.h"

extern matrix fullrange;
#define FULLRANGE (&fullrange)

SYMBOL_TABLE_ENTRY *locateSymbol();
SYMBOL_TABLE_ENTRY *insertSymbol();
SYMBOL_TABLE_ENTRY *firstSymbol();
SYMBOL_TABLE_ENTRY *nextSymbol();

saveVariables (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	FILE *fp;
	int warn = 0;
	int i;


	if (a[0].type == STRING)
  	 { if ((fp = fopen (a[0].val.s, "w")) == NULL)
	    { error (0,"%s() -- cannot open file %s\n",r[0].name, a[0].val.s);
	      return (-1);
	    }
	 }
 	else
 	 { error (0, "%s() -- first argument must be a string\n", r[0].name);
	   return (-1);
	 }

	if (n == 1)
	 { /* save everything in the symbol table */

	   SYMBOL_TABLE_ENTRY *s;

	   for (s = firstSymbol(); s != NULL; s = nextSymbol())
	    { if (!historyName (s->name))
	       { writeVariable (fp, s->name, &s->g, NULL, NULL);
	       }
	    }
	 }
	else
	 { 
	   for (i=1; i<n; i++)
	    { if (a[i].name &&
		  ((a[i].row_i == NULL && a[i].col_i == NULL) ||
		   (a[i].row_i == FULLRANGE && a[i].col_i == NULL) ||
		   (a[i].row_i == FULLRANGE && a[i].col_i == FULLRANGE)))
	       { SYMBOL_TABLE_ENTRY *s;
   
		 if ((s = locateSymbol(a[i].name)) == NULL)
		  { printf ("WARNING: %s() -- symbol %s not defined\n",
			    r[0].name, a[i].name);
		  }
		 else
		  { writeVariable (fp, a[i].name, &s->g, a[i].row_i, a[i].col_i);
		  }
	       }
	      else
	       { warn = 1;
	       }
	    }
   
	   if (warn)
	    { 
   printf ("WARNING: %s() -- non-variable expressions encountered and ignored\n",
	      r[0].name);
	    }
	 }

	fclose (fp);
	r->val.i = 1;
	return (1);
}

historyName (s)
register char *s;
{
	if (*s != '$')
	 { return (0);
	 }
	s++;
	if (!isdigit(*s))
	 { return (0);
	 }
	s++;
	while (*s != '\0')
	 { if (!isdigit(*s))
	    { return (0);
	    }
	   s++;
	 }
	return (1);
}

/* writes a single variable with value 'g1' and 'name' to the file
   described by 'fp'. Indexing information is included for future
   use only */

writeVariable (fp, name, g1, row_i, col_i)
FILE *fp;
char *name;
GTYPE *g1;
matrix *row_i, *col_i;
{
	switch (g1->type)
	 { case INTEGER:
	       { fprintf (fp, "( %s integer\n", name);
		 fprintf (fp, "  %d\n", g1->val.i);
		 fprintf (fp, ")\n");
		 break;
	       }
	   case DOUBLE:
	       { fprintf (fp, "( %s double\n", name);
		 fprintf (fp, "  %23.16e\n", g1->val.d);
		 fprintf (fp, ")\n");
		 break;
	       }
	   case MATRIX:
	       { matrix *m = g1->val.m;
		 int columnsPerRow = 3;
		 int i, j, k;
		 
		 fprintf (fp, "( %s matrix %d %d\n", name, m->row, m->col);
		 for (i=0; i<m->row; i++)
		  { k = 0;
		    for (j=0; j<m->col; j++)
		     { fprintf (fp, "  %23.16e", ELEMENT (m, i, j));
		       if (++k == columnsPerRow)
			{ putc ('\n', fp);
			  k = 0;
			}
		     }
		    if (k != 0)
		     { putc ('\n', fp);
		     }
		  }
		 fprintf (fp, ")\n");
		 break;
	       }
	   case STRING:
	       { int c;
		 char *s = g1->val.s;

		 fprintf (fp, "( %s string %d\n", name, strlen(s));
		 fprintf (fp, "  \"");
		 while (c = *s++)
		  { if (c == '"')
		     { putc ('\\', fp);
		     }
		    putc (c, fp);
		  }
		 fprintf (fp, "\"\n)\n");
		 break;
	       }
	 }
 }

/* Utility function used in reading a file: skips over white space and
   increments the line count if necessary */

static int lineNumber = 0;

skipWhiteSpace (fp)
FILE *fp;
{
	int c, i = 0;

	while (isspace(c = getc(fp)))
  	 { i++;
	   if (c == '\n')
	    { lineNumber++;
	    }
	 }
 	ungetc (c, fp);
 	return (i);
}

extern GTYPE *mcopy_gtype();
#define IBUFSIZE	1024

loadVariables (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	FILE *fp;
	GTYPE g;
	SYMBOL_TABLE_ENTRY *entry;

	char namebuf[IBUFSIZE+2];
	int status, i;

	if (a[0].type == STRING)
  	 { if ((fp = fopen (a[0].val.s, "r")) == NULL)
	    { error (0,"%s() -- cannot open file %s\n",r[0].name, a[0].val.s);
	      return (-1);
	    }
	 }
 	else
 	 { error (0, "%s() -- first argument must be a string\n", r[0].name);
	   return (-1);
	 }

	g.col_i = NULL;
 	g.row_i = NULL;
	lineNumber = 1;

	while ((status = readVariable (fp, namebuf, &g, r[0].name)) == 1)
  	 { if ((entry = locateSymbol (namebuf)) == NULL)
	    { if ((entry = insertSymbol (namebuf)) == NULL)
	       { return (-1);
	       }
	      entry->protection = 0;
	    }
	   else if (entry->protection == WRITE)
	    { continue;
	    }
	   else
	    { free_gtype (&entry->g);
	    }
	   mcopy_gtype (&entry->g, &g);
	 }

	fclose (fp);
	r->val.i = 1;
	if (status == EOF || status == 1)
  	 { return (1);
	 }
 	else
 	 { return (-1);
	 }
}

/* attempts to read a single variable from the file 'fp' and return
   its 'name' and value 'g'. 'fname' is the name of user level funtion
   for purposes of constructing error messages */

readVariable (fp, name, g, fname)
FILE *fp;  
char *name;
GTYPE *g;
char *fname;
{
#define TYPSIZE 64
	int c;
	char typename[TYPSIZE+1];

	skipWhiteSpace(fp);
	if ((c = getc(fp)) != '(')
  	 { if (c == EOF)
	    { return (EOF);
	    }
	   else
	    { error (0, "%s() -- missing '(' near line %d\n",
	   	     fname, lineNumber);
	    }
	   return (0);
	 }
	skipWhiteSpace(fp); 

	if (fscanf (fp, "%1024s", name) != 1)
  	 { error (0, "%s() -- file read error near line %d\n",
		     fname, lineNumber);
	   return (0);
	 }
	if (skipWhiteSpace(fp) == 0)
  	 { error (0, "%s() -- variable name too long near line %d\n",
		     fname, lineNumber);
	   return (0);
	 }
	if (fscanf (fp, "%64s", typename) != 1)
  	 { error (0, "%s() -- file read error near line %d\n",
		     fname, lineNumber);
	   return (0);
	 }
	typename[TYPSIZE] = '\0';

	if (strcmp (typename, "integer") == 0)
  	 { g->type = INTEGER;
	 }
	else if (strcmp (typename, "double") == 0)
  	 { g->type = DOUBLE;
	 }
	else if (strcmp (typename, "matrix") == 0)
  	 { g->type = MATRIX;
	 }
	else if (strcmp (typename, "string") == 0)
  	 { g->type = STRING;
	 }
        else
 	 { error (0, "%s() -- bad type name near line %d\n",
		     fname, lineNumber);
	   return (0);
	 }

	skipWhiteSpace(fp);

	switch (g->type)
 	 { case INTEGER:
	       { int i;
		 if (fscanf (fp, "%d", &i) != 1)
		  { error (0, "%s() -- bad integer format near line %d\n",
			      fname, lineNumber);
		    return (0);
		  }
		 g->val.i = i;
		 break;
	       }
	   case DOUBLE:
	       { double d;
		 if (fscanf (fp, "%le", &d) != 1)
		  { error (0, "%s() -- bad double format near line %d\n",
			      fname, lineNumber);
		    return (0);
		  }
		 g->val.d = d;
		 break;
	       }
	   case MATRIX:
	       { matrix *m;
		 int i, j, row, col;
		 if (fscanf (fp, "%d", &row) != 1)
		  { error (0, "%s() -- bad matrix size spec. near line %d\n",
			      fname, lineNumber);
		    return (0);
		  }
		 skipWhiteSpace(fp);
		 if (fscanf (fp, "%d", &col) != 1)
		  { error (0, "%s() -- bad matrix size spec. near line %d\n",
			      fname, lineNumber);
		    return (0);
		  }
		 if ((m = (matrix *)malloc_matrix (row, col)) == NULL)
		  { return (0);
		  }
		 for (i=0; i<row; i++)
		  { for (j=0; j<col; j++)
		     { double d;
		       skipWhiteSpace(fp);
		       if (fscanf (fp, "%le", &d) != 1)
			{ error (0, "%s() -- bad matrix entry near line %d\n",
			 	    fname, lineNumber);
			  free (m);
			  return (0);
			}
		       ELEMENT (m, i, j) = d;
		     }
		  }
		 g->val.m = m;
		 break;
	       }
	   case STRING:
	       { char *s;
		 int i, c1, c2, size;
		 if (fscanf (fp, "%d", &size) != 1)
		  { error (0, "%s() -- bad string size spec. near line %d\n",
			      fname, lineNumber);
		    return (0);
		  }
		 if ((s = (char *)malloc (size+1)) == NULL)
		  { error(0, "out of memory\n");
		    return (0);
		  }
		 skipWhiteSpace(fp);
		 if (getc (fp) != '"')
		  { error (0, "%s() -- missing \" near line %d\n",
			      fname, lineNumber);
		    free (s);
		    return (0);
		  }
		 for (i=0; i<size; i++)
		  { c1 = getc(fp);

		    if (c1 == '\\')
		     { if ((c2 = getc(fp)) == '"')
			{ c1 = '"';
			}
		       else
			{ ungetc (c2, fp);
			}
		     }
		    s[i] = c1;
		    if (c1 == '\n')
		     { lineNumber++;
		     }
		  }
		 s[size] = '\0';
		 skipWhiteSpace(fp);
		 if (getc (fp) != '"')
		  { error (0, "%s() -- missing \" near line %d\n",
			      fname, lineNumber);
		    free (s);
		    return (0);
		  }
		 g->val.s = s;
		 break;
	       }
	 }

	skipWhiteSpace(fp);
	if (getc (fp) != ')')
  	 { error (0, "%s() -- missing ')' near line %d\n",
	   	     fname, lineNumber);
	   if (g->type == STRING)
	    { free (g->val.s);
	    }
	   else if (g->type == MATRIX)
	    { free (g->val.m);
	    }
	   return (0);
	 }
	return (1);	
}
