#include "dbinc.h"
/*****************************************************************************
*                                                                            *
*      COMMON OCEANOGRAPHIC DATA ACCESS SYSTEM (CODAS)                       *
*                                                                            *
*      WRITTEN BY:  RAMON CABRERA, ERIC FIRING, and JULIE RANADA             *
*                   JOINT INSTITUTE FOR MARINE AND ATMOSPHERIC RESEARCH      *
*                   1000 POPE ROAD  MSB 404                                  *
*                   HONOLULU, HI 96822                                       *
*                                                                            *
*      VERSION:     3.00                                                     *
*                                                                            *
*      DATE:        APRIL 1989                                               *
*                                                                            *
*****************************************************************************/

/*
       FILE:  dbget.c

              SYSTEM FUNCTIONS FOR RETRIEVING DATA FROM AN
              EXISTING DATABASE
*/
/*-----------------------------------------------------------------------------

   FUNCTION:  DBGET

      It retrieves data from the database into the user's buffer.

   PARAMETERS:  
   
      type = pointer to data type

      data = address of user's buffer

             / on entrance, nb contains the number of bytes that the
      nb = /   user's buffer can accomodate
           \   on exit, nb contains the actual number of bytes that
             \ have been stored in the user buffer
              
      ierr = pointer to error code

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void DBGET(int *type, char *data, unsigned int *nb, int *ierr)
#else
void DBGET(type, data, nb, ierr)
int *type, *ierr;
char *data;
unsigned int *nb;
#endif
{
   int nbytes;
   long ofs;
   STRUCT_DEF_HDR_TYPE *strdef;

   if ( (*ierr = dbget_flags()) != 0 ) {*nb = 0; return;}
/*
  ---> PROFILE DATA (types 0 - 99)
*/
   if ( (*type >= 0) && (*type < (int)db->block_hdr.data_list_nentries) )
   {
      if (!(db->profile_is_open)) {*ierr = NO_PROFILE_IS_OPEN; *nb = 0; return;}
      if (db->data_list[*type].access_type == UNUSED) {*ierr = INVALID_TYPE_REQUEST; *nb = 0; return;}
      if (*nb == 0) {get_access_param(type, nb, &ofs); return;}
      if (get_profile_data(type, data, nb)) goto error_found;
   }
   else if ( (*type >= 100) && (*type < 200) )
   {
      switch (*type)
      {
         case DATASET_ID:
            if (*nb == 0) {*nb = sizeof(DATASET_ID_TYPE); return;}
            *nb = *nb >= sizeof(DATASET_ID_TYPE) ? sizeof(DATASET_ID_TYPE) : 0;
            move_byte((char *) &(db->block_hdr.dataset_id), data, *nb);
            break;
         case PRODUCER_ID:
            if (*nb == 0) {*nb = sizeof(PRODUCER_ID_TYPE); return;}
            *nb = *nb >= sizeof(PRODUCER_ID_TYPE) ? sizeof(PRODUCER_ID_TYPE) : 0;
            move_byte((char *) &(db->block_hdr.producer_id), data, *nb);
            break;
         case TIME:
            if (*nb == 0) {*nb = sizeof(YMDHMS_TIME_TYPE); return;}
            if (!(db->profile_is_open))
            {
               *ierr = NO_PROFILE_IS_OPEN;
               *nb = 0;
               return;
            }
            *nb = *nb >= sizeof(YMDHMS_TIME_TYPE) ? sizeof(YMDHMS_TIME_TYPE) : 0;
            if (*nb)
            {
               if (db->block_hdr.dir_time_flag == DIR_TIME_IN_SECONDS)
                  DIFTIM( (YMDHMS_TIME_TYPE *) &(db->block_base_time),
                     (YMDHMS_TIME_TYPE *) data,
                     (LONG *) &(db->profile_dir_entry_ptr->time) );
               else
                  HDIFTIM( (YMDHMS_TIME_TYPE *) &(db->block_base_time),
                     (YMDHMS_TIME_TYPE *) data,
                     (LONG *) &(db->profile_dir_entry_ptr->time) );
            }
            break;
         case POSITION:
            if (*nb == 0) {*nb = 2 * sizeof(DMSH_POSITION_TYPE); return;}
            *nb = *nb >= 2 * sizeof(DMSH_POSITION_TYPE) ? 2 * sizeof(DMSH_POSITION_TYPE) : 0;
            if (*nb)
               *ierr = get_position((DMSH_POSITION_TYPE *) data);
            break;
         case DATA_MASK:
            if (*nb == 0) {*nb = sizeof(ULONG); return;}
            *nb = *nb >= sizeof(ULONG) ? sizeof(ULONG) : 0;
            move_byte((char *) &(db->block_hdr.data_mask), data, *nb);
            break;
         case DATA_PROC_MASK:
            if (*nb == 0) {*nb = sizeof(ULONG); return;}
            *nb = *nb >= sizeof(ULONG) ? sizeof(ULONG) : 0;
            move_byte((char *) &(db->block_hdr.data_proc_mask), data, *nb);
            break;
         case DEPTH_RANGE:
            if (*nb == 0) {*nb = sizeof(DEPTH_RANGE_TYPE); return;}
            *nb = *nb >= sizeof(DEPTH_RANGE_TYPE) ? sizeof(DEPTH_RANGE_TYPE) : 0;
            if (*nb) *ierr = get_depth_range((DEPTH_RANGE_TYPE *) data);
            break;
         default:
            *ierr = INVALID_TYPE_REQUEST;
            *nb = 0;
            break;

      }
   }
   else if ( (*type >= 200) && (*type < 300) )
   {
      switch (*type)
      {
         case DATABASE_VERSION:
            if (*nb == 0) {*nb = 4; return;}
            *nb = *nb >= 4 ? 4 : 0;
            move_byte(db->block_hdr.db_version, data, *nb);
            break;
         case PRODUCER_HOST:
            if (*nb == 0) {*nb = 4; return;}
            *nb = *nb >= 4 ? 4 : 0;
            strncpy(data, db->block_hdr.producer_host, *nb);
            break;
         case DATABASE_NUMBER:
            if (*nb == 0) {*nb = sizeof(int); return;}
            *nb = *nb >= sizeof(int) ? sizeof(int) : 0;
            if (*nb) *((int *) data) = current_database + 1;
            break;
         case BLOCK_PROFILE_INDEX:
            if (*nb == 0) {*nb = 2 * sizeof(int); return;}
            *nb = *nb >= 2 * sizeof(int) ? 2 * sizeof(int) : 0;
            if (*nb)
            {
               move_byte((char *) &(db->block_dir_index), data, sizeof(int));
               move_byte((char *) &(db->profile_dir_index), data + sizeof(int),
                  sizeof(int));
            }
            break;
         default:
            *ierr = INVALID_TYPE_REQUEST;
            *nb = 0;
            break;
      }
   }
   else
   {
      switch (*type)
      {
         case BLOCK_DIR_FILE:
            if (*nb == 0) {*nb = strlen(db->block_dir_file); return;}
            *nb = *nb >= strlen(db->block_dir_file) ? strlen(db->block_dir_file) : 0;
            strcpy(data, db->block_dir_file);
            break;
         case BLOCK_FILE:
            if (*nb == 0) {*nb = strlen(db->block_file); return;}
            *nb = *nb >= strlen(db->block_file) ? strlen(db->block_file) : 0;
            strcpy(data, db->block_file);
            break;
         case BLOCK_DIR_HDR:
            if (*nb == 0) {*nb = BLOCK_DIR_HDR_SIZE; return;}
            *nb = *nb >= BLOCK_DIR_HDR_SIZE ? BLOCK_DIR_HDR_SIZE : 0;
            move_byte((char *) &(db->block_dir_hdr), data, *nb);
            break;
         case BLOCK_HDR:
            if (*nb == 0) {*nb = BLOCK_HDR_SIZE; return;}
            *nb = *nb >= BLOCK_HDR_SIZE ? BLOCK_HDR_SIZE : 0;
            move_byte((char *) &(db->block_hdr), data, *nb);
            break;
         case STRUCTURE_DEF:  /* assumes "data" points to name of structure */
            if (!db->block_strdef_loaded)
               if (load_block_strdef())
                  goto error_found;
            if ( (strdef = find_def(data, db->struct_def)) == NULL )
               *ierr = STRUCTURE_NOT_PRESENT;
            else
            {
               nbytes = (1 + strdef->nelem) * sizeof(STRUCT_DEF_ENTRY_TYPE);
               if (*nb == 0) *nb = nbytes;  /* query */
               else
               {
                  *nb = *nb >= nbytes ? nbytes : 0;
                  move_byte((char *) strdef, data, *nb);
               }
            }
            break;
         case BLOCK_FTR:
            nbytes = db->block_hdr.block_ftr_ofs <= 0 ? 0 :
		       (int)(db->block_hdr.block_nbytes - db->block_hdr.block_ftr_ofs);
            if (*nb == 0) {*nb = nbytes; return;}
            if (*nb > nbytes) *nb = nbytes;
            if (*nb > 0)
            {
               if ((db->error_code = read_data(db->fpblkdata, data, db->block_hdr.block_ftr_ofs, *nb)) != 0)
               {
                  db->error_data = BLOCK_FTR;
                  goto error_found;
               }
               /* block footer is of type text so we won't bother to
                  do a platform check/conversion on it -- JR 95/07 */
            }
            break;
         default:
            *ierr = INVALID_TYPE_REQUEST;
            *nb = 0;
            break;
      }
   }
   return;

   error_found:
      report_db_error("DBGET");
      *ierr = db->error_code * 1000 - db->error_data;
}

