/**---------------------------------------------------------------------------
 ** 
 ** access.c -- Utilities to access data from a CODAS database 
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/15 23:05:25
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/07/16 19:43:02
 ** Update Count    : 2
 ** Directory       : /home/pego/pcd1/codas3c/gfi/src/libs/db/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    These utilities provide easy access to data stored in a CODAS
 **    database. They are still under construction, but should already work
 **    quite well. They are meant as a more general and more robust
 **    replacement to those found in file xtract.h
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

#include "access.h"

/* ------------------------------------------------------------------
   Process a string to get its corresponding database variable or structure
   name, and eventually structure element name. No test is done to check if
   the specified variable is defined.
   
   Variables and structure elements are specified by their case sensitive
   name, as they appear in the producer definition file. Structure names
   and structure elements must be separated with a dot, as in C.

	 RETURNS: Zero

	 BUGS: This function cannot handle elements in structures of structures. But 
         do CODAS databases handle them? 
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int split_db_variable_name(/* Full variable name */
													 char *str,
													 /* Name of variable or structure */ 
													 char *name,
													 /* Name of structure element, or empty string */
													 char *elem
													 )
#else
int ()
#endif
{

	int i;

	/* Initialize the variable name and structure element name */
	for(i=0; i<((int) strlen(name)); i++)
		name[i] = '\0';
	for(i=0; i<((int) strlen(elem)); i++)
		elem[i] = '\0';

	/* Remove leading and trailing spaces */
	str = strtrim_lt(str);
	
	/* Separate variable name and structure element name */
	i = (int) strcspn(str, ".");
	
	/* Copy variable name */
	strncpy(name, str, (size_t) i);

	/* Eventually, copy structure element name */
	if(i < ((int) strlen(str)))
		strcpy(elem, str+i+1);
	
	return(0);

}

/* ------------------------------------------------------------------
   Initialize a DB_DATA_INFO_TYPE structure.

	 RETURNS: Zero.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int init_db_data_info(/* Pointer to the DB_DATA_INFO_TYPE structure to
												 initialize */
											DB_DATA_INFO_TYPE *dbinfo
											)
#else
int ()
#endif
{

	strcpy(dbinfo->name,     "");
	strcpy(dbinfo->elem,     "");
	strcpy(dbinfo->units,    "");
	strcpy(dbinfo->unused_1, "");
	
	dbinfo->entry_id = dbinfo->value_type 
		= dbinfo->count = dbinfo->size = (SHORT) EOF;

	dbinfo->offset = dbinfo->scale = BADFLOAT;

	return(0);

}

/* ------------------------------------------------------------------
   Given a variable or structure name present in the current CODAS
   database, get the associated variable identifier.
   
   If the specified variable is not present in the database, the identifier
   is set to EOF.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_variable_id(/* Variable or structure name */
									 char *name,
									 /* Variable or structure identifier. */
									 int  *id
									 )
#else
int ()
#endif
{

	int i, n, ierr=0;

	/* Initializations */
	*id = EOF;

	/* Check if the database is open */
	if(!(db))
		return(DB_IS_NOT_OPEN);

	/* Get the identifier for variable name */
	n = (int) db->block_hdr.data_list_nentries;
	i = 0;
	while( (i<n) 
				 && (!db->data_list[i].access_type 
						 || strcmp((char *) db->data_list[i].name, name)))
		i++;
	
	/* Set the variable identifier */
	if(i<n)
		*id = i;
	
	/* Special variable names */
	if(*id == EOF){
		if(strcmp(name, "TIME") == 0)
			*id = TIME;
		else if( !(strcmp(name, "POSITION"))  ||
						 !(strcmp(name, "LATITUDE"))  ||
						 !(strcmp(name, "LONGITUDE")) )
			*id = POSITION;
		else
			/* No variable with this name */
			ierr = NO_SUCH_ELEMENT;
	}

	return(ierr);

}

/* ------------------------------------------------------------------
   Given a structure name present in the current CODAS database, this
   function fills the DB_DATA_INFO_TYPE structure for this element.
   
   This function can only handle structure names, not variable names. See
   function get_db_data_info() for handling both structures and variables
   names. 

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_struct_info(/* Structure name */
											 char *name,
											 /* Eventually structure element name */
											 char *elem, 
											 /* Pointer to a DB_DATA_INFO_TYPE structure */
											 DB_DATA_INFO_TYPE *dbinfo
											 )
