#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:  dbput.c

              FUNCTIONS FOR UPDATING DATA IN AN EXISTING DATABASE

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

   FUNCTION:  DBPUT

      It updates data in the database.

   PARAMETERS:
   
      type = pointer to data type

      data = address of user buffer

      nb    = pointer to no. of bytes in user buffer
              (should not exceed size of originally reserved space)

      ierr = pointer to error code

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void DBPUT(int *type, char *data, unsigned int *nb, int *ierr)
#else
void DBPUT(type, data, nb, ierr)
int *type, *ierr;
char *data;
unsigned int *nb;
#endif
{
   if ((*ierr = dbget_flags()) != 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) {*ierr = INVALID_BUFFER_SIZE; return;}
      if (put_profile_data(type, data, nb, ierr)) goto error_found;
      return;
   }
   else 
   {
      switch (*type)
      {
         case DATA_PROC_MASK:
            db->block_hdr.data_proc_mask = *((ULONG *) data);
            db->block_modified = 1;
            *nb = sizeof(ULONG);
            break;
         case POSITION:
            *ierr = put_position((DMSH_POSITION_TYPE *) data);
            *nb = 2 * sizeof(DMSH_POSITION_TYPE);
            break;
         case DEPTH_RANGE:
            *ierr = put_depth_range((DEPTH_RANGE_TYPE *) data);
            *nb = sizeof(DEPTH_RANGE_TYPE);
            break;
         case STRUCTURE_DEF:
            *ierr = put_struct_def((STRUCT_DEF_HDR_TYPE *) data, nb);
            break;
         case PRODUCER_ID:
	    strncpy((char *) &(db->block_hdr.producer_id), data, 
	       sizeof(PRODUCER_ID_TYPE));
            db->block_modified = 1;
	    *nb = min_val(sizeof(PRODUCER_ID_TYPE), strlen(data));
	    break;
         case DATA_MASK:
            db->block_hdr.data_mask = *((ULONG *) data);
            db->block_modified = 1;
            *nb = sizeof(ULONG);
            break;
         default:
            *ierr = INVALID_TYPE_REQUEST;
            break;
      }
   }
   if (!(*ierr))
      return;

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

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

   FUNCTION:  put_profile_data

      It rewrites user-defined data (types 0 to 99) in current profile.

   PARAMETERS:

      type = pointer to data type

      data = array containing the data to be written to file

      user_nb = pointer to no. of bytes in user buffer

      ierr = pointer to error code

   RETURNS:  0 if okay
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int put_profile_data(int *type, char *data, unsigned int *user_nb, int *ierr)
#else
int put_profile_data(type, data, user_nb, ierr)
int *type, *ierr;
char *data;
unsigned int *user_nb;
#endif
{
   unsigned int nb, i, excess_start, excess_end;
   long ofs;
   char *copy;
   int return_value;

   get_access_param(type, &nb, &ofs);
   if (*user_nb == nb)
      return(put_profile_aux(data, nb, ofs));
   if (*user_nb > nb)
   {
      *ierr = INSUFFICIENT_SPACE;
      *user_nb = nb;               /* update to no. of bytes actually stored */
      return(put_profile_aux(data, nb, ofs)); /* store first <nb> bytes only */
   }
   /*--- else *user_nb < nb so we must "BAD-out" the excess storage space ---*/
   if ((copy = malloc(nb)) == NULL)        /* need a bigger buffer */
      return(INSUFFICIENT_MEMORY);
   move_byte(data, copy, *user_nb);        /* copy user data to new buffer */
   excess_start = *user_nb / VALUE_SIZE[db->data_list[*type].value_type];
   excess_end   = nb / VALUE_SIZE[db->data_list[*type].value_type];
   switch (db->data_list[*type].value_type)
   {
      case CHAR_VALUE_CODE   :
      case TEXT_VALUE_CODE   : for (i = excess_start; i < excess_end; i++)
                                  *(copy + i) = NUL;
                               break;
      case BYTE_VALUE_CODE   : for (i = excess_start; i < excess_end; i++)
                                  *(copy + i) = BADBYTE;
                               break;
      case STRUCT_VALUE_CODE :
      case UBYTE_VALUE_CODE  : for (i = excess_start; i < excess_end; i++)
                                  *( ((UBYTE *) copy) + i ) = BADUBYTE;
                               break;
      case SHORT_VALUE_CODE  : for (i = excess_start; i < excess_end; i++)
                                  *( ((SHORT *) copy) + i ) = BADSHORT;
                               break;
      case USHORT_VALUE_CODE : for (i = excess_start; i < excess_end; i++)
                                  *( ((USHORT *) copy) + i ) = BADUSHORT;
                               break;
      case LONG_VALUE_CODE   : for (i = excess_start; i < excess_end; i++)
                                  *( ((LONG *) copy) + i ) = BADLONG;
                               break;
      case ULONG_VALUE_CODE  : for (i = excess_start; i < excess_end; i++)
                                  *( ((ULONG *) copy) + i ) = BADULONG;
                               break;
      case FLOAT_VALUE_CODE  : for (i = excess_start; i < excess_end; i++)
                                  *( ((FLOAT *) copy) + i ) = BADFLOAT;
                               break;
      case DOUBLE_VALUE_CODE:  for (i = excess_start; i < excess_end; i++)
                                  *( ((DOUBLE *) copy) + i ) = BADDOUBLE;
                               break;
      case COMPLEX_VALUE_CODE: for (i = excess_start; i < excess_end; i++)
                               {
                                  ( ((COMPLEX *) copy) + i )->RE = BADFLOAT;
                                  ( ((COMPLEX *) copy) + i )->IM = BADFLOAT;
                               }
   }
   return_value = put_profile_aux(copy, nb, ofs);
   free(copy);
   *user_nb = nb;                  /* update to no. of bytes actually stored */
   return(return_value);
}

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

   FUNCTION:  put_profile_aux

      It performs the actual rewrite of profile data (types 0 to 99).

   PARAMETERS:

      data = pointer to where updated data are stored

      nb = number of bytes to rewrite

      ofs = offset into file at which to start rewriting

   RETURNS:  0 if okay
             SEEK_ERROR or WRITE_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int put_profile_aux(char *data, unsigned int nb, long ofs)
