/******************************************************************************

   FILE:  VC1.C

      Routines to convert a CODAS block file loaded using version 2
      into a CODAS version 3 block file.

 ******************************************************************************/
#include "vc.h"

FILE *fpin;
long file_nbytes;
unsigned int profile_dir_entry_size;

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

    FUNCTION: convert_block_file

       Converts a CODAS block file named <infile> that was created under
       version 2 into a version 3 file named <outfile>.

    RETURNS:

       0 if okay
       CODAS error code otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_block_file(char *infile, char *outfile)
#else
int convert_block_file(infile, outfile)
char *infile, *outfile;
#endif
{
   int ierr;

   if ((fpin = open_binary_file(infile, READ_ONLY)) == NULL)
   {
      ierr = FILE_PROTECTION_ERROR;
      goto error_found;
   }
   if ((db->fpblkdata = fopen(outfile, "wb+")) == NULL)
   {
      ierr = FILE_PROTECTION_ERROR;
      goto error_found;
   }
   file_nbytes = 0;

   if ((ierr = convert_block_hdr()) != 0)
      goto error_found;
   if ((ierr = convert_data_list()) != 0)
      goto error_found;
   if (db->producer->struct_def_max_nentries)
   {
      if ((ierr = write_block_strdef()) != 0)
         goto error_found;
      file_nbytes += db->producer->struct_def_max_nentries * STRUCT_DEF_ENTRY_SIZE;
   }
   if ((ierr = convert_profile_dir()) != 0)
      goto error_found;
   if ((ierr = convert_block_var_data()) != 0)
      goto error_found;
   if ((ierr = convert_profiles()) != 0)
      goto error_found;

   /*--------------------rewrite profile directory----------------------*/
   if ((ierr = write_data(db->fpblkdata, db->profile_dir,
      db->block_hdr.dir_ofs, profile_dir_entry_size * db->block_hdr.dir_nentries)) != 0)
      goto error_found;
   /*---------------------rewrite data list-----------------------------*/
   if ((ierr = write_data_list()) != 0)
      goto error_found;
   /*--------------------update & rewrite block header------------------*/
   db->block_hdr.block_nbytes = file_nbytes;
   if ((ierr = write_block_hdr()) != 0)
      goto error_found;

   free(db->profile_dir);
   fclose(fpin);
   fclose(db->fpblkdata);
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_block_file\n", ierr);
      free(db->profile_dir);
      fclose(fpin);
      fclose(db->fpblkdata);
      return(ierr);
}

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

    FUNCTION:  convert_block_hdr

       Partially converts a CODAS version 2 block file header
       into a version 3 block file header and writes it to the output file.
       Certain field values (offsets, etc.) have to preserved temporarily
       in order to properly access the remainder of the block file.
       The new block header will be updated and rewritten later.

    RETURNS:

       0 if okay
       INCOMPATIBLE_HOST_ERROR, SEEK_ERROR, READ_ERROR or WRITE_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_block_hdr(void)
#else
int convert_block_hdr()
#endif
{
   int index, ierr;

   set_byte((UBYTE *) &(db->block_hdr), '\0', BLOCK_HDR_SIZE);
   if ((index = identify_ms((char *) MACHINE_SIGNATURE)) == -1)
   {
      ierr = INCOMPATIBLE_HOST_ERROR;
      goto error_found;
   }
   if ((ierr = read_data(fpin, (char *) &(db->block_hdr), 0L,
      OLD_BLOCK_HDR_SIZE)) != 0)
      goto error_found;
   strncpy(db->block_hdr.db_version, DATABASE_SYSTEM_VERSION, 4);
   strncpy(db->block_hdr.producer_host, MS_TABLE[index].ms_name, 4);
   db->block_hdr.data_list_ofs = BLOCK_HDR_SIZE;
   db->block_hdr.struct_def_nentries = db->producer->struct_def_max_nentries;
   strncpy(db->block_hdr.ms_code, MS_TABLE[index].ms_code, 4);
   if ((ierr = write_block_hdr()) != 0)
      goto error_found;

   file_nbytes += BLOCK_HDR_SIZE;
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_block_hdr\n", ierr);
      return(ierr);
}

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

    FUNCTION:  convert_data_list

       Partially converts the data list structure in a CODAS version 2
       block file into a version 3 data list structure and writes it to
       the output file.  Further updating and rewriting will be performed
       later.

    RETURNS:

       0 if okay
       INSUFFICIENT_MEMORY, SEEK_ERROR, READ_ERROR, WRITE_ERROR,
       or CONVERT_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_data_list(void)
