/*******************************************************\
* 							*
*              @(#)surface.c	1/2/89			*
*	Author:       Tony Plate			*
*	Copyright (C) 1989 Tony Plate			*
*	This program may be freely distributed,		*
*	provided that this copyright notice is		*
*	retained intact.  There is no warranty		*
*	with this software.				*
* 							*
\*******************************************************/

/*
 * read a matrix, and output it in one of 4 formats:
 * The X and Y directions are used for the positions of values, the Z direction
 * is used to represent the value.
 */

#include <math.h>
#include <stdio.h>
#include <ctype.h>
#include "read_matrix.h"
#include "surfproto.h"

char *options_description[] = {
"Options: (an unambiguous substring is sufficient)",
"-array		: display as a matrix (same format as input)",
"-ascii		: ascii format",
"-binary		: binary output (default)",
"-brief		: display as a matrix, using brief format numbers",
"-delimit <DELIMITER>	: only take material from inside the",
"		  two lines containing the string <DELIMITER>",
"-grid		: draw 3d grid lines (default)",
"-help		: display this information",
"-histogram	: histogram style",
"-polygon	: sqare polygons",
"-squaregrid	: change the y upper bound so that the grid is square",
"-suppresstext	: suppress text following the array",
"-transpose	: transpose the x and y-axis",
"-triangle	: triangular polygons",
"-unit		: the input surface is in the XY unit square",
"-x <low> <high>: specify the X bounds of the input surface",
"-xlines		: just draw the lines in the x-direction",
"-y <low> <high>: specify the Y bounds of the input surface",
"-ylines		: just draw the lines in the y-direction",
NULL
};


#define DB(X) 
enum {ARRAY,GRID,X_GRID,Y_GRID,BRIEF,TRIANGLES,SQUARES} output_format;
int transpose=0;
int histogram=0;
int squaregrid=0;
int text_follow=1;
float xlow=0;
float xhigh=0;
float ylow=0;
float yhigh=0;
int x_dim,y_dim;

enum {ASCII,BINARY} number_format=BINARY;
#define LEN 16384
char str[LEN];

int print_options(void)

{
  char **p;
  p = options_description;
  while (*p) printf("%s\n",*p++);
}


int main(int argc ,
         char *argv[] )

