/**---------------------------------------------------------------------------
 ** 
 ** xtract.c -- Extraction utilities
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/15 16:55:05
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/11/12 18:29:11
 ** Update Count    : 6
 ** Directory       : /pcdata1/jaccard/codas3c/gfi/src/libs/db/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    These functions allow one to extract data from a any CODAS database by
 **    just specifying the variable names, as they are defined in the
 **    database. Element from structures are also supported. Many
 **    specifications to retrieve data have been added to these utilities so
 **    that a full rewritting had to be carried performed. Those defined in
 **    file access.c are supposed to replace them.
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

#include "xtract.h"

/* ------------------------------------------------------------------
	 Initialize extraction structure. 

   RETURNS: 0
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int init_xtract_prf_var(/* Pointer to extraction information */
                        EXTRACT_VARIABLE_TYPE *xvar)
#else
int init_xtract_prf_var(xvar)
#endif
{
  
	strcpy(xvar->name, "");
	sprintf(xvar->min_str, "%f", BADFLOAT);
	sprintf(xvar->max_str, "%f", -BADFLOAT);
	xvar->scale = (FLOAT) 1.0;
	xvar->code  = 0;
	xvar->min   = BADFLOAT;
	xvar->max   = -BADFLOAT;
	xvar->n     = 0;
	xvar->data  = NULL;
	xvar->struc = NULL;
	
	return(0);

}
		
/* ------------------------------------------------------------------
   Free memory allocated for data extraction.

   RETURNS: 0
   ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int free_xtract_prf_var(/* Pointer to extraction structure */
                        EXTRACT_VARIABLE_TYPE *xvar)
#else
int free_xtract_prf_var(xvar)
EXTRACT_VARIABLE_TYPE *xvar;
#endif
{

	if(xvar->data){
		free(xvar->data);
		xvar->data = NULL;
	}
	/* Element <struc> must not be freed, because it is not allocated
		 dynamically, but set to existent addresses within the global database
		 structure. */
	
	return(0);

}

/* ------------------------------------------------------------------
	 Process the given variable name.

   RETURNS: non-zero on error.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int process_variable_name(/* Given variable name */
                          char *name, 
                          /* Base name of variable */
                          char *var_name, 
                          /* Eventually structure element name */
                          char *elem_name, 
                          /* Eventually list of values */
													char *var_val_list)
#else
int process_variable_name()
#endif
{

	char *str1, *str2, tmp[VAR_NAME_LENGTH + VAR_VAL_LIST_LENGTH];

	/* Backup copy of variable name */
	strcpy(tmp, name);

	/* Split name and list values with tolerance */
	str1 = strtok(tmp, ":");
	str2 = strtok(NULL, ":");
	
	/* Get the list values with tolerance */
	if(str2)
		strcpy(var_val_list, str2);
	else
		strcpy(var_val_list, "");

	/* Split the variable and structure element name */
	strcpy(tmp, str1);
	str1 = strtok(tmp, ".");
	str2 = strtok(NULL, ".");

	/* Get the structure element name */
	if(str2)
		strcpy(elem_name, str2);
	else
		strcpy(elem_name, "");

	/* Get the variable name */
	if(str1)
		strcpy(var_name, str1);
	else
		strcpy(var_name, "");

	if(error_found(strlen(var_name) == 0, "EMPTY VARIABLE NAME"))
		return(-1);
	
		
	return(0);

}

/* ------------------------------------------------------------------
	 Select specific values according to the content of a formated string. The
   format of the string is the following:

     "{ (delta) mean, (delta) mean, ... }"

   where delta and mean can be either floats or integers. If floats,
   only values inside mean-delta and mean+delta will be kept. If integers,
   only values from bins ranging from mean-delta to mean+delta will be kept. 

   RETURNS: non-zero on error.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int select_specific_values(/* Pointer to extracted data */
                           EXTRACT_VARIABLE_TYPE *xvar, 
                           /* String specifying which variables to extract */
                           char *list)