#else
int ()
#endif
{

	STRUCT_DEF_ELEM_TYPE elem_def;
	unsigned int offset, elem_nb;
	int ierr=0;

	/* Check if the database is open */
	if(!(db))
		return(DB_IS_NOT_OPEN);

	/* Load the structure definition */
	if(!(db->block_strdef_loaded) && (ierr = load_block_strdef())){
		DBERROR(&ierr, "load_block_strdef()");
		return(ierr);
	}

	/* Get the structure size */
	dbinfo->size = (SHORT) get_struct_size(name, db->block_strdef);
	ierr = (dbinfo->size <= 0) ? UNEXPECTED_STRUCTURE_SIZE : 0;
	if(DBERROR(&ierr, "get_struct_size()"))
		return(ierr);

	/* Finish here if no element is specified */
	if(strlen(elem) == 0){
		dbinfo->count = 1;
		return(ierr);
	}

	/* Get the element information */
	ierr = find_elem(elem, name, db->block_strdef, &offset, &elem_nb, 
									 &elem_def);
	if(DBERROR(&ierr, "find_elem()"))
		return(ierr);

	/* Fill information for element */
	strcpy(dbinfo->units, elem_def.units);
	dbinfo->value_type = elem_def.value_type;
	dbinfo->count      = elem_def.count;
	dbinfo->scale      = (FLOAT) 1.0;
	dbinfo->offset     = (FLOAT) 0.0;
	dbinfo->size       = (SHORT) VALUE_SIZE[elem_def.value_type];
	
	return(ierr);
}

/* ------------------------------------------------------------------
   Given a variable or structure name present in the current database, this
   function fills its corresponding DB_DATA_INFO_TYPE structure.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_data_info(/* Variable or structure name */
										 char *name,
										 /* Eventually element name */
										 char *elem,
										 /* Pointer to the DB_DATA_INFO_TYPE structure to be
												filled */
										 DB_DATA_INFO_TYPE *dbinfo
										 )
#else
int ()
#endif
{

	DATA_LIST_ENTRY_TYPE *data;
	int id, ierr=0;

	/* Initializations */
	init_db_data_info(dbinfo);
	strcpy(dbinfo->name, name);
	strcpy(dbinfo->elem, elem);

	/* Check if the database is open */
	if(!(db))
		return(DB_IS_NOT_OPEN);

	/* Get the identifier for variable name */
	ierr = get_db_variable_id(name, &id);
	if(DBERROR(&ierr, "get_db_data_id()\n"))
		return(ierr);
	dbinfo->entry_id = (SHORT) id;

	/* Fill information structure */
	if(!(strcmp(name, "TIME"))){
		dbinfo->count = 1;
		dbinfo->size  = (SHORT) sizeof(YMDHMS_TIME_TYPE);
	}
	else if(!(strcmp(name, "POSITION"))){
			dbinfo->count = 2;
			dbinfo->size  = (SHORT) sizeof(DMSH_POSITION_TYPE);
	}
	else if(!(strcmp(name, "LATITUDE"))){
			dbinfo->count = 1;
			dbinfo->size  = (SHORT) sizeof(DMSH_POSITION_TYPE);
	}
	else if(!(strcmp(name, "LONGITUDE"))){
			dbinfo->count = 1;
			dbinfo->size  = (SHORT) sizeof(DMSH_POSITION_TYPE);
	}
	else{
		data = db->data_list + dbinfo->entry_id;
		if(data->value_type == STRUCT_VALUE_CODE){
			ierr = get_db_struct_info(name, elem, dbinfo);
			if(DBERROR(&ierr, "get_db_struct_info()\n"))
				return(ierr);
		}
		else{
			strcpy(dbinfo->units, data->units);
			dbinfo->value_type = data->value_type;
			dbinfo->count      = 1;
			dbinfo->size       = (SHORT) VALUE_SIZE[data->value_type];
			dbinfo->offset     = data->offset;
			dbinfo->scale      = data->scale;
		}
	}

	return(ierr);
}