/*-----------------------------------------------------------------------------

   FUNCTION:  get_profile_data

      It extracts user-defined data (types 0 to 99) from current profile
      if n > 0; if n = 0, it simply updates n to the number of bytes needed
      for that data type without retrieving the data.

   PARAMETERS:

      type = pointer to data type

      data = array to hold profile data

      user_nb = pointer to number of bytes that the user array can hold

   RETURNS:  0 if okay
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int get_profile_data(int *type, char *data, unsigned int *user_nb)
#else
int get_profile_data(type, data, user_nb)
int *type;
char *data;
unsigned int *user_nb;
#endif
{
   unsigned int nb, nv;
   long ofs;

   get_access_param(type, &nb, &ofs);
   *user_nb = min_val(*user_nb, nb);
   if (*user_nb > 0)
   {
      if ((db->error_code = read_data(db->fpblkdata, data, ofs, *user_nb)) != 0)
      {
         db->error_data = PROFILE_DATA;
         goto error_found;
      }
      if (db->block_host != HOST_ENVIRONMENT)
      {
         if (db->data_list[*type].value_type == STRUCT_VALUE_CODE)
         {
            nv = -*user_nb;
            if (!db->block_strdef_loaded)
               if (load_block_strdef())
                  goto error_found;
         }
         else
            nv = *user_nb / VALUE_SIZE[db->data_list[*type].value_type];
         if (convert_select(data, data, db->block_host, HOST_ENVIRONMENT,
            nv, db->data_list[*type].value_type,
            db->data_list[*type].name, db->struct_def) == BADUINT)
         {
            db->error_code = CONVERT_ERROR;
            db->error_data = *type;
            goto error_found;
         }
      }
   }
   return(0);

   error_found:
      *user_nb = 0;
      report_db_error("get_profile_data");
      return(db->error_code);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  get_position

      It retrieves the position parameter for the current profile.

   PARAMETER:

      data = short integer array for storing latitude and longitude

   RETURNS:  0 if okay
             NO_PROFILE_IS_OPEN otherwise

*/
#if PROTOTYPE_ALLOWED
int get_position(DMSH_POSITION_TYPE *data)
#else
int get_position(data)
DMSH_POSITION_TYPE *data;
#endif
{
   int profile_key_type;

   profile_key_type = db->block_hdr.dir_type % 10;
   if ((profile_key_type == 0) || (profile_key_type == 2))
   {
      HUNPOS(&data[0], &(db->block_hdr.lon1));
      HUNPOS(&data[1], &(db->block_hdr.lat1));
   }
   else if ((profile_key_type == 1) || (profile_key_type == 3))
   {
      if (!(db->profile_is_open))
         return(NO_PROFILE_IS_OPEN);
      HUNPOS(&data[0], &(db->profile_dir_entry_ptr->lon));
      HUNPOS(&data[1], &(db->profile_dir_entry_ptr->lat));
   }
   return(0);
}

/*-----------------------------------------------------------------------------

   FUNCTION:  get_depth_range

      It retrieves the depth range parameter for the current profile.

   PARAMETER:

      data = short integer array for storing depth range

   RETURNS:  0 if okay
             NO_PROFILE_IS_OPEN otherwise

*/
#if PROTOTYPE_ALLOWED
int get_depth_range(DEPTH_RANGE_TYPE *data)
#else
int get_depth_range(data)
DEPTH_RANGE_TYPE *data;
#endif
{
   int profile_key_type;

   profile_key_type = db->block_hdr.dir_type % 10;
   if ((profile_key_type == 0) || (profile_key_type == 1))
   {
      data->min_depth = db->block_hdr.depth1;
      data->max_depth = db->block_hdr.depth2;
   }
   else if ((profile_key_type == 2) || (profile_key_type == 3))
   {
      if (!(db->profile_is_open))
         return(NO_PROFILE_IS_OPEN);
      data->min_depth = db->profile_dir_entry_ptr->depth1;
      data->max_depth = db->profile_dir_entry_ptr->depth2;
   }
   return(0);
}