#else
int put_profile_aux(data, nb, ofs)
char *data;
unsigned int nb;
long ofs;
#endif
{
   if (nb > 0)
   {
      if ((db->error_code = write_data(db->fpblkdata, data, ofs, nb)) != 0)
      {
         db->error_data = PROFILE_DATA;
         goto error_found;
      }
   }
   return(0);

   error_found:
      report_db_error("put_profile_aux");
      return(db->error_code);
}

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

   FUNCTION:  put_position

      It updates the position parameter in the current profile.

   PARAMETER:

      data = array containing the updated longitude and latitude to be written

   RETURNS: 0 if okay
            INVALID_POSITION or NO_PROFILE_IS_OPEN otherwise

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

   profile_key_type = db->block_hdr.dir_type % 10;
   if (invalid_position(&data[0], 0) || invalid_position(&data[1], 1))
      return(INVALID_POSITION);
   if ((profile_key_type == 0) || (profile_key_type == 2))
   {
      db->block_hdr.lon1 = POSHUN(&data[0]);
      if (db->block_hdr.lon1 < 0) 
         db->block_hdr.lon1 += 129600000L;
      db->block_hdr.lon2 = db->block_hdr.lon1;
      db->block_hdr.lat1 = POSHUN(&data[1]);
      db->block_hdr.lat2 = db->block_hdr.lat1;
   }
   else if ((profile_key_type == 1) || (profile_key_type == 3))
   {
      if (!(db->profile_is_open)) 
         return(NO_PROFILE_IS_OPEN);
      db->profile_dir_entry_ptr->lon = POSHUN(&data[0]);
      if (db->profile_dir_entry_ptr->lon < 0)
         db->profile_dir_entry_ptr->lon += 129600000L;
      db->profile_dir_entry_ptr->lat = POSHUN(&data[1]);
      write_profile_dir_entry();
   }
   db->block_modified = 1;
   return(0);
}

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

   FUNCTION:  put_depth_range

      It updates the depth range parameter for the current profile.

   PARAMETER:

      data = array containing updated depth range to be written

   RETURNS:  0 if okay
             NO_PROFILE_IS_OPEN otherwise