/* ------------------------------------------------------------------
   When extracting data for a structure element, the whole structure is
   firstly extracted. Given the names of the structure and its element to
   extract, this function flushes all values of element only at the
   beginning of the memory space allocated for the structure data.
   
	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int flush_db_elem(/* Structure name */
									char *name,
									/* Name of structure element */
									char *elem,
									/* Pointer to structure data */
									char *data,
									/* On input, this is the number of bytes in the structure
										 data. On output, it is updated to the number of bytes for
										 the element data only. */
									unsigned int *nb
									)
#else
int ()
#endif
{

	STRUCT_DEF_ELEM_TYPE elem_def;
	long int struct_nb;
	unsigned int offset, elem_nb;
	char *d1, *d2;
	int n, i, ierr=0;

	/* Load the structure definition */
	if(!(db->block_strdef_loaded) && (ierr = load_block_strdef())){
		DBERROR(&ierr, "load_block_strdef()");
		return(ierr);
	}

	/* Get the structure size */
	struct_nb = get_struct_size(name, db->block_strdef);
	ierr = (struct_nb <= 0) ? UNEXPECTED_STRUCTURE_SIZE : 0;
	if(DBERROR(&ierr, "get_struct_size()"))
		return(ierr);
	
	/* Get the element information */
	ierr = find_elem(elem, name, db->block_strdef, &offset, &elem_nb, 
									 &elem_def);
	if(DBERROR(&ierr, "find_elem()"))
		return(ierr);
	
	/* Flushes all element values at the beginning of the data array */
	d1 = d2 = data;
	n = 0;
	while((n*struct_nb) < (*nb)){
		/* Pointer to element in structure */
		d1 = data + (n*struct_nb) + offset;
		/* Pointer to destination for element */
		d2 = data + (n*elem_nb);
		/* Copy bytes */
		for(i=0; i<((int) elem_nb); i++)
			d2[i] = d1[i];
		/* Update number of elements */
		n++;
	}
	
	/* Update number of total bytes for element */
	*nb = n * elem_nb;
	
	return(ierr);
}

/* ------------------------------------------------------------------
   Given a name to a variable or structure present in the current CODAS
   database, and eventually a name to one of the structure elements, this
   function extracts all the data associated to this variable. No scaling
   is performed, but the function returns also the identifier for the
   specified variable or structure name, so that further information for
   scaling can be easily obtained.

   On error, allocated space for data is released, the data pointer is
   set to NULL and the number of of allocated bytes to zero.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_data(/* Variable or structure name */
								char *name,
								/* Eventually, structure element name, or NULL or the empty
									 string  */
								char *elem,
								/* Pointer to data. The required memory space is allocated
									 within the function and returns the base address on output.
									 On input, if non-NULL, a call to free is perfomed. Hence,
									 if not allocated on input, this pointer should be
									 initialized to NULL before calling this function */
								char **data,
								/* Identifier for the specified variable or structure name. If
									 the specified name is not valid, it is set to EOF */
								int *id,
								/* Number of bytes allocated for data */
								unsigned int *nb
								)
#else
int ()
#endif
{

	int ierr=0;

	/* Initializations */
	*nb = 0;
	*id = EOF;

	/* Free data pointer */
	if(*data){
		free((void *) (*data));
		*data = NULL;
	}
	
	/* Check if the database is open */
	if(!(db))
		return(DB_IS_NOT_OPEN);
	
	/* Get the variable identifier */
	ierr = get_db_variable_id(name, id);
	if(DBERROR(&ierr, "get_db_variable_id()\n"))
		return(ierr);

	/* Get the number of bytes required for this variable */
	DBGET(id, NULL, nb, &ierr);
	if(DBERROR(&ierr, "DBGET: getting number of bytes"))
		return(ierr);

	/* Check the number of bytes */
	if(error_found( (*nb) <= 0, "DBGET: No data for specified variable\n"))
		return(NO_SUCH_ELEMENT);

	/* Allocate space for data */
	(*data) = (char *) calloc(*nb, sizeof(char));
	ierr = ( !(*data) ) ? INSUFFICIENT_MEMORY : 0;
	if(DBERROR(&ierr, "calloc(): space for data\n"))
		return(INSUFFICIENT_MEMORY);
	
	/* Get the data */
	DBGET(id, *data, nb, &ierr);
	if(DBERROR(&ierr, "DBGET: get variable data"))
		goto on_error;

	/* Check if the data is from an element in a structure */
	if(is_db_struct(*id) && elem && strlen(elem)){
		ierr = flush_db_elem(name, elem, *data, nb);
		if(DBERROR(&ierr,	"flush_db_elem()\n"))
			goto on_error;
	}

on_error:

	/* Check for errors */
	if(ierr){
		free((void *) (*data));
		*data = NULL;
		*nb = 0;
	}

	return(ierr);
	
}

