/*-=-=-=-=-=-=-=-=-=-=-=- 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:	output
 *
 * DESCRIPTION:	Routines for printing values and sending them to the
 *		standard output and log file, if appropriate.
 *  
 * AUTHOR/DATE:	John Lloyd, Summer 1987
 *
 *~=~=~=~=~=~= McGill-Computer-Vision-and-Robotics-Laboratory ~=~=~=~=~=~=*/

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <varargs.h>
#include "cdefs.h"
#include "types.h"
#include "parameters.h"

#define DEFAULT_LINE_WIDTH	80
#define DEFAULT_TAB_SIZE	8
#define LEFT_MARGIN	 	4

static int outputBase = 10;
static int doubleOutputCode = SHORT;

extern int lineWidth;
extern int tabSize;
int lineWidth = DEFAULT_LINE_WIDTH;
int tabSize = DEFAULT_TAB_SIZE;

/* Change the default output formats for either integers or floats */

setOutputBase (x)
int x;
{
	if (x < 2 || x > 16)
	 { x = 10;
	 }
	else
	 { outputBase = x;
	 }
}

setOutput (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	int i;

	switch (a[0].type)
	 { case INTEGER:
	       { if ((i = acceptOutputBaseInteger(a[0].val.i)) == -1)
		  { return (-1);
		  }
		 else
		  { outputBase = i;
		  }
		 break;
	       }
	   case STRING:
	       { if ((i = acceptOutputBaseString(a[0].val.s)) == -1)
		  { return (-1);
		  }
		 if (i < DEFAULT)
		  { outputBase = i;
		  }
		 else
		  { doubleOutputCode = i;
		  }
		 break;
	       }
	   default:
	       { 
error (0, "`output()' accepts only strings and scalars\n");
		 return (-1);
		 break;
	       }
	 }
 	return (1);
}

/* Parse the string specifying the new output format */

acceptOutputBaseString (s)
char *s;
{
	if (strcmp (s, "x") == 0 || strcmp (s, "X") == 0)
	 { return (16);
	 }
	else if (strcmp (s, "d") == 0 || strcmp (s, "D") == 0)
	 { return (10);
	 }
	else if (strcmp (s, "o") == 0 || strcmp (s, "O") == 0)
	 { return (8);
	 }
 	else if (strcmp (s, "b") == 0 || strcmp (s, "B") == 0)
	 { return (2);
	 }
	else if (strcmp (s, "z") == 0 || strcmp (s, "Z") == 0)
	 { return (MACHINE);
	 }
 	else if (strcmp (s, "lf") == 0 || strcmp (s, "LF") == 0)
	 { return (LONG);
	 }
 	else if (strcmp (s, "f") == 0 || strcmp (s, "F") == 0)
  	 { return (SHORT);
	 }
 	else if (strcmp (s, "le") == 0 || strcmp (s, "LE") == 0)
	 { return (LONG_E);
	 }
 	else if (strcmp (s, "e") == 0 || strcmp (s, "E") == 0)
  	 { return (SHORT_E);
	 }
	else if ( (strlen (s) == 1 && isdigit(s[0])) ||
		  (strlen (s) == 2 && isdigit(s[0]) && isdigit(s[1])) )
  	 { int b = atoi(s);

	   if (b < 2 || b > 16)
	    { error (0, "legal output base values range from 2 to 16\n");
	      return (-1);
	    }
	   return (b);
	 }
	else
 	 { 
error (0, "valid output base codes are digits and <x,d,o,b,z,f,e,lf,le>\n");
	   return (-1);
	 }
}

/* Check an integer specifying a new output base */

acceptOutputBaseInteger (i)
int i;
{
	if (i < 2 || i > 16)
	 { error (0, "legal output base values range from 2 to 16\n");
	   return (-1);
	 }
 	else
	 { return (i);
	 }
}

defaultOutputBase ()
{
	return (outputBase);
}

defaultDoubleOutputCode ()
{
	return (doubleOutputCode);
}

/* Basic routine for printing values. Calls the approprite sub-procedure */

printValue (g, resultNumber, IOcode)
GTYPE *g;
int resultNumber, IOcode;
{
	int base, dcode;

	if (!IOcode)
 	 { base = defaultOutputBase();
	   dcode = defaultDoubleOutputCode();
	 }
	else if (2 <= IOcode && IOcode <= 16)
 	 { base = IOcode;
	   dcode = defaultDoubleOutputCode();
	 }
 	else
 	 { base = defaultOutputBase();
	   dcode = IOcode;
	 }

	switch (g->type)
 	 { case INTEGER:
	      { printInteger (g->val.i, base);
		break;
	      }
	   case DOUBLE:
	      { matrix m;

		m.row = m.col = 1;
		m.buf = &g->val.d;
	        printMatrix (&m, resultNumber, dcode, base);
	        break;
	      }
	   case MATRIX:
	      { printMatrix (g->val.m, resultNumber, dcode, base);
	        break;
	      }
	   case STRING:
	      { printOut ("\n    ");
		printOut ("%s\n", g->val.s);
		printOut ("\n");
	        break;
	      }
	 }
}