#else
int convert_data_list()
#endif
{
   OLD_DATA_LIST_ENTRY_TYPE *old_dl;
   unsigned nbytes;
   int i, ierr;

   if (db->block_hdr.data_list_nentries > 0)
   {
      nbytes = db->block_hdr.data_list_nentries * OLD_DATA_LIST_ENTRY_SIZE;
      if ((old_dl = (OLD_DATA_LIST_ENTRY_TYPE *) malloc(nbytes)) == NULL)
      {
         ierr = INSUFFICIENT_MEMORY;
         goto error_found;
      }
      if ((ierr = read_data(fpin, (char *) old_dl, OLD_BLOCK_HDR_SIZE,
         nbytes)) != 0)
         goto error_found;
      for (i = 0; i < db->block_hdr.data_list_nentries; i++)
      {
         strncpy(db->data_list[i].name, old_dl[i].name, 20);
         strncpy(db->data_list[i].units, old_dl[i].units, 12);
         db->data_list[i].value_type  = old_dl[i].value_type;
         db->data_list[i].access_type = old_dl[i].access_type;
         db->data_list[i].access_0    = old_dl[i].access_0;
         db->data_list[i].access_1    = old_dl[i].access_1;
         db->data_list[i].offset      = old_dl[i].offset;
         db->data_list[i].scale       = old_dl[i].scale;
         db->data_list[i].unused      = 0L;
      }
      free(old_dl);
      if ((ierr = write_data_list()) != 0)
         goto error_found;
      file_nbytes += db->block_hdr.data_list_nentries * DATA_LIST_ENTRY_SIZE;
   }
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_data_list", ierr);
      return(ierr);
}

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

    FUNCTION:  convert_profile_dir

       Partially converts the profile directory in a CODAS version 2
       block file into a version 3 profile directory and writes it to
       the output file.  Further updating and rewriting will be performed
       later.

    RETURNS:

       0 if okay
       INSUFFICIENT_MEMORY, SEEK_ERROR, READ_ERROR, WRITE_ERROR,
          or INVALID_DIRECTORY_TYPE otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_profile_dir(void)