#else
int select_specific_values()
#endif
{

	char *str, tmp[VAR_VAL_LIST_LENGTH];
	int n, i, tol_i;
	FLOAT f, tol_f, *tmp_f;

	/* Default values */
	tol_i = DEFAULT_VARIABLE_INT_TOLERANCE;
	tol_f = (FLOAT) DEFAULT_VARIABLE_FLOAT_TOLERANCE;
	
	/* Take away the braces */
	strcpy(tmp, list);
	str = strtok(tmp, "{");
	if(error_found(str==NULL, "FORMAT ERROR"))
		return(-1);
	strcpy(tmp, str);
	str = strtok(tmp, "}");
	if(error_found(str==NULL, "FORMAT ERROR"))
		return(-1);
	
	/* Allocate space for temprary data */
	tmp_f = (FLOAT *) calloc(xvar->n, sizeof(FLOAT));
	if(error_found(tmp_f == NULL, "INSUFFICIENT MEMORY"))
		return(-1);
	for(n=0; n<((int) xvar->n); n++)
		tmp_f[n] = BADFLOAT;

	/* Read through the list and select appropriate variables */
	strcpy(tmp, str);
	str = strtok(tmp, ",");
	while(str){
		/* Bin or float value */
		if(strchr(str, '.')){
			/* Try to read a tolerance */
			if(sscanf(str, "(%f)", &tol_f) != 1){
				if(error_found(sscanf(str, "%f", &f) != 1, "FOMAT ERROR")){
					free(tmp_f);
					return(-1);
				}
				for(n=0; n<((int) xvar->n); n++)
					if( ((xvar->data)[n] < ADJ_BADFLOAT)
							&& ((xvar->data)[n] >= (f - tol_f))
							&& ((xvar->data)[n] <= (f + tol_f)) )
						tmp_f[n] = (xvar->data)[n];
			}
		}
		else{
			/* Try to read a tolerance */
			if(sscanf(str, "(%d)", &tol_i) != 1){
				if(error_found(sscanf(str, "%d", &i) != 1, "FOMAT ERROR")){
					free(tmp_f);
					return(-1);
				}
				i -= 1;
				for(n=0; n<((int) xvar->n); n++)
					if( ((xvar->data)[n] < ADJ_BADFLOAT)
							&& (n >= (i - tol_i))
							&& (n <= (i + tol_i)) ) 
						tmp_f[n] = (xvar->data)[n];
			}
		}
		str = strtok(NULL, ",");
	}
	
	memcpy((void *) xvar->data, (void *) tmp_f, (xvar->n) * sizeof(FLOAT));
	free(tmp_f);

	return(0);

}

/* ------------------------------------------------------------------
	 Given a variable name, this function get all data corresponding to this
	 variable in current profile.

   RETURNS: number of bytes allocated to hold data, or negative on error.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int extract_prf_var(/* Variable name */
                    char *name, 
                    /* Pointer to array of data. Must be dealocated and set to 
                     NULL on input. Space for data is automatically allocated
                     in the function */
                    char **data, 
                    /* Variable identifier. It is updated on output to the
                       variable identifier specified in argument name, or -1
                       if no identifier can be found */
                    int *id)