static char obuf[256];

printInteger (i, base)
int i, base;
{
	int fieldWidth, fw;

	printOut ("\n    ");
	if (base == 2 || base == 4 || base == 8 || base == 16)
	 { print_base (obuf, i, base, UNSIGNED);
	 }
 	else
 	 { print_base (obuf, i, base, SIGNED);
	 }
	fieldWidth = ((strlen(obuf)+1)/6+1)*6;
	for (fw = strlen(obuf); fw < fieldWidth; fw++)
  	 { putOut (' ');
	 }
  	printOut ("%s\n", obuf);
	putOut ('\n');
}

printMatrix (m, resultNumber, IOcode, base)
matrix *m;
int resultNumber, IOcode, base;
{
	int i, j, k, row, col, numcols;
	int fieldWidth;
	int ks;
	double x;
	double max = -HUGE;
	double min =  HUGE;
	double scale;
	int type = INTEGER;
	char *format;
	
	/* find the maximum value of the matrix, and see the matrix
	   contains all `integer' entries */

	row = m->row;
	col = m->col;
	for (i=0; i<row; i++)
	 { for (j=0; j<col; j++)
	    { x = m->buf[i*col+j];
	      if (x > max)
	       { max = x;
	       }
	      if (x < min)
	       { min = x;
	       }
	      if ((k = x) != x)
	       { type = DOUBLE;
	       }
	    }
	 }

	/* If the matrix is 'integer', then see how wide the field
	   width has to be to print the matrix in the specified
 	   integer base */

	if (type == INTEGER && MININT <= min && max <= MAXINT)
	 { int fw, ival = MAX (FABS(min), FABS(max));

	   print_base (obuf, ival, base, UNSIGNED);
	   fieldWidth = ((strlen(obuf)+1)/6+1)*6;
	   scale = 1.0;
	 }
 	else
 	 { type = DOUBLE;
	 }

	if (type == DOUBLE)
 	 { 
	   /* Compute the position of the maximum matrix value to the left
	      (or right) of the decimal point. This value is stored in the
	      variable ks */

	   max = MAX (FABS(min), FABS(max));
	   if (max < SMALLEST)
	    { ks = 0;
	    }
	   else
	    { double l = log10 (max);
	      if (l <= 0 && l != ((int)l))
	       { ks = l-1;
	       }
	      else
	       { ks = l;
	       }
	    }
	   switch (IOcode)
	    { case SHORT:
		 if (col == 1 && row == 1)	/* printing single number */
		  { if (-2 <= ks && ks <= 4)
		     { ks = 0;
		     }
		    if (ks != 0)
		     { IOcode = SHORT_E;
		     }
		  }
		 else
		  { if (-2 <= ks && ks <= 2)
		     { ks = 0;
		     }
		  }
		 break;
	      case LONG:
		 if (col == 1 && row == 1)
		  { if (-2 <= ks && ks <= 2)
		     { ks = 0;
		     }
		    if (ks != 0)
		     { IOcode = LONG_E;
		     }
		  }
		 else
		  { if (-2 <= ks && ks <= 1)
		     { ks = 0;
		     }
		  }
		 break;
	    }
	   switch (IOcode)
	    { 
	      case SHORT:
	         format = "%11.4f";
		 fieldWidth = 11;
		 scale = pow(10.0,(double)ks);
		 break;
	      case LONG:
		 format = "%19.14f";
		 fieldWidth = 19;
		 scale = pow(10.0,(double)ks);
		 break;
	      case SHORT_E:
		 format = "%13.5e";
		 fieldWidth = 13;
		 scale = 1.0;
		 break;
	      case LONG_E:
		 format = "%24.15e";
		 fieldWidth = 24;
		 numcols = 3;
		 scale = 1.0;
		 break;
	      case MACHINE:
		 format = "  0%08x%08x";
		 fieldWidth = 19;
		 scale = 1.0;
		 break;		
	    }
	 }

	numcols = (lineWidth-LEFT_MARGIN) / fieldWidth;

	if (scale != 1.0)
	 { for (j=0; j<LEFT_MARGIN; j++)
	    { putOut (' ');
	    }
	   printOut ("\n  %5.0e *\n\n", scale);
	 }
 	else
  	 { putOut ('\n');
	 }
	for (k=0; k<col; k+=numcols)
 	 { int jcols = col-k;
	   if (jcols > numcols)
	    { jcols = numcols;
	    }
	   if (numcols < col)
	    { for (j=0; j<LEFT_MARGIN; j++)
	       { putOut (' ');
	       }
	      if (jcols == 1)
	       { printOut ("  column %d\n", k+1);
	       }
	      else
	       { printOut ("  columns %d through %d\n", k+1, k+jcols);
	       }
	    }
	   for (i=0; i<row; i++)
	    { for (j=0; j<LEFT_MARGIN; j++)
	       { putOut (' ');
	       }
	      for (j=k; j<k+jcols; j++)
	       { if (type == DOUBLE)
		  { printOut (format, ELEMENT (m, i, j)/scale);
		  }
		 else
		  { int fw, ival;

		    ival = ELEMENT (m, i, j);
		    if (base == 2 || base == 4 || base == 8 || base == 16)
		     { print_base (obuf, ival, base, UNSIGNED);
		     }
		    else
		     { print_base (obuf, ival, base, SIGNED);
		     }
		    fw = strlen(obuf);
		    for (fw = strlen(obuf); fw < fieldWidth; fw++)
		     { putOut (' ');
		     }
		    printOut ("%s", obuf);
		  }
	       }
	      putOut ('\n');
	    }
	   putOut ('\n');
	 }
}