#else
int convert_profile_dir()
#endif
{
   char *buf;
   unsigned int nbytes = 0, old_ofs, new_ofs;
   int i, ierr;

   switch (db->block_hdr.dir_type)
   {
      case 0:  profile_dir_entry_size = PROFILE_DIR_0_ENTRY_SIZE;
               break;
      case 1:  profile_dir_entry_size = PROFILE_DIR_1_ENTRY_SIZE;
               break;
      case 2:  profile_dir_entry_size = PROFILE_DIR_2_ENTRY_SIZE;
               break;
      case 3:  profile_dir_entry_size = PROFILE_DIR_3_ENTRY_SIZE;
               break;
      default: ierr = INVALID_DIRECTORY_TYPE;
               goto error_found;
   }
   if (db->block_hdr.dir_nentries > 0)
   {
      nbytes = db->block_hdr.dir_nentries * db->block_hdr.dir_entry_nbytes;
      if ((buf = malloc(nbytes)) == NULL)
      {
         ierr = INSUFFICIENT_MEMORY;
         goto error_found;
      }
      if ((db->profile_dir = calloc(db->block_hdr.dir_nentries,
         profile_dir_entry_size)) == NULL)
      {
         ierr = INSUFFICIENT_MEMORY;
         goto error_found;
      }
      if ((ierr = read_data(fpin, buf, db->block_hdr.dir_ofs, nbytes)) != 0)
         goto error_found;
      for (i = 0; i < db->block_hdr.dir_nentries; i++)
      {
         new_ofs = i * profile_dir_entry_size;
         old_ofs = i * db->block_hdr.dir_entry_nbytes;
         ((PROFILE_DIR_0_ENTRY_TYPE *)(db->profile_dir + new_ofs))->ofs =
            ((OLD_PROFILE_DIR_0_ENTRY_TYPE *)(buf + old_ofs))->ofs;
         ((PROFILE_DIR_0_ENTRY_TYPE *)(db->profile_dir + new_ofs))->time =
            ((OLD_PROFILE_DIR_0_ENTRY_TYPE *)(buf + old_ofs))->time;
         if ((db->block_hdr.dir_type == 2) || (db->block_hdr.dir_type == 3))
         {
            ((PROFILE_DIR_2_ENTRY_TYPE *)(db->profile_dir + new_ofs))->depth1 =
               ((OLD_PROFILE_DIR_2_ENTRY_TYPE *)(buf + old_ofs))->depth1;
            ((PROFILE_DIR_2_ENTRY_TYPE *)(db->profile_dir + new_ofs))->depth2 =
               ((OLD_PROFILE_DIR_2_ENTRY_TYPE *)(buf + old_ofs))->depth2;
         }
         if ((db->block_hdr.dir_type == 1) || (db->block_hdr.dir_type == 3))
         {
            ((PROFILE_DIR_1_ENTRY_TYPE *)(db->profile_dir + new_ofs))->lon =
               ((OLD_PROFILE_DIR_1_ENTRY_TYPE *)(buf + old_ofs))->lon;
            ((PROFILE_DIR_1_ENTRY_TYPE *)(db->profile_dir + new_ofs))->lat =
               ((OLD_PROFILE_DIR_1_ENTRY_TYPE *)(buf + old_ofs))->lat;
         }
      }
      free(buf);
      nbytes = profile_dir_entry_size * db->block_hdr.dir_nentries;
      if ((ierr = write_data(db->fpblkdata, db->profile_dir, file_nbytes,
         nbytes)) != 0)
         goto error_found;
   }
   db->block_hdr.dir_ofs = file_nbytes;
   db->block_hdr.dir_entry_nbytes = profile_dir_entry_size;
   file_nbytes += nbytes;
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_profile_dir\n", ierr);
      return(ierr);
}

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

    FUNCTION:  convert_block_var_data

       Copies all the block-variable data in a CODAS version 2 block file
       to the output file.  Note that some rearrangement among the
       block variables may occur in case the block variables were not
       originally loaded in the order of the data list ID numbers.
       The data list is updated to reflect the new offsets in case this occurs.

    RETURNS:

       0 if okay
       INSUFFICIENT_MEMORY, SEEK_ERROR, READ_ERROR, WRITE_ERROR otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_block_var_data(void)