{
  int x,y,label_found;
  char *p,*label,*my_name;
  float *matrix;
  my_name = argv[0];
  output_format = GRID;
  label = NULL;
  while (*(++argv)) {
    p = argv[0];
    if (*p == '-') {
      p++;
      if (prefix(p,"array"))
	output_format = ARRAY;
      else if (prefix(p,"ascii"))
	number_format = ASCII;
      else if (prefix(p,"binary"))
	number_format = BINARY;
      else if (prefix(p,"brief"))
	output_format = BRIEF;
      else if (prefix(p,"delimit"))
	label = *(++argv);
      else if (prefix(p,"grid"))
	output_format = GRID;
      else if (prefix(p,"help")) {
	print_options();
	exit(0);
      } else if (prefix(p,"histogram"))
	histogram = 1;
      else if (prefix(p,"polygon"))
	output_format = SQUARES;
      else if (prefix(p,"transpose"))
	transpose = 1;
      else if (prefix(p,"triangle"))
	output_format = TRIANGLES;
      else if (prefix(p,"x")) {
	if (argv[1]==NULL || argv[2]==NULL) {
	  fprintf(stderr,"%s: expected two numbers after -x\n",my_name);
	  exit(1);
	}
	xlow = atof(argv[1]);
	xhigh = atof(argv[2]);
	argv++;
	argv++;
      } else if (prefix(p,"y")) {
	if (argv[1]==NULL || argv[2]==NULL) {
	  fprintf(stderr,"%s: expected two numbers after -y\n",my_name);
	  exit(1);
	}
	ylow = atof(argv[1]);
	yhigh = atof(argv[2]);
	argv++;
	argv++;
      } else if (prefix(p,"unit")) {
	xlow = 0; xhigh = 1;
	ylow = 0; yhigh = 1;
      } else if (prefix(p,"xlines"))
	output_format = X_GRID;
      else if (prefix(p,"ylines"))
	output_format = Y_GRID;
      else if (prefix(p,"squaregrid"))
	squaregrid = 1;
      else if (prefix(p,"suppresstext"))
	text_follow = 0;
      else goto bad_option;
    } else {
      
    bad_option:
      fprintf(stderr,"%s: bad or ambiguous option: %s\nType \"%s -help\" to see list of options\n",my_name,argv[0],my_name);
      exit(1);
    }
  }
  if (label!=NULL) {
    /*
     * find the label string in the file
     */
    label_found=0;
    while (!label_found && fgets(str,LEN,stdin)!=NULL)
      if (substring(label,str)) label_found=1;
    if (!label_found) {
      fprintf(stderr,"%s: label %s not in input\n",my_name,label);
      exit(1);
    }
  }
  if ((matrix = read_matrix(&x_dim,&y_dim))==NULL) exit(1);
  if (xlow==0 && xhigh==0) xhigh = x_dim;
  if (ylow==0 && yhigh==0) yhigh = y_dim;
  if (squaregrid) {
    yhigh = y_dim*(xhigh - xlow)/x_dim + ylow;
  }
  if (transpose) {
    int old_x,old_y,i,start;
    float tmp,new_tmp;
    char *done;
    done = (char*)calloc(x_dim,y_dim);
    /*
     * this is a difficult transpose to do in place,
     * because the array may not be square.
     * we do it by a permutation - work out where 0,1 goes, etc.
     */
    start = 0;
    while (start < x_dim*y_dim) {
      i = start;
      old_y = i / x_dim;
      old_x = i % x_dim;
      tmp = matrix[i];
      /* printf("(%d",i); */
      do {
	done[i]++;
	i = old_x * y_dim + old_y;
	/* printf(",%d",i); */
	new_tmp = matrix[i];
	matrix[i] = tmp;
	tmp = new_tmp;
	old_y = i / x_dim;
	old_x = i % x_dim;
      } while (i!=start);
      /* printf(")\n"); */
      while (done[start] && start<x_dim*y_dim) start++;
    }
    i = y_dim;
    y_dim = x_dim;
    x_dim = i;
    tmp = xlow; xlow = ylow; ylow = xlow;
    tmp = xhigh; xhigh = yhigh; yhigh = xhigh;
  }
  if (number_format==BINARY && output_format!=BRIEF && output_format!=ARRAY)
    printf("binary format float4\n");
  /*
   * horizontal lines, non-histogram
   */
  if (!histogram && (output_format==GRID || output_format==X_GRID))
    for (y=0;y<y_dim;y++)
      for (x=0;x<x_dim;x++)
	point((x==0 ? 'm' : 'n'),x,y,matrix[y*x_dim+x]);
  /*
   * vertical lines, non-histogram
   */
  if (!histogram && (output_format==GRID || output_format==Y_GRID))
    for (x=0;x<x_dim;x++)
      for (y=0;y<y_dim;y++)
	point((y==0 ? 'm' : 'n'),x,y,matrix[y*x_dim+x]);
  /*
   * square panels, non-histogram
   */
  if (!histogram && output_format==SQUARES) {
    for (x=0;x<x_dim-1;x++) {
      for (y=0;y<y_dim-1;y++) {
	point('m',x,y,matrix[y*x_dim+x]);
	point('n',x+1,y,matrix[y*x_dim+x+1]);
	point('n',x+1,y+1,matrix[(y+1)*x_dim+x+1]);
	point('n',x,y+1,matrix[(y+1)*x_dim+x]);
	point('j',x,y,matrix[y*x_dim+x]);
      }
    }
  }
  /*
   * triangular panels, non-histogram
   */
  if (!histogram && output_format==TRIANGLES) {
    fprintf(stderr,"Can't do triangles yet!\n");
    exit(1);
  }
  /*
   * square panels, histogram
   */
  if (histogram && (output_format==SQUARES || output_format==TRIANGLES)) {
    int n_square=0;
    for (x=0;x<x_dim;x++) {
      for (y=0;y<y_dim;y++) {
	DB( fprintf(stderr,"Doing %d %d (%g)\n",x,y,matrix[y*x_dim+x]); )
	/* a flat square */
	point('m',x,y,matrix[y*x_dim+x]);
	point('n',x+1,y,matrix[y*x_dim+x]);
	point('n',x+1,y+1,matrix[y*x_dim+x]);
	point('n',x,y+1,matrix[y*x_dim+x]);
	point('j',x,y,matrix[y*x_dim+x]);
	DB( sprintf(str," sq %d",n_square); point_text(str); )
	/* the vertical square in the x direction */
	if (x<x_dim-1 && matrix[y*x_dim+x]!=matrix[y*x_dim+x+1]) {
	  point('m',x+1,y,matrix[y*x_dim+x]);
	  point('n',x+1,y,matrix[y*x_dim+x+1]);
	  point('n',x+1,y+1,matrix[y*x_dim+x+1]);
	  point('n',x+1,y+1,matrix[y*x_dim+x]);
	  point('j',x+1,y,matrix[y*x_dim+x]);
	}
	/* the vertical square in the y direction */
	if (y<y_dim-1 && matrix[y*x_dim+x]!=matrix[(y+1)*x_dim+x]) {
	  point('m',x,y+1,matrix[y*x_dim+x]);
	  point('n',x,y+1,matrix[(y+1)*x_dim+x]);
	  point('n',x+1,y+1,matrix[(y+1)*x_dim+x]);
	  point('n',x+1,y+1,matrix[y*x_dim+x]);
	  point('j',x,y+1,matrix[y*x_dim+x]);
	}
	n_square++;
      }
    }
  }
  if (output_format==BRIEF) {
    printf("%d %d\n",x_dim,y_dim);
    for (y=0;y<y_dim;y++) {
      for (x=0;x<x_dim;x++)
	printf("%s",sn(matrix[y*x_dim + x]));
      printf("\n");
    }
  }
  if (output_format==ARRAY) {
    printf("%d %d\n",x_dim,y_dim);
    for (y=0;y<y_dim;y++) {
      for (x=0;x<x_dim;x++)
	printf(" %11g",matrix[y*x_dim + x]);
      printf("\n");
    }
  }
  /*
   * horizontal lines, histogram
   */
  if (histogram && (output_format==GRID || output_format==Y_GRID)) {
    for (y=0;y<=y_dim;y++) {
      if (y>0) {
	point_next('m');
	for (x=0;x<=x_dim;x++) {
	  if (x>0)
	    point('n',x,y,matrix[(y-1)*x_dim + x-1]);
	  if (x<x_dim &&
	      (x==0 || (matrix[(y-1)*x_dim + x-1]!=matrix[(y-1)*x_dim + x])))
	    point('n',x,y,matrix[(y-1)*x_dim + x]);
	}
      }
      if (y<y_dim) {
	point_next('m');
	for (x=0;x<=x_dim;x++) {
	  if (x>0) point('n',x,y,matrix[y*x_dim + x-1]);
	  if (x<x_dim
	      && (x==0 || (matrix[y*x_dim + x-1]!=matrix[y*x_dim + x])))
	    point('n',x,y,matrix[y*x_dim + x]);
	}
      }
    }
  }
  /*
   * vertical lines, histogram
   */
  if (histogram && (output_format==GRID || output_format==X_GRID)) {
    for (x=0;x<=x_dim;x++) {
      if (x>0) {
	point_next('m');
	for (y=0;y<=y_dim;y++) {
	  if (y>0) point('n',x,y,matrix[(y-1)*x_dim + x-1]);
	  if (y<y_dim
	      && (y==0 || (matrix[(y-1)*x_dim + x-1]!=matrix[y*x_dim + x-1])))
	    point('n',x,y,matrix[y*x_dim + x-1]);
	}
      }
      if (x<x_dim) {
	point_next('m');
	for (y=0;y<=y_dim;y++) {
	  if (y>0) point('n',x,y,matrix[(y-1)*x_dim + x]);
	  if (y<y_dim
	      && (y==0 || (matrix[(y-1)*x_dim + x]!=matrix[y*x_dim + x])))
	    point('n',x,y,matrix[y*x_dim + x]);
	}
      }
    }
  }
  point_end();
  label_found=0;
  if (text_follow)
    while (fgets(str,LEN,stdin)!=NULL && !label_found)
      if (!isblank(str)) {
	if (label!=NULL && substring(label,str)) label_found=1;
	else {
	  /* if (number_format==BINARY) putc('t',stdout); */
	  fputs(str,stdout);
	}
      }
}