*/
#if PROTOTYPE_ALLOWED
int put_depth_range(DEPTH_RANGE_TYPE *data)
#else
int put_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))
   {
      db->block_hdr.depth1 = data->min_depth;
      db->block_hdr.depth2 = data->max_depth;
   }
   else if ((profile_key_type == 2) || (profile_key_type == 3))
   {
      if (!(db->profile_is_open))
         return(NO_PROFILE_IS_OPEN);
      db->profile_dir_entry_ptr->depth1 = data->min_depth;
      db->profile_dir_entry_ptr->depth2 = data->max_depth;
      write_profile_dir_entry();
   }
   db->block_modified = 1;
   return(0);
}

/*jr++*/
/*-----------------------------------------------------------------------------

   FUNCTION:  put_struct_def

      It rewrites a block structure definition entry.

   RETURNS:  0 if okay
             error code otherwise

*/
#if PROTOTYPE_ALLOWED
int put_struct_def(STRUCT_DEF_HDR_TYPE *new_sd, unsigned int *nb)
#else
int put_struct_def(new_sd, nb)
STRUCT_DEF_HDR_TYPE *new_sd;
unsigned int *nb;
#endif
{
   int must_free = 0;
   STRUCT_DEF_HDR_TYPE *old_sd;

   if (!db->block_strdef_loaded)
   {
      if ((db->error_code = load_block_strdef()) != 0)
         return(db->error_code);
      must_free = 1;
   }
   if ((old_sd = find_def(new_sd->name, db->block_strdef)) == NULL)
   {
      db->error_code = STRUCTURE_NOT_PRESENT;
      goto error_found;
   }
   if (new_sd->nelem > old_sd->nelem)
   {
      db->error_code = INSUFFICIENT_SPACE;
      *nb = 0;                 /* no bytes stored */
      goto error_found;
   }
   else
   {
      if (new_sd->nelem < old_sd->nelem) /* want to NULL-out the excess */
         set_byte((UBYTE *) (old_sd + new_sd->nelem + 1), 0,
            (old_sd->nelem - new_sd->nelem) * STRUCT_DEF_ENTRY_SIZE);
      *nb = (old_sd->nelem + 1) * STRUCT_DEF_ENTRY_SIZE; /* no. bytes stored */
      move_byte((CHAR *) (new_sd + 1), (CHAR *) (old_sd + 1),
         new_sd->nelem * STRUCT_DEF_ENTRY_SIZE);
         /* we want to preserve the old_sd nelem so when we are searching
         for structure definitions, we don't fall into a hole and mistakenly
         think we've reached the end of the block structure definitions */
      if ((db->error_code = write_data(db->fpblkdata, (char *) db->block_strdef,
         db->block_hdr.data_list_ofs + db->block_hdr.data_list_nentries *
         DATA_LIST_ENTRY_SIZE,
         db->block_hdr.struct_def_nentries * STRUCT_DEF_ENTRY_SIZE)) != 0)
         goto error_found;
      if (must_free)
      {
         free(db->block_strdef);
         db->block_strdef_loaded = 0;
      }
      return(0);
   }
   error_found:
      if (must_free)
      {
         free(db->block_strdef);
         db->block_strdef_loaded = 0;
      }
      db->error_data = STRUCTURE_DEF;
      return(db->error_code);
}

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

   FUNCTION:  write_profile_dir_entry

      It rewrites the current profile directory entry.

   RETURNS:  0 if successful
             db->error_code otherwise

*/
#if PROTOTYPE_ALLOWED
int write_profile_dir_entry(void)
#else
int write_profile_dir_entry()
#endif
{
   unsigned int nb;
   long ofs;

   nb = db->block_hdr.dir_entry_nbytes;
   ofs = db->block_hdr.dir_ofs + nb * db->profile_dir_index;
   if ((db->error_code = write_data(db->fpblkdata,
      (char *) db->profile_dir_entry_ptr, ofs, nb)) != 0)
   {
      db->error_data = PROFILE_DIR;
      goto error_found;
   }
   return(0);

   error_found:
      report_db_error("write_profile_dir_entry");
      return(db->error_code);
}

