/***************************************************************************
   FILE: readtran_bbp.c

         Routines for reading Transect BroadBand Processed data files.

    Based on readtnbp.c by UH for reading NB processed transect files.

    Modified by : Dave Senciall    Sept '95
                  NWAFC,DFO,Gov of Canada

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

#include <dbinc.h>
#include <ioserv.h>
#include <mc0.h>
#include "tran_bbp.h"
#include "tran_dcl.h"

#define TRUE 1
#define ID_NOT_FOUND 99

extern char msg[]; /* in loadtbbp.c */

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

#if PROTOTYPE_ALLOWED
int read_header(FILE *fp, BBP_HEADER_TYPE *header, long *ens_start)
#else
int read_header(fp, header, ens_start)
   FILE *fp;
   BBP_HEADER_TYPE *header;
   long *ens_start;
#endif
{
   int c, d;
   static char buff[BBP_HEADER_FIXED_SIZE], *buf_ptr;

   /* use while loop to search for next good header so one can resynch/
      recover data after encountering an error in the input file */
   while ( (c = getc(fp)) != EOF &&
            (c != 0x7f  ||
               ( (d = getc(fp)) != EOF && d != 0x7e ) ))
   {
      if (c != EOF && d == 0x7f)
         ungetc(d, fp);
   }
   if (c == EOF || d == EOF)
      return(EOF);
   *ens_start = ftell(fp) - 2;

   if (fread(buff, BBP_HEADER_FIXED_SIZE, 1, fp) != 1)
      return(EOF);

   buf_ptr = (char *) &buff[0];
   buf_ptr += convert_array_short((char *)(&header->n_bytes_in_data_segment),
                                  (char *) buf_ptr,
                                  PC_COMPATIBLE_HOST,
                                  HOST_ENVIRONMENT,
                                  1);

   buf_ptr += copy_byte((char *)&header->n_depth_cells, buf_ptr, 2, AS_IS, NULL);

   if ( (header->offset = (USHORT *) calloc(header->n_data_types, sizeof(USHORT))) == NULL )
      return(INSUFFICIENT_MEMORY);
   if ( error_found( fread(header->offset, sizeof(USHORT), header->n_data_types, fp) != header->n_data_types,
      "Can't read header offsets."))
      return(READ_ERROR);

   convert_array_short((char *) header->offset, (char *) header->offset, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, header->n_data_types);

   /* Transect bug: for the configuration data type only, the
   offset is written as MSB LSB, and the LSB is too small by 1.
   Nils Oien corrected the unswapping for any platform.
   */
   if (header->n_data_types == 1)
   {
      copy_byte((char *) header->offset, (char *) header->offset, 2, INVERTED, NULL);
      (header->offset[0])++;
   }

   return(OK);
}


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