static prev_type=0;
static use_type=0;

int point_end(void)

{
  if (number_format==ASCII) {
    if (prev_type=='m' || prev_type=='n')
      printf("\" \n");
  } else if (number_format==BINARY) {
    /*
    if (prev_type=='m' || prev_type=='n')
      putc('e',stdout);
     */
  }
  prev_type = 0;
}

int point_text(char *text )

{
  if (number_format==ASCII) {
    printf("\"%s\n",text);
  } else if (number_format==ASCII) {
    printf("t\"%s\n",text);
  }
  prev_type = 0;
}

int point_next(char type )

{
  use_type = type;
}

int point(char type ,
          int x ,
          int y ,
          double z )

{
  float yf,xf;
  float zf = z;
  if (histogram) {
    yf = (y-0.5)*(yhigh-ylow)/(y_dim-1) + ylow;
    xf = (x-0.5)*(xhigh-xlow)/(x_dim-1) + xlow;
  } else {
    yf = y*(yhigh-ylow)/(y_dim-1) + ylow;
    xf = x*(xhigh-xlow)/(x_dim-1) + xlow;
  }
  if (use_type) { type = use_type; use_type = 0; }
  if (number_format==ASCII) {
    if (type=='j') type='n';
    if (type=='m' && (prev_type=='m' || prev_type=='n')) printf("\" \n");
    printf("%g %g %g\n",xf,yf,zf);
  } else if (number_format==BINARY) {
    if (type=='j') {
      putc('j',stdout); /* join up */
      type='n';
    } else {
      putc(type,stdout);
      fwrite(&xf,sizeof(xf),1,stdout);
      fwrite(&yf,sizeof(yf),1,stdout);
      fwrite(&zf,sizeof(zf),1,stdout);
      /* printf("%c %g %g %g\n",type,xf,yf,zf); */
    }
  }
  prev_type = type;
}

int substring(char *sub ,
              char *str )

{
  register char *p,*q;
  while (*str) {
    p = str;
    q = sub;
    while (*p && *p == *q) {p++; q++;}
    if (*q==NULL) return 1;
    str++;
  }
  return 0;
}

int isblank(char *str )

{
  if (str!=NULL)
    while (*str)
      if (!isspace(*str++)) return 0;
  return 1;
}

/*
 * return 1 if prefix is an unambigous prefix of the option 'word'
 */

int prefix(char *prefix ,
           char *word )

{
  int matches,len;
  char **option;
  len = strlen(prefix);
  if (strncmp(prefix,word,len)) return 0;
  /* we've got a match, check for ambiguity */
  matches = 0;
  option = options_description;
  while (*option) {
    if (option[0][0]=='-' && !strncmp(prefix,option[0]+1,len))
      matches++;
    option++;
  }
  if (matches==1) return 1;
  else return 0;
}