#else
int extract_prf_var()
#endif
{

	unsigned int nb;
	int nv, ierr;

	int i;
	/* Checking the pointers */
	if(error_found(*data!=NULL, "Data pointer must be initialized to NULL"))
		return(-1);
	
	/* Defaults */
	*id = -1;

	/* Look for the identifier of the variable */
	nv = (int) db->block_hdr.data_list_nentries;
	i = 0;
	while( (i<nv) 
				 && (!db->data_list[i].access_type 
						 || strcmp((char *) db->data_list[i].name, name)))
		i++;
	/* Special variables */
	if(i >= nv){
		if(strcmp(name, "TIME") == 0)
			*id = TIME;
		else if((strcmp(name, "POSITION") == 0)
						|| (strcmp(name, "LATITUDE") == 0)
						|| (strcmp(name, "LONGITUDE") == 0))
			*id = POSITION;
		else{
			error_found(1, "VARIABLE NOT PRESENT");
			return(-1);
		}
	}
	else
		*id   = i;
	
	/* Get the number of required bytes for this variable */
	nb = 0;
	DBGET(id, NULL, &nb, &ierr);
	if(DBERROR(&ierr, "DBGET: number of bytes"))
		return(-1);
	if(error_found(nb<=0, "ZERO BYTES"))
		return(-1);

	/* Allocate space for data */
	*data = (char *) calloc(nb,1);
	if(error_found(*data==NULL, "INSUFFICIENT MEMORY"))
		return(-1);

	/* Get the data */
	DBGET(id, *data, &nb, &ierr);
	if(DBERROR(&ierr, "DBGET: profile data")){
		free(*data);
		return(-1);
	}
	
	return((int) nb);

}

/* ------------------------------------------------------------------
	 Given an array of structures and an element name, this function extracts
	 all data specific to this element.

   RETURNS: number of bytes in array containing element data or negative on
            error. 
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int extract_elem(/* Pointer to hold data from element. Must not be allocated
                    and set to NULL on input. It will be allocated to the
                    correct size by the function itself. */
                 char **elem_data, 
                 /* Place for element definition */
                 STRUCT_DEF_ELEM_TYPE *elem_def, 
								 /* Name of element */
                 char *elem_name, 
                 /* Name odf structure */
                 char *str_name, 
                 /* Pointer to array containing data for the complete
                    structure */
                 char *str_data,
                 /* Number of bytes in the array with structure data */
                 int *nb)
#else
int extract_elem()
#endif
{

	unsigned int elem_ofs, elem_size;
	long str_nb; 
	int  n_data, elem_nb;
	int  i, c, n;
	char *str_ptr, *elem_ptr;


	/* Check data pointer */
	if(error_found(*elem_data != NULL, "Element data pointer must be NULL"))
		return(-1);
	
	/* Load the structure definition */
	if(!db->block_strdef_loaded){
		if(error_found( load_block_strdef(), "Loading Structure Definition"))
			return(-1);
	}
	
	/* Get the structure size */
	str_nb = get_struct_size(str_name, db->block_strdef);
	if(error_found(str_nb <= 0, "EMPTY STRUCTURE"))
		return(-1);

	/* Get the element */
	if(error_found(find_elem(elem_name, str_name, db->block_strdef, &elem_ofs,
													 &elem_size, elem_def),
								 "Getting element information"))
		return(-1);

	/* Retrieve all values specific to elem_name only */
	str_ptr = str_data;
	n_data  = *nb / str_nb;
	elem_nb = n_data * elem_size * elem_def->count;
	*elem_data = (char *) calloc((size_t) elem_nb, 1);
	if(error_found(*elem_data == NULL, "INSUFFICIENT MEMORY"))
		return(-1);
	elem_ptr = *elem_data;
	for(i=0; i<n_data; i++){
		for(c=0; c<elem_def->count; c++){
			for(n=0; n<((int) elem_size); n++)
				*(elem_ptr + n) = *(str_ptr + elem_ofs + c*elem_size + n);
			elem_ptr += elem_size;
		}
		str_ptr  += str_nb;
	}
	
	return(elem_nb);

}

/* ------------------------------------------------------------------
	 Extract all data for the variable described in the extraction structure and 
   save it as FLOAT into the data element of this structure. This element must 
   not be allocated on input and point to NULL. It will be automatically
   updated to the correct size and filled by the function. This function can
   handle stand alone variables as well as elements of structures. The
   variable to extract is deterimined by its name which must is stored in the
   extraction structure and must of course be set. Names are identical to
   those defined in the database to visit (uppercase sensitive). Structure
   elements are specified as in C. In additions, the following variables are
   implemented: TIME, POSITION, LATITUDE and LONGITUDE.

   RETURNS: the number of data extracted or negative on error.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int extract_prf_var_f(/* Pointer to extraction structure */
                      EXTRACT_VARIABLE_TYPE *xvar, 
                      /* Year base for time conversions */
                      int year_base)