/* ------------------------------------------------------------------
   Given a variable or structure name present in the current CODAS
   database, and in the case of structures one of its element name, this
   function extract all data for this variable in the current profile. Data
   is scaled and returned as FLOAT.
   
   On error, the allocated space for data is released, the data pointer is
   set to NULL and the number of data to zero.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int get_db_data_f(/* Variable or structure name */
									char *name,
									/* Eventually, structure element name, or NULL, or the empyt
										 string */
									char *elem,
									/* Pointer to the data array. Memory space to hold data is
										 allocated within the function which returns the base
										 address of the array of FLOAT values. If non_NULL on
										 entry, a call to free is performed in order to release
										 memory previously allocated. If not allocated, this
										 pointer should be initialized to NULL before calling this
										 function. */
									FLOAT **data,
									/* Number of data elements in the array of FLOAT pointed by
										 data */
									unsigned int *n,
									/* This is an additionnal parameter used for data
										 conversions. Possible values are depend on the variable
										 to extract:
										 
										    TIME: it must be the year base for converting time
												      into decimal days.

												LONGITUDE: if non-zero, longitude will be converted
												           into values between -180 and +180.
												           Otherwise they are converted into values
												           between 0 and 360. */ 
									int param 
									)
#else
int ()
#endif
{

	DMSH_POSITION_TYPE *pos;
	DB_DATA_INFO_TYPE dbinfo;
	char *d=NULL;
	
	int i, id, ierr=0;
	unsigned int nb;
	
	/* Initializations */
	id = EOF;
	*n = nb = 0;

	/* Free data pointer */
	if(*data){
		free((void *) (*data));
		*data = NULL;
	}

	/* Initialize data info structure */
	init_db_data_info(&dbinfo);
	ierr = get_db_data_info(name, elem, &dbinfo);
	if(DBERROR(&ierr, "get_db_data_info()\n"))
		return(ierr);

	/* Check that data can be converted to a FLOAT */
	if(error_found( (dbinfo.entry_id == STRUCT_VALUE_CODE) 
									&& (!(elem) || !(strlen(elem))),
									"Cannot convert structures to FLOAT\n"))
		return(UNDEFINED_STRUCTURE);

	/* Get all data for this variable */
	ierr = get_db_data(name, elem, &d, &id, &nb);
	if(DBERROR(&ierr, "get_db_data()\n"))
		return(ierr);
	
	/* Calculate the number of data */
	*n = round(nb / dbinfo.size);

	/* Allocate memory */
	(*data) = (FLOAT *) calloc((size_t) *n, sizeof(FLOAT));
	ierr = ( !(*data) ) ? INSUFFICIENT_MEMORY : 0;
	if(error_found(ierr, "calloc(): FLOAT data\n"))
		goto on_error;
	
	/* Convert data */
	if(dbinfo.entry_id == TIME){
		if(invalid_time((YMDHMS_TIME_TYPE *) d))
			(*data)[0] = BADFLOAT;
		else
			(*data)[0] = (FLOAT) year_day((YMDHMS_TIME_TYPE *) d, param);
	}
	else if(dbinfo.entry_id == POSITION){
		pos = (DMSH_POSITION_TYPE *) d;
		for(i=0; i<2; i++){
			if((pos + i)->degree == BADSHORT)
				(*data)[i] = BADFLOAT;
			else
				(*data)[i] = ((FLOAT) POSHUN(pos+i)) / 360000.0;
		}
		if(param)
			(*data)[0] = to180( (*data)[0] );
		else
			(*data)[0] = to360( (*data)[0] );
		if(!(strcmp(name, "LONGITUDE")))
			*n = 1;
		if(!(strcmp(name, "LATITUDE"))){
			*n = 1;
			(*data)[0] = (*data)[1];
		}
	}
	else{
		/* Check that scaling is defined */
		ierr = ((dbinfo.offset < ADJ_BADFLOAT) && (dbinfo.scale < ADJ_BADFLOAT)) ?
			0 : INVALID_SCALE;
		if(DBERROR(&ierr, "Undefined scaling parameters"))
			goto on_error;

		/* Unscale data */
		ierr = (*unscale[dbinfo.value_type])(*data, d, &(dbinfo.scale),
																					&(dbinfo.offset), n);
		if(DBERROR(&ierr, "(*unscale[])()"))
			goto on_error;
	}

on_error:

	/* Release memory */
	if(d) free((void *) d);

	/* Check for errors */
	if(ierr){
		if(*data){
			free((void *) (*data));
			*data = NULL;
		}
		*n = 0;
	}

	return(ierr);

}