#else
int convert_block_var_data()
#endif
{
   char *data;
   unsigned int i, nbytes;
   int ierr;

   for (i = 0; i < db->block_hdr.data_list_nentries; i++)
      if (db->data_list[i].access_type == BLOCK_VAR_ACCESS_CODE)
      {
         nbytes = db->data_list[i].access_1;
         if (nbytes > 0)
         {
            if ((data = malloc(nbytes)) == NULL)
            {
               ierr = INSUFFICIENT_MEMORY;
               goto error_found;
            }
            if ((ierr = read_data(fpin, data, db->data_list[i].access_0, nbytes)) != 0)
               goto error_found;
            if ((ierr = write_data(db->fpblkdata, data, file_nbytes, nbytes)) != 0)
               goto error_found;
            db->data_list[i].access_0 = file_nbytes;
            file_nbytes += nbytes;
            free(data);
         }
      }
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_block_var_data\n", ierr);
      return(ierr);
}

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

    FUNCTION:  convert_profiles

       Converts the data directories and copies the profile-variable data
       in a CODAS version 2 block file to the version 3 output file.
       Note that some rearrangement among the profile variables may occur
       if the profile variables were not originally loaded in the order of
       their data list ID numbers.  The data directories are rewritten to
       update the offsets in case this occurs.

    RETURNS:

       0 if okay
       INSUFFICIENT_MEMORY, SEEK_ERROR, READ_ERROR, WRITE_ERROR, otherwise

*/
#if PROTOTYPE_ALLOWED
int convert_profiles(void)
#else
int convert_profiles()
#endif
{
   OLD_DATA_DIR_ENTRY_TYPE *old_dd;
   char *data;
   unsigned int i, j, old_dd_nbytes, new_dd_nbytes;
   int ierr;
   LONG old_dd_ofs, new_dd_ofs;

   if (db->block_hdr.data_dir_nentries)
   {
      old_dd_nbytes = db->block_hdr.data_dir_nentries * OLD_DATA_DIR_ENTRY_SIZE;
      for (i = 0; i < db->block_hdr.dir_nentries; i++)
      {
         if ((old_dd = (OLD_DATA_DIR_ENTRY_TYPE *) malloc(old_dd_nbytes)) == NULL)
         {
            ierr = INSUFFICIENT_MEMORY;
            goto error_found;
         }
         old_dd_ofs = *((LONG *) (db->profile_dir +
            i * db->block_hdr.dir_entry_nbytes));
         if ((ierr = read_data(fpin, (char *) old_dd, old_dd_ofs, old_dd_nbytes)) != 0)
            goto error_found;
         for (j = 0; j < db->block_hdr.data_dir_nentries; j++)
         {
            db->data_dir[j].ofs = old_dd[j].ofs;
            db->data_dir[j].nbytes = old_dd[j].nbytes;
         }
         free(old_dd);
         new_dd_nbytes = db->block_hdr.data_dir_nentries * DATA_DIR_ENTRY_SIZE;
         new_dd_ofs = file_nbytes;
         if ((ierr = write_data(db->fpblkdata, (char *) db->data_dir, new_dd_ofs,
            new_dd_nbytes)) != 0)
            goto error_found;
         file_nbytes += new_dd_nbytes;
         *((LONG *) (db->profile_dir + i * db->block_hdr.dir_entry_nbytes)) =
            new_dd_ofs;

         for (j = 0; j < db->block_hdr.data_dir_nentries; j++)
         {
            if (db->data_dir[j].nbytes > 0)
            {
               if ((data = malloc(db->data_dir[j].nbytes)) == NULL)
               {
                  ierr = INSUFFICIENT_MEMORY;
                  goto error_found;
               }
               if ((ierr = read_data(fpin, data, old_dd_ofs + db->data_dir[j].ofs,
                  db->data_dir[j].nbytes)) != 0)
                  goto error_found;
               if ((ierr = write_data(db->fpblkdata, data, file_nbytes,
                  db->data_dir[j].nbytes)) != 0)
                  goto error_found;
               db->data_dir[j].ofs = file_nbytes - new_dd_ofs;
               file_nbytes += db->data_dir[j].nbytes;
               free(data);
            }
         }

         /*-----------------rewrite data directory --------------------*/
         if ((ierr = write_data(db->fpblkdata, (char *) db->data_dir, new_dd_ofs,
            new_dd_nbytes)) != 0)
            goto error_found;
      }
   }
   return(0);

   error_found:
      printf("\n ERROR: %d in convert_block_var_data\n", ierr);
      return(ierr);
}

