/**---------------------------------------------------------------------------
 ** 
 ** view_nbr.c -- 
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/16 19:23:25
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/07/30 11:26:08
 ** Update Count    : 11
 ** Directory       : /pcdata1/jaccard/codas3c/gfi/src/apps/nbr/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    Undocumented.
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES:
 **
 ** 1999/07/27 Pierre Jaccard
 **    Included content of files keyboard.c and menu.c to have only one source 
 **    file. The same has been done for the header file. 
 **------------------------------------------------------------------------**/

#include "view_nbr.h"

/* ##################################################################
	 -Menu Functions:
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Help Menu:

	 Displays help menu
	 ------------------------------------------------------------------ */
	 
#if PROTOTYPE_ALLOWED
int help_menu()
#else
int help_menu() 
#endif
{

	char func[]="help_menu";

	int error;

	/* Print the help menu */
	error = print_menu((char *) menu_file, "HELP");
	if(error){
		error_msg(func, "while printing HELP menu");
		return(error);
	}

	/* Wait for <return> key */
	read_key(NULL);

	return(0);
}

/* ------------------------------------------------------------------
	 --Selection Menu:

	 This procedure displays the possible selections for viewing buffers of
	 data. The view information structure is updated to the corresponding buffer
	 name.

	 INPUT : VIEW_INFO_TYPE *view; pointer to view information structure

	 RETURN: user's choice
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int select_menu(VIEW_INFO_TYPE *view)
#else
int select_menu(view)
VIEW_INFO_TYPE *view;
#endif
{
	
	char func[]="select_menu";

	int error;
	char choice, done;

	VIEW_INFO_TYPE *vi;
	
	/* Selection loop */
	done = 0;
	while(!done){

		/* print select menu */
		error = print_menu((char *) menu_file, "SELECT MENU");
		if(error != 0){
			error_msg(func, "while printing SELECT MENU menu");
			return(error);
		}
		
		/* Wait for user selection */
		printf("\n\n >>>>> ");
		error = read_key(NULL);
		if(error==EOF) return(READ_ERROR);
		else choice = (char) error;

		/* Process selection */
		vi = vinfo;
		while(vi->code != '\0'){
			if(vi->code == choice){
				strcpy(view->name, vi->name);
				done = 1;
			}
			vi++;
		}
	}
	/* Done */
	return(0);
}

/* ##################################################################
	 -Initializations:
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Initialize View:

	This function sets and reads the first data file name and the first ensemble
	in it.

	INPUT : FILE *fp;                 pointer to list of files
          DATA_FILE_INFO_TYPE *fi;  pointer to data file information structure
          NBR_ENSEMBLE_TYPE *ping;  pointer to ping data structure
          VIEW_INFO_TYPE *view;     pointer to view information structure
	
	RETURN: non-zero on error
	------------------------------------------------------------------

	*/
#if PROTOTYPE_ALLOWED
int init_view(FILE *fp, DATA_FILE_INFO_TYPE *fi, NBR_ENSEMBLE_TYPE *ping,
							VIEW_INFO_TYPE *view)
#else
int init_view(fp, fi, ping, view)
FILE *fp;
DATA_FILE_INFO_TYPE *fi;
NBR_ENSEMBLE_TYPE *ping;
VIEW_INFO_TYPE *view;
#endif
{

	char func[]="init_view";

	int error;
  char *c;

	/* Initializations */
	error = init_nbr_ping(fp, fi, ping);
	if(error){
		error_msg(func, "While initializing ping data");
		return(error);
	}

	view->name = (char *) malloc(20);
	if(view->name == NULL){
		error_msg(func, "Could not initialize space for buffaer name in view");
		return(INSUFFICIENT_MEMORY);
	}
	strcpy(view->name, "");
	view->first_bin =0;

  /* Get path to file containing menu text. This is a fast solution avoiding
     re-editing of the header file when the package is installed somewhere
     else. This will probably disapear in a next release, and all messages
     hardcoded. */
  strcpy(menu_file, "");
  c = NULL;
  if(error_found( (c = getenv("CODASHOME")) == NULL,
                  "You need to set your environment variable CODASHOME"))
    exit(-1);
  strcpy(menu_file, c);
  strcat(menu_file, "/gfi/etc/view_nbr.mnu");
  
	return(0);
	
}