/* ------------------------------------------------------------------
   Given a variable or structure name present in the current CODAS
   database, and in the case of structures one of its element name, this
   function extract all data for this variable in the current profile. Data
   is scaled and returned as DOUBLE.
   
   On error, the allocated space for data is released, the data pointer is
   set to NULL and the number of data to zero.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_data_d(/* Variable or structure name */
									char *name,
									/* Eventually, structure element name, or NULL, or the empyt
										 string */
									char *elem,
									/* Pointer to the data array. Memory space to hold data is
										 allocated within the function which returns the base
										 address of the array of FLOAT values. If non_NULL on
										 entry, a call to free is performed in order to release
										 memory previously allocated. If not allocated, this
										 pointer should be initialized to NULL before calling this
										 function. */
									DOUBLE **data,
									/* Number of data elements in the array of FLOAT pointed by
										 data */
									unsigned int *n,
									/* This is an additionnal parameter used for data
										 conversions. Possible values are depend on the variable
										 to extract:
										 
										    TIME: it must be the year base for converting time
												      into decimal days.

												LONGITUDE: if non-zero, longitude will be converted
												           into values between -180 and +180.
												           Otherwise they are converted into values
												           between 0 and 360. */ 
									int param 
									)
#else
int ()
#endif
{
	
	FLOAT *tmp=NULL;

	int i;
	int ierr=0;

	/* Initializations */
	*n = 0;

	/* Free data pointer */
	if(*data){
		free((void *) (*data));
		*data = NULL;
	}

	/* Get data as floats first */
	ierr = get_db_data_f(name, elem, &tmp, n, param);
	if(DBERROR(&ierr, "get_db_data_f()\n"))
		goto on_error;
	
	/* Allocate space for data */
	if(*n > 0){
		(*data) = (DOUBLE *) calloc((size_t) (*n), sizeof(DOUBLE));
		ierr = (!(*data)) ? INSUFFICIENT_MEMORY : 0;
	}
	if(DBERROR(&ierr, "calloc(): DOUBLE data"))
		goto on_error;
	
	/* Convert data to DOUBLE */
	for(i=0; i<((int) (*n)); i++){
		if(tmp[i] < ADJ_BADFLOAT)
			(*data)[i] = (DOUBLE) tmp[i];
		else
			(*data)[i] = BADDOUBLE;
	}

on_error:

	/* Release memory */
	if(tmp) free(tmp);

	/* Check for errors */
	if(ierr){
		if(*data){
			free(*data);
			*data = NULL;
		}
		*n = 0;
	}

	return(ierr);
}

/* ------------------------------------------------------------------
   This function extract the current profile time and converts it into
   decimal days, in DOUBLE format.

	 RETURNS: CODAS error code
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int get_db_time_d(/* Pointer to the decimal day variable */
									DOUBLE *yd,
									/* Year base to use for conversion into decimal days */
									int year_base
									)