#else
int extract_prf_var_f()
#endif
{

	int var_nb, elem_nb, var_id;

	STRUCT_DEF_ELEM_TYPE elem_def;
	char var_name[20], elem_name[20], var_val_list[80];
	char *var_data = NULL;
	char *elem_data = NULL;

	FLOAT offset, scale;
	DMSH_POSITION_TYPE *pos;
		
	/* Check pointers */
	if(error_found(xvar->data != NULL, 
								 "Data pointer must be initialized to NULL"))
		return(-1);

	/* Split the variable name */
	if(error_found(process_variable_name(xvar->name, var_name, elem_name, 
																			 var_val_list),
								 "Processing the variable name"))
		return(-1);

	/* Get all data for this variable */
	var_nb = extract_prf_var(var_name, &var_data, &var_id);
	if(error_found(var_nb <= 0, "Extracting profile data"))
		return(-1);

	/* Check if it is a structure */
	if(db->data_list[var_id].value_type == STRUCT_VALUE_CODE){
		if(error_found(strlen(elem_name) == 0, "EMPTY ELEMENT NAME")){
			free(var_data);
			return(-1);
		}
		elem_nb = extract_elem(&elem_data, &elem_def, elem_name, var_name, 
													 var_data, &var_nb);
		if(error_found(elem_nb <= 0, "Extracting element data")){
			free(var_data);
			return(-1);
		}
		/* Place back all element values into the var_data array */
		free(var_data);
		var_data = calloc((size_t) elem_nb, 1);
		memcpy((void *) var_data, (void *) elem_data, (size_t) elem_nb);
		free(elem_data);
		var_nb = elem_nb;
		/* Final settings for conversions */
		offset = (FLOAT) 0.0;
		scale  = (FLOAT) 1.0;
		xvar->struc = &elem_def;
	}
	else if((var_id != TIME) && (var_id != POSITION)){
		xvar->struc = (STRUCT_DEF_ELEM_TYPE *) &(db->data_list[var_id]);
		offset = db->data_list[var_id].offset;
		scale  = db->data_list[var_id].scale;
	}
	
	/* Convert all data to FLOAT */
	if(var_id == TIME)
		xvar->n = 1;
	else if (var_id == POSITION)
		xvar->n = 2;
	else
		xvar->n = (unsigned int) (var_nb / VALUE_SIZE[xvar->struc->value_type]);
	xvar->data = (FLOAT *) calloc(xvar->n, sizeof(FLOAT));
	if(error_found(xvar->data == NULL, "INSUFFICIENT MEMORY")){
		free(var_data);
		return(-1);
	}
	if(var_id == TIME){
		*(xvar->data) = (FLOAT) year_day((YMDHMS_TIME_TYPE *) var_data,
																		 year_base);
		xvar->n = 1;
	}
	else if(var_id == POSITION){
		pos = (DMSH_POSITION_TYPE *) var_data;
		/* Check for bad position: 
		 
		   NOTE: it is not possible to use `invalid_position()', because this
			       function accepts BAD positions (i.e. no position) */
		if(pos->degree == BADSHORT)
			(xvar->data)[0] = BADFLOAT;
		else
			(xvar->data)[0] = ((FLOAT) POSHUN(pos)) / 360000.0;
		if(pos->degree == BADSHORT)
			(xvar->data)[1] = BADFLOAT;
		else
			(xvar->data)[1] = ((FLOAT) POSHUN(pos+1)) / 360000.0;
		/* The position structure has `longitude' in the first element,
			 and `latitude' in the second */
		if(strcmp(var_name, "LATITUDE") == 0){
			xvar->n = 1;
			(xvar->data)[0] = (xvar->data)[1];
		}
		else if(strcmp(var_name, "LONGITUDE") == 0){
			xvar->n = 1;
		}
	}
	else{
		if(error_found( (*unscale[xvar->struc->value_type])
										(xvar->data, var_data, &scale, &offset, &(xvar->n)),
									"Unscaling data")){
			free(xvar->data);
			free(var_data);
			return(-1);
		}
	}
	
	/* Free var_data array before leaving */
	free(var_data);
	
	/* Check for specific values */
	if(strlen(var_val_list))
		if(error_found(select_specific_values(xvar, var_val_list),
									 "Selecting specific values"))
			return(-1);

	return((int) xvar->n);

}