/* ##################################################################
	 -Updating Output:

	 These functions display ensemble data to screen. They make use of the
	 [[VIEW_OUTPUT_LIST_TYPE]] structure initialized in the header file.
	 ################################################################## */

/* ------------------------------------------------------------------
	 --Update Output:

	 Print the content of [[ptr]], according to the format [[fmt]]. The value of
	 [[code]] must be conform to the codes of value types defined in
	 [[dbext.h]]. 

	 The format [[fmt]] must have a leading non-empty string before the first
	 occurence of a [[%]] formatting character. It will not be possible to
	 display the character [[%]] itself.

	 As long as there is a following [[%]] formatting character in the format
	 string, the value pointed by the data pointer will be printed. If the value
	 of [[nbin]] is larger than 1, then the pointer is incremented after each
	 output. When [[nbins]] incrementation are reached, the output of the next
	 data is reset to the first one pointed by [[ptr]].

	 As additionnal parameter, [[n]] gives the initial offset from the value of
	 [[ptr]]. Hence, the first value is the one located at [[ptr + (n*nbins)]].

	 INPUT : VIEW_OUTPUT_LIST_TYPE *vlst;  pointer to output list
	         USHORT n;                     initial offset from pointer

	 RETURN: 0
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int update_output(VIEW_OUTPUT_LIST_TYPE *vlst, USHORT n)
#else
int update_output(vlst, n)
VIEW_OUTPUT_LIST_TYPE *vlst;
USHORT n;
#endif
{

	char func[]="update_output";

	char str[100], tmp[100], msg[100], *s;
	USHORT i;
	int lenmax;

	/* Split format string into tokens */
	strcpy(tmp, vlst->fmt);
	s = strtok(tmp, "%");

	/* Write title */
	printf(s);
	fflush(stdout);
	
	/* Get next token */
	s = (char *) strtok(NULL, "%");

	/* Initialize variable index */
	i = 0;

	/* Write next data */
	while(s != NULL){
		strcpy(str, "%");
		strcat(str, s);
		/* Choose format and data */
		switch(vlst->code){
		case BYTE_VALUE_CODE:
			sprintf(msg, str, *((((BYTE *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 3;
			break;
		case UBYTE_VALUE_CODE:
			sprintf(msg, str, *((((UBYTE *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 3;
			break;
		case SHORT_VALUE_CODE:
			sprintf(msg, str, *((((SHORT *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 6;
			break;
		case USHORT_VALUE_CODE:
			sprintf(msg, str, *((((USHORT *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 6;
			break;
		case LONG_VALUE_CODE:
			sprintf(msg, str, *((((LONG *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 11;
			break;
		case ULONG_VALUE_CODE:
			sprintf(msg, str, *((((ULONG *) vlst->ptr) + n*(vlst->nbins)) + i));
			lenmax = 11;
			break;
		default:
			error_msg(func, "Undefined code");
			return(CONVERT_ERROR);
			break;
		}
		/* print message */
		printf("%s", msg);
		/* Get next token */
		s = strtok(NULL, "%");
		/* Update variable index */
		i++;
		if(i >= (USHORT) vlst->nbins)
			i = 0;
	}

	return(0);

}

/* ------------------------------------------------------------------
	--Update View:

	Main function that control display of ensemble data. 

	+It searches for the specific output list in the global output list
	 initialized in the header file. 
	+It checks if the corresponding buffer is present or not. 
	+If the data to display is an array of bins, it displays each bin according
 	 to the format specified in the output list.
	+Final display of data is performed by the [[update_output]] function.

	INPUT : VIEW_INFO_TYPE *view;      pointer to output list
          DATA_FILE_INFO_TYPE *fi;   pointer to data file information
          NBR_ENSEMBLE_TYPE *ping;   pointer to ensemble data

	RETURN: non-zero on error
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int update_view(VIEW_INFO_TYPE *view, DATA_FILE_INFO_TYPE *fi, 
								NBR_ENSEMBLE_TYPE *ping)
#else
int update_view(view, fi, ping)
VIEW_INFO_TYPE *view;
DATA_FILE_INFO_TYPE *fi;
NBR_ENSEMBLE_TYPE *ping;
#endif
{

	char func[]="update_view";

	VIEW_OUTPUT_LIST_TYPE *vlst;
	USHORT start_bin, stop_bin, i;
	int error;
	ULONG done;

	/* Initializations */
	clear_screen();
	
	/* Print main header */
	printf("\nFILE: %s\n\n", fi->name);
	printf("ENSEMBLE NUMBER: %uh\n", ping->leader.ensemble_number);
	printf("ADDRESS IN FILE: %016lXh\n", fi->data_pos);
	printf("START ADCP TIME: %02d/%02d - %02d:%02d:%02d\n", 
				 ping->leader.ensemble_start_time.month, 
				 ping->leader.ensemble_start_time.day, 
				 ping->leader.ensemble_start_time.hour, 
				 ping->leader.ensemble_start_time.minute, 
				 ping->leader.ensemble_start_time.second);
	printf("BUFFER PRESENT : %01lX\n\n", ping->presence_bits);


	/* Determine start and stop bin positions */
	if(strstr(view->name, "HEADER") || strstr(view->name, "LEADER")){
		start_bin = 0;
		stop_bin  = 1;
	}
	else{
		start_bin = (USHORT) view->first_bin;
		stop_bin = start_bin + NB_BINS_PER_PAGE;
	}

	/* Get start element inside output list */
	vlst = output_list;
	done = 0;
	while(!done){
		if(vlst->fmt != NULL)
			done = (ULONG) strstr(vlst->fmt, view->name);
		done = (done || (vlst->code <0));
		vlst++;
	}
	vlst--;

	if(vlst->code < 0){
		error_msg(func, "Could not find an output list for current buffer");
		return(READ_ERROR);
	}

	/* Copy ping data in global ping variable */
	memcpy(&tmp_ens, ping, sizeof(NBR_ENSEMBLE_TYPE));

	/* Print the data from the list */
	while(vlst->fmt != NULL){
		/* Test presence bits */
		if(vlst->bits){
			if(!(ping->presence_bits & vlst->bits)){
				printf("\n\n***** BUFFER NOT PRESENT *****\n\n");
				return(0);
			}
		}
		/* Loop through the bins */
		if(vlst->code == 0)
			error = update_output(vlst, 0);
		else
			for(i=start_bin; i<stop_bin; i++){
				/* Print bin number, if several bins */
				if(stop_bin > 2)
					printf("  %3u", i);
				error = update_output(vlst, i);
			}
		if(error){
			error_msg(func, "While updating output");
			return(WRITE_ERROR);
		}

		/* Select next output line from list */
		vlst++;
	}

	return(error);

}

/* ##################################################################
	 -Main:

	This is the main routine. It is called by [[main_cnt]]. The argument passed
	on the command line is the name of a list containing name of ADCP raw data
	files. This file has been uncommented by [[main_cnt]].
	################################################################## */
	
#if PROTOTYPE_ALLOWED
void do_it(FILE *fp)
#else
void do_it(fp) 
FILE *fp;
#endif
{

	char func[]="do_it";

	char quit, choice, msg[100];
	int error, num;

	NBR_ENSEMBLE_TYPE ping;
	DATA_FILE_INFO_TYPE finfo;
	VIEW_INFO_TYPE view;

	/* Initializations and read first ensemble */
	error = init_view(fp, &finfo, &ping, &view);
	if (error != 0){
		error_msg(func, "while in init_view");
		goto on_error;
	}

	/* Loop for data viewing */
	quit = 0;
	choice = '?';
	while(!quit){
		
		error = 0;

		/* Process user choice */
		switch (choice){
		case '?': case 'h':
			error = help_menu();
			strcpy(msg, "while in help_menu");
			break;
		case 'q': 
			quit=1;
			error = 0;
			break;
		case 's':
			error = select_menu(&view);
			strcpy(msg, "while in select_menu");
			break;
		case 'f':
			error = init_view(fp, &finfo, &ping, &view);
			strcpy(msg, "while in init_view");
			break;
		case 'n':
			error = next_nbr_ping(fp, &finfo, &ping, 1);
			strcpy(msg, "while reading next ping");
			break;
		case 'p':
			error = prev_nbr_ping(fp, &finfo, &ping, 1);
			strcpy(msg, "while reading prvious ping");
			break;
		case 'g':
			printf("\n GOTO ENSEMBLE NUMBER: ");
			scanf("%d", &num);
			error = goto_nbr_ping(fp, &finfo, &ping, num);
			strcpy(msg, "While going to ping");
			break;
		case 'm':
			printf("\n MOVE OVER N ENSEMBLE: ");
			scanf("%d", &num);
			error = skip_nbr_ping(fp, &finfo, &ping, num);
			strcpy(msg, "While skipping to ping");
			break;
		case 'd':
			view.first_bin += NB_BINS_PER_PAGE;
			break;
		case 'u':
			view.first_bin -= NB_BINS_PER_PAGE;
			break;
		default:
			/* Nothing to do */
			break;
		}
		/* Check errors */
		if((error != 0) && (error!=EOF)){
			error_msg(func, msg);
			goto on_error;
		}
		
		/* Check bin start number */
		if(view.first_bin < 0) view.first_bin = 0;
		if(view.first_bin + NB_BINS_PER_PAGE > MAX_BINS) 
			view.first_bin = (SHORT) MAX_BINS - NB_BINS_PER_PAGE;


		if(!quit){
			/* Update view */
			if (!strcmp(view.name, "")){
				choice='s';
			}
			else{
				/* Update screen with current settings */
				error = update_view(&view, &finfo, &ping); 
				if(error != 0){
					error_msg(func, "while in update_view");
					goto on_error;
				}
				/* Wait for next choice */
				printf("\n\n >>>>> ");
				error = read_key(trans_table);
				if(error==EOF) goto on_error;
				else choice = tolower((char) error);

			}
		}
	}

	error = 0;

on_error:
	fclose(finfo.fp);
	if(error!=0){
		error_msg(func, "Not terminating normally");
	}
	else{
		printf("\n\n Function do_it() in view_nbr terminating normally.\n\n");
	}
	fflush(stdout);
	
}


/* ------------------------------------------------------------------
   Content of previous menu.c file
   ------------------------------------------------------------------ */

/*
	-Introduction:

	 This function prints to screen the menu corresponding to the name defined
	 in the menu file.

		+A menu or help text to display begins with '<' in the first column
	   followed by a string containing the text name.

	 	+A menu or help text ends with with '>' in the first column.

		+Comments in the file are those used in C and are not printed.

	 	+All lines found between the delimiters described above are printed as is.

	 The program using these functions may redefine the menu delimiters by
	 redefining the macros [[MENU_START_CHAR]] and [[MENU_END_CHAR]].

*/

/*
	-Print Menu:

	This procedure searches for for the menu name [[menu_name]] in the menu
	definition file [[menu_file]] and prints it to [[stdout]]. 
*/
  
#if PROTOTYPE_ALLOWED
int print_menu(char *menu_file, char *menu_name)
#else
	int print_menu(menu_file, menu_name)
	char *menu_file;
	char *menu_name;
#endif
{
	
	char func[]="print_menu";

	FILE *fp_menu, *fp_menu_nc;
	char tmp_file[L_tmpnam], line[100];
	int  error=0;
	UBYTE menu_start_found, menu_end_found;

	/* Initialize uncommented menu file */
	tmpnam(tmp_file);
	fp_menu = check_fopen(menu_file, "r");
	fp_menu_nc = check_fopen(tmp_file, "w+");
	strip_comments(fp_menu, fp_menu_nc);
	fclose(fp_menu);
	rewind(fp_menu_nc);
	
	/* Find the start of the menu */
	menu_start_found = 0;
	while(!menu_start_found){
		if(fgets( line, 80, fp_menu_nc) == NULL){
			error_msg( func, "while searching for menu start");
			error = READ_ERROR;
			goto on_error;
		}
		if(line[0]==MENU_START_CHAR){
			if(strstr(line, menu_name) != (char *) NULL){
				menu_start_found=1;
			}
		}
	}

	/* Print content of file to stdout until end of menu */
	clear_screen();
	menu_end_found=0;
	while(!menu_end_found){
		if(fgets( line, 81, fp_menu_nc) == NULL){
			error_msg( func, "while reading menu");
			error = READ_ERROR;
			goto on_error;
		}
		
		if(line[0]!=MENU_END_CHAR){
			printf("%s", line);
		}
		else menu_end_found=1;
	}

	error = 0;

on_error:
	fclose(fp_menu_nc);
	remove(tmp_file);
	if (error) error_msg( func, menu_name);

	return(error);

}

/* ------------------------------------------------------------------
   Content of previous keyboard.c file
   ------------------------------------------------------------------ */

/*
	-Key Name:

	Get the name of a key, as it is described in [[kbd_table]].

	kbd_char: characters defining a key in [[kbd_table]]. If the given sequence
	          is found in [[kbd_table]], its associated name is copied into
						[[kbd_char]]. 
	*/
#if PROTOTYPE_ALLOWED
int key_name(KBD_CHAR_TYPE *kbd_char)
#else
int key_name(kbd_char)
KBD_CHAR_TYPE *kbd_char;
#endif
{

	
	KBD_CHAR_TYPE *kbd_def;
	int i, k, equal, done;

	/* Scan the keyboard definition table */
	i=0;
	done=0;
	while(!done){
		/* Get current element from definition table */
		kbd_def = kbd_table+i;
		/* Look for identical byte sequence */
		equal = 1;
		for(k=0; k<EXTENDED_CHAR_LENGTH; k++)
			equal = equal && (kbd_def->ext[k] == kbd_char->ext[k]);
		/* Copy the key name, if identical */
		if(equal){
			strcpy(kbd_char->name, kbd_def->name);
			done = 1;
		}
		else{
			/* Check for last defined key (empty_char) */
			if(strcmp(kbd_def->name, "empty_char")==0){
				strcpy(kbd_char->name, "undef_char");
				done = 1;
			}
			/* Increment for next definition */
			i++;
		}
	}
	
	return(0);

}

/*
	-Key Translation:

	Translate the key name into a character, according to the given translation
	table. 

	Return EOF if no translation found, or the translated character converted to
	an integer. This conversion is done for the same reason as in [[getchar]].
	*/
#if PROTOTYPE_ALLOWED
int translate_key(KBD_CHAR_TYPE *kbd_char, 
									KBD_TRANSLATION_TABLE_TYPE *kbd_table)
#else
int translate_key(kbd_char, kbd_table)
KBD_CHAR_TYPE *kbd_char;
KBD_TRANSLATION_TABLE_TYPE *kbd_table;
#endif
{

	int i, done;
	int c=EOF;
	KBD_TRANSLATION_TABLE_TYPE *kbd_trans;

	/* Scan the translation table to find the name */
	i=0;
	done=0;
	while(!done){
		/* Get the element in the table */
		kbd_trans = kbd_table + i;
		/* Look for identical names */
		if(strcmp(kbd_trans->name, kbd_char->name)==0){
			c = (int) kbd_trans->c;
			done=1;
		}
		else{
			/* Check for end of table */
			if(strcmp(kbd_trans->name, "NULL")==0) done=1;
			else 
				/* Increment for next translation element */
				i++;
		}
	}

	return(c);

}

/* 
	 -Read Key:

	 Read an input line (terminated with '\n') and return the first input
	 character. If [[trans_table]] is [[NULL]], no translation is performed and
	 only the first input byte is returned.

	 The return value is an [[int]], and not a [[char]], for the same reason as
	 in the function [[getchar]].

	 Return [[EOF]] if an error occured.
	 */
#if PROTOTYPE_ALLOWED
int read_key(KBD_TRANSLATION_TABLE_TYPE *trans_table)
#else
int read_key(trans_table)
KBD_TRANSLATION_TABLE_TYPE *trans_table;
#endif
{
	
	char func[]="read_key";

	char line[80];
	int len, i, prefix;
	int c=EOF;
	KBD_CHAR_TYPE kbd_char;

	/* Read an input line */
	if(fgets(line, 79, stdin) == NULL){
		error_msg(func, "while reading input line");
		return(EOF);
	}
	len=strlen(line);

	/* Determine the number of prefix characters */
	if(len > EXTENDED_CHAR_LENGTH) prefix = 0;
	else prefix = EXTENDED_CHAR_LENGTH - len;
	
	/* Build the prefixed extended character array */
	for(i=0; i<prefix; i++) kbd_char.ext[i] = '\000';
	while(i < EXTENDED_CHAR_LENGTH){
		kbd_char.ext[i] = line[i-prefix];
		i++;
	}
	
	/* Get the name of the extended character, if defined */
	key_name(&kbd_char);

	/* Apply translation if required */
	if(trans_table != NULL)
		c = translate_key(&kbd_char, trans_table);
	
	/* Test if a name or a translation has been found */
	if(c==EOF){
		/* Retrieve the first non prefix char in the byte sequence */
		if(prefix<4)
			c = (int) kbd_char.ext[prefix];
		/* Or the null byte for empty chars */
		if(prefix==4)
			c = (int) '\000';
	}

	return(c);

}
	