#else
int ()
#endif
{

	FLOAT *data=NULL;
	int n,ierr=0;

	/* Get profile time */
	ierr = get_db_data_f("TIME", "", &data, (unsigned int *)&n, year_base);
	if(DBERROR(&ierr, "get_db_data_f(): TIME"))
		goto on_error;

	/* Check that only one data has been extracted */
	ierr = (n != 1) ? INVALID_NUMBER : 0;
	if(ierr) goto on_error;

	/* Copy profile time */
	*yd = (DOUBLE) *data;

on_error:
	
	if(data) free(data);
	
	return(ierr);

}

/* ------------------------------------------------------------------
   This function extracts the current profile position and converts it into
   decimal values in DOUBLE format.
	 
	 RETURNS: Error status.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_db_pos_d(/* Pointer to an array of two DOUBLE elements. On output,
										longitude data is located in the first element, latitude
										data is in the second one */
								 DOUBLE *pos,
								 /* Parameter for converting longitude data. If non-zero,
										these are converted into values between -180 and +180. */
								 int lon_180
								 )
#else
int ()
#endif
{

	FLOAT *data=NULL;
	int n, ierr=0;

	/* Get profile position */
	ierr = get_db_data_f("POSITION", "", &data, (unsigned int *) &n, lon_180);
	if(DBERROR(&ierr, "get_db_data_f(): POSITION"))
		goto on_error;

	/* Check that only two data have been extracted */
	ierr = (n != 2) ? INVALID_NUMBER : 0;
	if(ierr) goto on_error;

	/* Copy positions */
	pos[0] = (DOUBLE) data[0];
	pos[1] = (DOUBLE) data[1];

on_error:
	
	if(data) free(data);

	return(ierr);

}

/* ------------------------------------------------------------------
   Given a position index and its code, this function seeks the current
   database until it has passed the specified profile. In a certain way,
   this works as DBSRCH(). However seeking is always performed forward, 
   and searching can be done according to more criteria. These are:

			   TIME_SEEK:      The new position is given by a YMDHMS_TIME_TYPE
				                 structure.

				 DAY_SEEK:       The given position is a time in decimal days in
				                 DOUBLE format. The value of the year base to use for
				                 time conversion is required.

				 LONGITUDE_SEEK: The given position is a longitude in decimal degrees,
				                 in DOUBLE format. The flag for converting longitude
				                 values between -180 and +180 is required.

				 LATITUDE_SEEK:  The new position is a latitude in decimal degrees in
				                 DOUBLE format. 

				 BLOCK_SEEK:     The new position is a block number.

				 BLKPRF_SEEK:    The new position is given by a BLKPRF_INDEX_TYPE
				                 structure, specifiying the block and profile number. 
			
			
	 Optionnally, a RANGE_TYPE structure can be specified to allow seeking
   only within a certain range. In this case, seeking is started from the
   beginning of the specified range.

   On output, the new position of the current database is the profile
   located just after or at the given position.
			
	 RETURNS: CODAS error status. If no element is found, returns
	          NO_SUCH_BLOCK_PROFILE
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int seek_db(/* Profile position to move to. See the function description for
							 the different types and structures to use. */
						char *index,
						/* Index code specifying the position criteria. See the function
						description for possible values. */
						int code,
						/* If non-NULL, this specifies the range within which the database
						should be seeked. */
						RANGE_TYPE *range,
						/* An additionnal parameter, required by some position criteria.
						See the function function description for more information. */
						int par,
						/* The number of profiles moved from the initial position, or from
							 the start of the specified range to get to the given position,
							 or EOF if the position is not found */
						int *n
						)