#define STACK_SIZE 100

/* print the integer 'number' into 'string' with the given 'base',
   using either a signed or unsigned format depending on the value
   of 'signed' */

print_base (string, number, base, signedp)
char *string;
int number, base, signedp;
{
	unsigned int output;
	char stack [STACK_SIZE];
	int sp, j;

	if (base > 16 || base < 1)
	 { return (-1);
	 }

	if (signedp)
	 { output = (unsigned int) ((number < 0) ? -number : number);
	 }
	else
	 { output = (unsigned int) (number);
	 }
	
	sp = STACK_SIZE;
	stack [--sp] = 0;
	while (output != 0)
	 { 
	   if ((output % base) < 10)
	    { stack [--sp] = '0' + (output % base);
	    }
	   else
	    { stack [--sp] = 'a' + (output % base) - 10;
	    }
	   output = output / base;
	 }
	if (sp == STACK_SIZE-1 || evalchar (stack[sp]) >= 10)
	 { stack [--sp] = '0';
	 }
	if (signedp && number < 0)
	 { stack [--sp] = '-';
	 }
	strcpy (string, &stack[sp]);
}

/* ------- Routines for maintaining log files ------- */

FILE *logFile;
int logEnable;

openLogFile (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	if (logFile != NULL)
 	 { fclose (logFile);
	 }
	if ((logFile = fopen (a[0].val.s, "w")) == NULL)
  	 { error (0, "%s() -- cannot open file %s\n", 
		     r->name, a[0].val.s);
	   return (-1);
	 }
	r->val.i = 1;
	logEnable = 0;	/* set by the prompt function */
	return (1);
}

closeLogFile (r, n, a)
GTYPE *r;
int n;
GTYPE *a;
{
	if (logFile != NULL)
 	 { putc ('\n', logFile);
	   fclose (logFile);
	   logFile = NULL;
	 }
 	r->val.i = 1;
	return (1);
}

/* ------- the prompt routine ------- */

static terminal = UNDEF;

printPrompt (n)
int n;
{
	if (terminal == UNDEF)
 	 { terminal = isatty (fileno(stdout));
	 }
	if (!logEnable)
	 { logEnable = 1;
	 }
	if (terminal)
	 { sprintf (obuf, "ARC-%d-> ", n);
	   printOut ("%s", obuf);
	   return (strlen(obuf));
	 }
 	else
  	 { return (0);
	 }
}

/* -------- The basic output routines -------- */	
	
static int normalOutput = 1;

struct space {
	unsigned long l[8];
};

#ifdef vax
#define vfprintf(fp,fmt,ap)	_doprnt(fmt,ap,fp)
#endif

printOut (va_alist)
va_dcl
{
	char *fmt;
	va_list ap;

	va_start(ap);
	fmt = va_arg(ap, char*);

	if (logFile && logEnable)
	 { vfprintf (logFile, fmt, ap);
	 }
	if (normalOutput)
	 { vfprintf (stdout, fmt, ap);
	 }
	va_end(ap);
}

putOut (c)
char c;
{
	if (logFile && logEnable)
  	 { putc (c, logFile);
	 }
	if (normalOutput)
	 { putc (c, stdout);
	 }
}

putLog (c)
char c;
{
	if (logFile && logEnable)
	 { putc (c, logFile);
	 }
}