/* ------------------------------------------------------------------
	 Extract extrema values as specified in the extraction structure. These aare 
   the minimal and maximal values. 

   RETURNS: non-zero on error
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int extract_range_values_f(/* Pointer to extraction structure */
                           EXTRACT_VARIABLE_TYPE *xvar, 
                           /* Base yeaar for time conversions */
                           int year_base)
#else
int extract_range_values_f()
#endif
{

	EXTRACT_VARIABLE_TYPE tmp;
	int i;

	/* Get minimal value */
	xvar->min = +BADFLOAT;
	if(strlen(xvar->min_str)){
		if(sscanf(xvar->min_str, "%f", &(xvar->min)) != 1){
			strcpy(tmp.name, xvar->min_str);
			tmp.data = NULL;
			tmp.n = (unsigned int) extract_prf_var_f(&tmp, year_base);
			if(error_found(tmp.n <= 0, "Extracting minimum value"))
				return(1);
			/* Choose the value of the minimum value */
			if((strcmp(xvar->min_str, "ACCESS_VARIABLES.first_good_bin") == 0)
				 && (tmp.data[0] < ADJ_BADFLOAT)){
				i = (int) tmp.data[0];
				strcpy(tmp.name, "DEPTH");
				tmp.data = NULL;
				tmp.n = (unsigned int) extract_prf_var_f(&tmp, year_base);
				if(error_found(tmp.n <= 0, "Extracting DEPTH"))
					return(1);
				xvar->min = tmp.data[i-1];
			}
			else{
				for(i=0; i<((int) tmp.n); i++)
					if(tmp.data[i] < ADJ_BADFLOAT)
						xvar->min = min_val(xvar->min, tmp.data[i]);
			}
		}
	}
	else
		xvar->min = -BADFLOAT;

	/* Get maximal value */
	xvar->max = -BADFLOAT;
	if(strlen(xvar->max_str)){
		if(sscanf(xvar->max_str, "%f", &(xvar->max)) != 1){
			strcpy(tmp.name, xvar->max_str);
			tmp.data = NULL;
			tmp.n = (unsigned int) extract_prf_var_f(&tmp, year_base);
			if(error_found(tmp.n <= 0, "Extracting maximum value"))
				return(1);
			/* Choose the value of the maximum value */
			if((strcmp(xvar->max_str, "ACCESS_VARIABLES.last_good_bin") == 0)
				 && (tmp.data[0] < ADJ_BADFLOAT)){
				i = (int) tmp.data[0];
				strcpy(tmp.name, "DEPTH");
				tmp.data = NULL;
				tmp.n = (unsigned int) extract_prf_var_f(&tmp, year_base);
				if(error_found(tmp.n <= 0, "Extracting DEPTH"))
					return(1);
				xvar->max = tmp.data[i-1];
			}
			else{
				for(i=0; i<((int) tmp.n); i++)
					if(tmp.data[i] < ADJ_BADFLOAT)
						xvar->max = max_val(xvar->max, tmp.data[i]);
			}
		}
	}
	else
		xvar->max = BADFLOAT;

	return(0);

}