#else
int ()
#endif
{

	YMDHMS_TIME_TYPE *time, this_time;
	BLKPRF_INDEX_TYPE *bp, blk, tmp, this_bp;
	DOUBLE_LL_TYPE this_pos;
	DOUBLE yd, this_yd, lon, lat;
	int yb, lon_180;

	int ierr=0, err, done, prev, curr, steps=1, id;
	unsigned int nb;

	/* Initializations */
	*n = EOF;

	/* Initialize pointers according to position criteria */
	switch(code)
		{
		case TIME_SEEK:
			time = (YMDHMS_TIME_TYPE *) index;
			break;
		case DAY_SEEK:
			yd = *( (DOUBLE *) index );
			yb = par;
			break;
		case LONGITUDE_SEEK:
			lon = *( (DOUBLE *) index );
			lon_180 = par;
			break;
		case LATITUDE_SEEK:
			lat = *( (DOUBLE *) index );
			break;
		case BLOCK_SEEK:
			blk.block = *( (int *) index );
			blk.profile = 0;
			break;
		case BLKPRF_SEEK:
			bp = (BLKPRF_INDEX_TYPE *) index;
			break;
		default:
			ierr = error_found(INVALID_NUMBER, "Unknown seek code\n");
			break;
		}
	if(ierr) return(ierr);

	/* Position DB at start of range */
	if(range){
		ierr = goto_start_of_range(range);
		ierr = db_not_beof(ierr);
		if(DBERROR(&ierr, "goto_start_of_range()"))
			return(ierr);
	}
	
	/* Move inside database until position, counting number of profiles */
	*n = done = prev = curr = 0;
	while(!ierr && !done){
		/* Check current position */
		switch(code)
			{
			case TIME_SEEK:
				/* Get current time */
				nb = sizeof(YMDHMS_TIME_TYPE);
				id = TIME;
				DBGET(&id, (char *) &this_time, &nb, &ierr);
				DBERROR(&ierr, "DBGET(): TIME");
				curr = ( (int) HTIMDIF(time, &this_time) > 0) ? +1 : -1;
				break;
			case DAY_SEEK:
				/* Get decimal day */
				ierr = get_db_time_d(&this_yd, yb);
				DBERROR(&ierr, "get_db_time_d()");
				curr = (this_yd > yd) ? +1 : -1;
				break;
			case LONGITUDE_SEEK:
				/* Get longitude */
				ierr = get_db_pos_d((DOUBLE *) &(this_pos), lon_180);
				DBERROR(&ierr, "get_db_pos_d()");
				curr = (this_pos.lon > lon) ? +1 : -1;
				break;
			case LATITUDE_SEEK:
				/* Get longitude */
				ierr = get_db_pos_d((DOUBLE *) &(this_pos), lon_180);
				DBERROR(&ierr, "get_db_pos_d()\n");
				curr = (this_pos.lat > lat) ? +1 : -1;
				break;
			case BLOCK_SEEK:
				/* Get block profile index */
				nb = sizeof(BLKPRF_INDEX_TYPE);
				id = BLOCK_PROFILE_INDEX;
				DBGET(&id, (char *) &this_bp, &nb, &ierr);
				DBERROR(&ierr, "DBGET(): BLOCK_PROFILE_INDEX");
				curr = (this_bp.block != blk.block) ? +1 : -1;
				break;
			case BLKPRF_SEEK:
				/* Get block profile index */
				nb = sizeof(BLKPRF_INDEX_TYPE);
				id = BLOCK_PROFILE_INDEX;
				DBGET(&id, (char *) &this_bp, &nb, &ierr);
				DBERROR(&ierr, "DBGET(): BLOCK_PROFILE_INDEX");
				curr = ( !(BPCMP(&this_bp, bp)) ) ? +1 : -1;
				break;
			default:
				ierr = error_found(INVALID_NUMBER, "Unknown seek code\n");
				break;
			}
		/* Check for errors */
		if(error_found(ierr, "Getting profile position\n"))
			return(ierr);

		/* Loop exit flag */
		done = ((curr * prev) < 0) ? 1 : 0;
		prev = curr;
		
		/* Move inside database */
		if(!done){
			DBMOVE(&steps, &ierr);
			err = db_not_eof(ierr);
			DBERROR(&err, "DBMOVE()");
		}
		/* Update number of elements */
		if(!done && !ierr)
			(*n)++;
		/* Control current position */
		if(!done && !ierr && range && !(in_range((char *) &tmp, range))){
			ierr = SEARCH_BEYOND_END;
		}
	}

	/* Set the number of profiles */
	*n = (done) ? *n : EOF;
	
	ierr = (db_eof(ierr) && !done) ? NO_SUCH_BLOCK_PROFILE : ierr;

	return(ierr);
}