#if PROTOTYPE_ALLOWED
int read_configuration(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_configuration(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   BBP_CONFIGURATION_TYPE *conf;
   static char buff[BBP_CONFIGURATION_FIXED_SIZE], *buf_ptr;

   conf = &ens->conf;
   if (error_found( fread(buff, BBP_CONFIGURATION_FIXED_SIZE, 1, fp) != 1, "Can't read configuration."))
      return(READ_ERROR);

   buf_ptr = buff;

   buf_ptr += copy_byte((char *)&conf->adcp_type, buf_ptr, 3, AS_IS, NULL);

   sprintf (msg, "adcp type,ver,rev = %u %u %u\n",
   conf->adcp_type,conf->transect_version,conf->transect_revision);
   report_msg(msg);

   buf_ptr += convert_array_short((char *) &conf->ADCP_firmware_number, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);


   buf_ptr += copy_byte((char *)&conf->n_beams, buf_ptr, 1, AS_IS, NULL);
   buf_ptr += convert_array_short((char *) &conf->beam_angle, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->frequency, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += copy_byte((char *)&conf->profile_mode, buf_ptr, 1, AS_IS, NULL);
   buf_ptr += convert_array_short((char *) &conf->coord_system, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 3);
   buf_ptr += copy_byte((char *)&conf->n_bins, buf_ptr, 1, AS_IS, NULL);
   buf_ptr += convert_array_long((char *) &conf->ping_interval, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->pings_per_ens, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->bin_length, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->blank, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_long((char *) &conf->adcp_depth, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += copy_byte((char *)&conf->avg_method, buf_ptr, 1, AS_IS, NULL);
   buf_ptr += convert_array_long((char *) &conf->avg_interval, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 4);
   buf_ptr += convert_array_short((char *) &conf->intensity_scale, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 3);
   buf_ptr += convert_array_long((char *) &conf->sound_speed, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += copy_byte((char *)&conf->use_default_sound_speed, buf_ptr, 2, AS_IS, NULL);
   buf_ptr += convert_array_long((char *) &conf->start_discharge, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 4);
   buf_ptr += copy_byte((char *)&conf->top_q_method, buf_ptr, 2, AS_IS, NULL);
   buf_ptr += convert_array_short((char *) &conf->power_curve_exp, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->bin_1_dist, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_short((char *) &conf->xmit_pulse_len, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += convert_array_long((char *) &conf->spare2, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   buf_ptr += copy_byte((char *)&conf->dply_name_size, buf_ptr, 1, AS_IS, NULL);


   if ( (conf->name = calloc(conf->dply_name_size + 1, 1)) == NULL )
      return(INSUFFICIENT_MEMORY);
   if (error_found( fread(conf->name, conf->dply_name_size, 1, fp) != 1, "Can't read configuration deployment name."))
      return(READ_ERROR);


   if (error_found( fread(&conf->primary_drive, sizeof(BYTE), 2, fp) != 2, "Can't read configuration drives, etc."))
      return(READ_ERROR);

   /* Direct commands, possibly in two blocks.  Use calloc to
   allocate a block 1 byte larger than the expected number of
   characters and initialize it to zeros.  Then using fread to
   get the expected number of characters will result in a null-
   terminated string, which can be manipulated with string
   handling functions in set_block_comments.
   */
   if (error_found( fread(&conf->direct_cmd_size, sizeof(BYTE), 1, fp) != 1, "Can't read configuration direct command 1 size"))
      return(READ_ERROR);

   if ( (conf->direct_cmd = calloc(conf->direct_cmd_size + 1, 1)) == NULL )
      return(INSUFFICIENT_MEMORY);

   if (error_found( fread(conf->direct_cmd, conf->direct_cmd_size, 1, fp) != 1, "Can't read configuration direct command 1 set."))
      return(READ_ERROR);

   if (error_found( fread(&conf->direct_cmd2_size, sizeof(BYTE), 1, fp) != 1, "Can't read configuration direct command 2 size"))
      return(READ_ERROR);

   if (conf->direct_cmd2_size > 0)
   {

      if ( (conf->direct_cmd2 = calloc(conf->direct_cmd2_size + 1, 1)) == NULL )
         return(INSUFFICIENT_MEMORY);

      if (error_found( fread(conf->direct_cmd2, conf->direct_cmd2_size, 1, fp) != 1, "Can't read configuration direct command 2 set."))
         return(READ_ERROR);
   }

 return (OK);
}

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

#if PROTOTYPE_ALLOWED
int read_leader(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_leader(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{

   BBP_LEADER_TYPE *leader;
   static char buff[BBP_LEADER_SIZE], *buf_ptr;

   leader = &ens->leader;
   if (error_found( fread(buff, BBP_LEADER_SIZE, 1, fp) != 1, "Can't read leader."))
      return(READ_ERROR);

   buf_ptr = buff;

   buf_ptr += convert_array_long((char *) &leader->ens_start_day, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 2);
   buf_ptr += convert_array_short((char *) &leader->ens_number, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 17);
   buf_ptr += convert_array_long((char *) &leader->x_displacement, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 7);
   buf_ptr += convert_array_short((char *) &leader->bit_result, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
   /* ?? leader seems to have an extra byte of padding! */
   /* buf_ptr++; */
   /* EF 98/01/19: The extra byte seems no longer to be present, so
      I commented out the line above.
   */
   buf_ptr += convert_array_short((char *) &leader->n_raw_ens, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);

   ens->leader_present = 1;
   return (OK);
}

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

#if PROTOTYPE_ALLOWED
int read_velocity(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_velocity(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   SHORT *u, *v, *w, *e;
   int n_vel, ibuff, i;
   static USHORT buff[4 * MAX_BINS];

   u = ens->u;
   v = ens->v;
   w = ens->w;
   e = ens->e;

   n_vel = 4 * ens->header.n_depth_cells;
   if (error_found( fread(buff, sizeof(USHORT), n_vel, fp) != n_vel, "Can't read velocity."))
      return(READ_ERROR);
   convert_array_short((char *)buff,
                       (char *)buff,
                       PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, n_vel);
   for (i = 0, ibuff = 0; i < ens->header.n_depth_cells; i++, ibuff += 4)
   {
      u[i] = (buff[ibuff    ] == 0x8000 ? BADSHORT : (SHORT) buff[ibuff    ]);
      v[i] = (buff[ibuff + 1] == 0x8000 ? BADSHORT : (SHORT) buff[ibuff + 1]);
      w[i] = (buff[ibuff + 2] == 0x8000 ? BADSHORT : (SHORT) buff[ibuff + 2]);
      e[i] = (buff[ibuff + 3] == 0x8000 ? BADSHORT : (SHORT) buff[ibuff + 3]);
   }

   ens->vel_present = TRUE;
   return (OK);
}

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

#if PROTOTYPE_ALLOWED
int read_amp(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_amp(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   int n_amp, i, j;

   n_amp = 4 * ens->header.n_depth_cells;
   if (error_found( fread(ens->raw_amp, sizeof(UBYTE), n_amp, fp) != n_amp,
      "Can't read echo intensity."))
      return(READ_ERROR);

   for (i = 0; i < ens->header.n_depth_cells; i++)
   {
      j = i * 4;
      ens->mean_amp[i] = (UBYTE)
         round((ens->raw_amp[j] + ens->raw_amp[j+1] + ens->raw_amp[j+2] + ens->raw_amp[j+3]) / 4);
   }

   ens->amp_present = TRUE;
   return (OK);
}

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

#if PROTOTYPE_ALLOWED
int read_pgood(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_pgood(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   if (error_found( fread(ens->pg, sizeof(BYTE), ens->header.n_depth_cells, fp) != ens->header.n_depth_cells,
      "Can't read percent-good."))
      return(READ_ERROR);

   ens->pg_present = TRUE;
   return (OK);
}

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

#if PROTOTYPE_ALLOWED
int read_discharge(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_discharge(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   LONG *discharge;

   discharge = ens->discharge;

   if (error_found(fread(discharge, sizeof(LONG), ens->header.n_depth_cells, fp) != ens->header.n_depth_cells, "Can't read discharge."))
      return(READ_ERROR);
   convert_array_long((char *)discharge,
                      (char *) discharge,
                      PC_COMPATIBLE_HOST, HOST_ENVIRONMENT,
                      ens->header.n_depth_cells);

   ens->discharge_present = TRUE;
   return(OK);
}

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

#if PROTOTYPE_ALLOWED
int read_navigation(FILE *fp, BBP_ENSEMBLE_TYPE *ens)
#else
int read_navigation(fp, ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
#endif
{
   char buff[TRANSECT_NAV_SIZE], *buf_ptr;
   TRANSECT_NAV_TYPE *nav;

   nav = &ens->nav;
   if (error_found( fread(buff, TRANSECT_NAV_SIZE, 1, fp) != 1, "Can't read navigation."))
      return(READ_ERROR);

   buf_ptr = buff;
   buf_ptr += convert_array_short((char *) &nav->u, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 2);
   buf_ptr += convert_array_long ((char *) &nav->x, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 5);
   buf_ptr += convert_array_short((char *) &nav->filter_width, buf_ptr, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);

   ens->nav_present = TRUE;
   return (OK);
}

typedef struct
{
   int id;
   int data_present;
   int (*read_data) ();
}  DATA_LIST_ELEMENT_TYPE;

DATA_LIST_ELEMENT_TYPE dt_list[] =
{
   {LEADER_ID, 0, read_leader},
   {VELOCITY_ID, 0, read_velocity},
   {AMPLITUDE_ID, 0, read_amp},
   {PGOOD_ID, 0, read_pgood},
   {DISCHARGE_ID, 0, read_discharge},
   {NAVIGATION_ID, 0, read_navigation},
   {CONFIGURATION_ID, 0, read_configuration}
};

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

/*

   FUNCTION:  read_ensemble

              Reads an entire BBP_ENSEMBLE_TYPE from the input file
              and copies it into a DB_ENSEMBLE_TYPE.

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

#if PROTOTYPE_ALLOWED
int read_ensemble(FILE *fp, BBP_ENSEMBLE_TYPE *ens, DB_ENSEMBLE_TYPE *db_ens)
#else
int read_ensemble(fp, ens, db_ens)
   FILE *fp;
   BBP_ENSEMBLE_TYPE *ens;
   DB_ENSEMBLE_TYPE *db_ens;
#endif
{
   int found, i, j, error = OK, num_items = sizeof(dt_list)/sizeof(DATA_LIST_ELEMENT_TYPE);
   USHORT id, checksum;
   long ens_start, save_ofs;
   static CONFIGURATION_1_TYPE prev_conf;
   static char *prev_bc;
   static int prev_bc_nbytes = 0;

   error = read_header(fp, &ens->header, &ens_start);
   if (error != OK)
      goto on_error;
   for (i = 0; i < ens->header.n_data_types; i++)
   {
      save_ofs = ftell(fp);
      if (fseek(fp, ens_start + ens->header.offset[i] - 1 , 0))
      {
         error = SEEK_ERROR;
         goto on_error;
      }
      if (fread((char *) &id, sizeof(USHORT), 1, fp) != 1)
      {
         error = READ_ERROR;
         goto on_error;
      }
      convert_array_short((char *)&id, (char *) &id,
                           PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);
      found = 0;
      for (j = 0; j < num_items && !found; j++)
         found = (id == dt_list[j].id);
      if (found)
      {
         error = (*dt_list[j-1].read_data)(fp, ens);
         if (error != OK)
            goto on_error;
         dt_list[j-1].data_present = TRUE;
      }
      else
      {
         error = ID_NOT_FOUND;
         goto on_error;
      }
   }
   free(ens->header.offset);

   if (fread(&checksum, sizeof(USHORT), 1, fp) != 1)
   {
      error = READ_ERROR;
      goto on_error;
   }
   /* skipping checksum verification for now...
      there are some inconsistencies between files and documentation,
      in particular:
      1) an extra byte in the leader structure, probably between the
         bit result and n_raw_ensembles fields
      2) the configuration offset reads 0008 when it should be 0900
      I do not know how these affect the checksum.
      Still waiting for a callback from RDI on this.  95/02/15 */

   convert_array_short((char *)&checksum, (char *) &checksum, PC_COMPATIBLE_HOST, HOST_ENVIRONMENT, 1);

   if (ens->leader_present == 0) /* must have read just a configuration-type segment */
   {
      fill_config1(&(db_ens->conf1), &(ens->conf));
      db_ens->blkvar_has_changed = cmp_byte((char *)&(db_ens->conf1),
                                             (char *)&prev_conf,
                                              sizeof(CONFIGURATION_1_TYPE));
      move_byte((char *)&(db_ens->conf1), (char *)&prev_conf, sizeof(CONFIGURATION_1_TYPE));

      if (db_ens->blkvar_has_changed)
         set_depth(db_ens, ens);

      error = set_block_comments(db_ens, ens); /* deploy. name & direct commands */
      if (error) goto on_error;
      db_ens->blkvar_has_changed |= (prev_bc_nbytes != db_ens->bc_nbytes);
      if (!db_ens->blkvar_has_changed) /* compare only if conf hasn't changed and bc size is same */
         db_ens->blkvar_has_changed |= cmp_byte(db_ens->bc, prev_bc, prev_bc_nbytes);
      if (db_ens->bc_nbytes > 0 && prev_bc_nbytes != db_ens->bc_nbytes)
      {
         if (error_found( prev_bc == NULL ? (prev_bc = malloc(db_ens->bc_nbytes)) == NULL :
                                            (prev_bc = realloc(prev_bc, db_ens->bc_nbytes)) == NULL,
            "Can't allocate memory for block comments."))
         {
            error = INSUFFICIENT_MEMORY;
            goto on_error;
         }
      }
      move_byte(db_ens->bc, prev_bc, db_ens->bc_nbytes);
      prev_bc_nbytes = db_ens->bc_nbytes;

      if (ens->conf.dply_name_size > 0)
         free(ens->conf.name);
      if (ens->conf.direct_cmd_size > 0)
         free(ens->conf.direct_cmd);
      return(read_ensemble(fp, ens, db_ens));  /* now try to read a regular data-type segment */
   }
   else
   {
      sprintf(msg, "%6hu", ens->leader.ens_number);
      report_msg(msg);
      if (ens->leader.ens_number % 10 == 0)
         report_msg("\n");
      return(OK);
   }
   on_error:  /* try to recover from read-related errors, otherwise quit */
      if (error == EOF)
         return(EOF);
      report_msg(" READ BAD ");
      switch(error)
      {
         case SEEK_ERROR:
            printf ("SEEK_ERROR\n");
            if (fseek(fp, save_ofs, SEEK_SET)) break;
         case READ_ERROR:
            printf ("READ_ERROR\n");
            break;
         case ID_NOT_FOUND:
            printf ("ID_NOT_FOUND_ERROR\n");
            return(read_ensemble(fp, ens, db_ens));
         default:
            break;
      }
      report_msg("\n Unrecoverable error in read_ensemble, giving up on this file.\n");
      return(EOF);
}
/***************************************************************************/

