/*****************************************************************************
 loadtran_bbp.c
            Sept '95        based on loadtran.c (NB transect reader by UH)
            Dave Senciall
            NWAFC, DFO, Gov of Canada


  Reads broadband transect processed data files(s) and creates codas3
 dataset. Tested with transect version 2.72 files.

 NOTES & UNRESOLVED ISSUES:
    1) xmit_pulse_length variable of BB appears to represent the same thing
as the NB pulse length variable according to the docs. Values are not quit the
same though.

eg a bin size of 4 meters would likely have a pulse length of 4 meters under NB
  but xmit_pulse_len for BB is 5.97 meters.

 I have use the xmit_pulse_len for the pulse length field.

E.F.: I don't think this is an issue; it seems to be simply the way the BB
works.

    2) The BB has 2 blocks of direct commands, I have not tested that the
2nd block gets copied correctly since I have no data with enough commands
to cause entries in the 2nd block.

    3) All structures for reading the BB files have the BBP prefix, some of
these structures are indentical to the NBP structures but I have given them
unique names in case the two softwares evolve away from each other.

E.F.: I may change this.
I would like to bring the NB and BB versions as close together as
possible.


    4) Sources with the 'bb' extension contain BB specific references. Ones
with no 'bb' or 'nb' extension are independent of instrument type.

E.F.: File naming has changed once since DS made the conversion from
the NB version of loadtran, and will probably change one more time.
This will be in connection with making a consistent NB/BB release,
with as much in common as possible.

    5) This code has only been tested on a SUN4 system (Sun-OS 4.1.3).
-----------------
E.F.: Nil Oien ported it to the PC in February, 1996; this and all future
versions from UH will be PC-compatible. We will soon at least check
that it compiles OK under Solaris 2.4 and IRIX-5.  (Much of what Nils
did for the port was a work-around for Turbo-C's tiny stack.  I replaced
all that with changes that minimize stack use.  Sizeable local variables
such as arrays and strings are now declared as static rather than
auto.  This increases DATA segment usage but keeps the stack from
overflowing.


E.F.
Feb. 1996: bug fixes and port to PC by Nils Oien.
April 1996: Dave Senciall fixed the get_prof_time_BB function to handle
   leap years correctly.
   EF: corrected the pulse length, blank length, bin length
   parameters in the configuration; changed to floating point
   for the calculation of the depth array.  Now, to store depth
   with decimeter or centimeter precision, just change the "scale"
   parameter for depth in the data definition file.

   I made a few other miscellaneous bug fixes and cleanups, some
   in response to NB version bug notes by Alain Coat (coat@shom.fr).

   I have now changed get_prof_time_BB to use CODAS date/time routines,
   so that Dave's date function is no longer needed.
   Added rudimentary time correction facility, via the "add_seconds:"
   option in the file options list.  (This is presently the only
   option!)  If the ADCP clock was 10 s behind for a given file,
   then put "add_seconds: 10" between the file name and "end" in
   the control file.

   At some point, we should add a configuration_2 structure
   to store miscellaneous info from the Transect
   configuration file, or add it as text to the block
   comments.

Sat  98/06/13 EF
   Moved all function declarations to tran_dcl.h, which is
   now included by *.c.
   Various little cleanups to eliminate warnings from gcc.
   Now the TIME_CORRECTED bit (bit 0) is set in the data processing
   mask if the time has been corrected via "add_seconds:".
   Fixed bug in the calculation of a single BT depth from
   the individual beam depths provided by Transect; there is
   still a question as to how Transect handles depths larger
   than 655 meters, if at all.

Thurs 00/01/27 EF
   Y2K fix to get_prof_time_BB()

2000/03/17 EF
   Added the ADD_SECONDS: global option, to make a constant time
   offset to all profiles; this is added to any "add_seconds"
   per-file offset.
   Changed the time/date calculation to agree with the manual,
   with zero at the start of 1900 or 2000.  It had been a day
   earlier, which seemed to be needed for some datasets (some
   Transect versions?) but does not seem correct at present.
   If it does not work now for any dataset, use the ADD_SECONDS:
   option to compensate.

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


#include <dbinc.h>
#include <time.h>    /* time() and ctime() */
#include <ioserv.h>
#include <use_db.h>
#include <adcp.h>
#include <data_id.h>
#include "tran_bbp.h"
#include "tran_dcl.h"

#define TEMPORAL 0  /* avg_method */

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


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

char msg[256];  /* for message strings in all modules */

static FILE_NAME_TYPE dbname, prdname, logname, filename;
static char new_block_at_file[10];
static int max_prf_per_block = 400, max_time_gap = 30,
           start_new_block_at_file = 0, YearBase = -1;
static long add_seconds, ADD_SECONDS;


static OPTION_TYPE load_options[] =
{
   { "TRANSECT_FILES:"        , 0          , NULL        ,   (char *) NULL},
   { "DATABASE_NAME:"     , TYPE_STRING, op_get_param, dbname},
   { "DEFINITION_FILE:"       , TYPE_STRING, op_get_param, prdname},
   { "LOG_FILE:"          , TYPE_STRING, op_get_param, logname},
   { "MAX_PROFILES_PER_BLOCK:"    , TYPE_INT   , op_get_param, (char *) &max_prf_per_block},
   { "NEW_BLOCK_AT_FILE?"     , TYPE_STRING, op_get_param, new_block_at_file},
   { "NEW_BLOCK_TIME_GAP(min):"   , TYPE_INT   , op_get_param, (char *) &max_time_gap},
   { "YEAR_BASE:"                 , TYPE_INT   , op_get_param, (char *) &YearBase},
   {"ADD_SECONDS:"               , TYPE_LONG   ,   op_get_param, (char *) &ADD_SECONDS},
   { NULL             , 0      , NULL        , (char *) NULL}
};

static OPTION_TYPE file_options[] =
{
   { "end"            , 0          , NULL        , (char *) NULL        },
   { "add_seconds:"   , TYPE_LONG  , op_get_param, (char *) &add_seconds},
   { NULL             , 0          , NULL        , (char *) NULL        }
};

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

#if PROTOTYPE_ALLOWED
void do_it(FILE *fpcnt)
#else
void do_it(fpcnt)
   FILE *fpcnt;
#endif
{
   FILE *fplog = NULL, *fp_in;
   time_t time_stamp;
   long save_offset;
   int db_id = 1, mem_mode = 0, error = 0;
   static FILE_NAME_TYPE dirblkname;

   strcpy(dbname, "");
   strcpy(prdname, "");
   strcpy(logname, "");
   strcpy(new_block_at_file, "yes");
   ADD_SECONDS = 0;

   if (execute_options(fpcnt, load_options, NOECHO) <= 0) goto close_files;
   if (!strcmp(dbname, "") || !strcmp(prdname, "") || YearBase < 0)
   {
      fprintf(stderr,
         "\n ERROR: DATABASE_NAME:, DEFINITION_FILE:, and YEAR_BASE: must be specified in the control file\n\n");
      return;
   }

   if (!stricmp(new_block_at_file, "yes"))
      start_new_block_at_file = 1;
   else if (!stricmp(new_block_at_file, "no"))
      start_new_block_at_file = 0;
   else
   {
      fprintf(stderr,
         "\n ERROR: NEW_BLOCK_AT_FILE? value must be either 'yes' or 'no'\n\n");
      return;
   }

   if (strcmp(logname, ""))
   {
      fplog = check_fopen(logname, "a+t");
      set_msg_file(fplog);
      time(&time_stamp);
      fprintf(fplog,
         "\n**************************\n %s**************************\n", ctime(&time_stamp));
   }

   save_offset = ftell(fpcnt);  /* need to go back here for pass 2 */

   /* pass 1 -- checking control file & input files */
   report_msg("\n--------\n Pass 1: Checking control file...\n--------\n");
   while (fscanf(fpcnt, " %s", filename) == 1)
   {
      report_msg(msg);
      if (execute_options(fpcnt, file_options, ECHO) <= 0) return;
      if ((fp_in = check_fopen(filename, "rb")) == NULL)
         goto close_files;
      fclose(fp_in);
   }

   /* pass 2 -- load data into database */
   fseek(fpcnt, save_offset, SEEK_SET);
   sprintf(dirblkname, "%sdir.blk", dbname);
   if (EXISTS(dirblkname)){
      sprintf(msg, "\n--------\n Pass 2: Database %s exists, appending...\n--------\n", dbname);
      report_msg(msg);
   }else{
      sprintf(msg, "\n--------\n Pass 2: Creating new database %s...\n--------\n", dbname);
      report_msg(msg);
    }
   DBCREATE(&db_id, dbname, prdname, &mem_mode, &error);
   if (DBERROR(&error, "creating database")) goto close_files;
   load_data(fpcnt);
   DBCLOSE(&error);
   DBERROR(&error, "closing database");

   close_files:
      if (fplog) fclose(fplog);
      return;
}

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

#if PROTOTYPE_ALLOWED
void load_data(FILE *fpcnt)
#else
void load_data(fpcnt)
   FILE *fpcnt;
#endif
{
   FILE *fp_in;
   static FILE_NAME_TYPE filename;
   int error = OK, file_is_new, block_is_open = 0, nprf_in_block = 0;
   YMDHMS_TIME_TYPE prof_time, prev_time;
   static BBP_ENSEMBLE_TYPE ens;
   static DB_ENSEMBLE_TYPE db_ens;

   /* some basic initializations: */
   init_ancil1(&(db_ens.anc1));
   init_ancil2(&(db_ens.anc2));
   init_access_var(&(db_ens.acc));
   set_byte(db_ens.pf, '\0', MAX_BINS);
   set_byte((char *)&ens, '\0', sizeof(BBP_ENSEMBLE_TYPE));
   db_ens.bc = NULL;
   prev_time.year = 32767;
   max_time_gap *= 60;  /* from minutes to seconds */

   /* Loop through the list of transect files. */
   while (fscanf(fpcnt, " %s", filename) == 1)
   {
      report_msg(filename);
      add_seconds = 0; /* initialize here, so it must be
                          specified separately for each file
                          */
      if (execute_options(fpcnt, file_options, NOECHO) <= 0)
         return;
      sprintf(msg, "\n   add_seconds = %ld\n", add_seconds);
      report_msg(msg);
      if ((fp_in = check_fopen(filename, "rb")) == NULL)
         return;
      file_is_new = 1;
      while (!error)
      {
         error = read_ensemble(fp_in, &ens, &db_ens);
         if (!error)
         {
            if (error_found(ens.conf.n_bins == 0, "Can't find configuration in first file."))
               return;
            if (error_found(get_prof_time_BB(&prof_time, &ens), "Reading ensemble time."))
               return;
            if (block_is_open && nprf_in_block > 0 &&
                   ( (start_new_block_at_file && file_is_new) ||
                     (nprf_in_block >= max_prf_per_block)     ||
                     ( (prev_time.year != BADSHORT) &&
                         (TIMDIF(&prev_time, &prof_time) > max_time_gap)) ) )
            {
               DBENDBLK(&error);
               if (DBERROR(&error, "DBENDBLK"))
                  return;
               block_is_open = 0;
            }
            file_is_new = 0;
            if (!block_is_open)
            {
               DBNEWBLK(&error);
               if (DBERROR(&error, "DBNEWBLK"))
                  return;
               block_is_open = 1;
               nprf_in_block = 0;
               if (error_found(add_block_variables(&db_ens), "Adding block variables."))
                  return;
            }
            DBNEWPRF(&prof_time, &error);
            if (DBERROR(&error, "DBNEWPRF"))
               return;
            if (error_found(add_profile_variables(&db_ens, &ens), "Adding profile variables."))
               return;
            move_byte((char *)(&prof_time),
                      (char *)(&prev_time),
                       sizeof(YMDHMS_TIME_TYPE));
            DBENDPRF(&error);
            if (DBERROR(&error, "DBENDPRF"))
               return;
            nprf_in_block++;
            ens.leader_present = ens.vel_present = ens.amp_present =
               ens.pg_present = ens.discharge_present = ens.nav_present = 0;
         } /* if !error on read_ensemble */
      } /* end of loop thru reading ensembles in one file */
      if (error == EOF)
      {
         report_msg("   EOF\n");
         fclose(fp_in);
         error = 0;
      }
      else
         return;
   } /* end of loop through files */
   if (block_is_open)
   {
      DBENDBLK(&error);
      if (DBERROR(&error, "DBENDBLK"))
         return;
   }
   return;
}

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

#if PROTOTYPE_ALLOWED
int add_block_variables(DB_ENSEMBLE_TYPE *db_ens)
#else
int add_block_variables(db_ens)
DB_ENSEMBLE_TYPE *db_ens;
#endif
{
   int error = OK;
   int  type;
   unsigned int  nbad, nbins;
   LONG data;

   error = dbadd_cnf(CONFIGURATION_1, (char *) &(db_ens->conf1),
      sizeof(CONFIGURATION_1_TYPE), "adding CONFIGURATION_1");
   if (error) return(error);

   if (add_seconds + ADD_SECONDS != 0L)
   {
      data = 1;
      error = dbadd_cnf(DATA_PROC_MASK, (char *) &data, sizeof(LONG),
               "adding DATA_PROC_MASK");
      if (error) return(error);
   }

   type = DEPTH;
   nbins = db_ens->conf1.num_bins;
   DBADD_F(&type, db_ens->depth, &nbins, &nbad, &error);
   error_found(error, "adding DEPTH");
   if (error) return(error);

   /* db_ens->bc should contain a null-terminated string plus
   a few (4, if I counted right) extra bytes that are indeterminate
   and that we don't want to store.  Therefore use strlen to
   find the part we want.
   */
   error = dbadd_cnf(BLOCK_COMMENTS, (char *) db_ens->bc,
      strlen(db_ens->bc), /*db_ens->bc_nbytes,*/ "adding BLOCK_COMMENTS");
   if (error) return(error);
   return(OK);
}

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

int add_profile_variables(db_ens, ens)
DB_ENSEMBLE_TYPE *db_ens;
BBP_ENSEMBLE_TYPE *ens;
{
   int error = OK;

   /* various structures, always added */
   fill_ancil1(&(db_ens->anc1), ens);
   error = dbadd_cnf(ANCILLARY_1, (char *) &(db_ens->anc1),
      sizeof(ANCILLARY_1_TYPE), "adding ANCILLARY_1");
   if (error) return(error);

   fill_ancil2(&(db_ens->anc2), ens);
   error = dbadd_cnf(ANCILLARY_2, (char *) &(db_ens->anc2),
      sizeof(ANCILLARY_2_TYPE), "adding ANCILLARY_2");
   if (error) return(error);

   fill_bt(&(db_ens->bt), &(ens->leader));
   error = dbadd_cnf(BOTTOM_TRACK, (char *) &(db_ens->bt),
      sizeof(BOTTOM_TRACK_TYPE), "adding BOTTOM_TRACK");
   if (error) return(error);

   fill_nav(&(db_ens->nav), ens);
   error = dbadd_cnf(NAVIGATION, (char *) &(db_ens->nav),
      sizeof(NAVIGATION_TYPE), "adding NAVIGATION");
   if (error) return(error);

   db_ens->acc.last_good_bin = db_ens->conf1.num_bins;
   error = dbadd_cnf(ACCESS_VARIABLES, (char *) &(db_ens->acc),
      sizeof(ACCESS_VARIABLES_TYPE), "adding ACCESS_VARIABLES");
   if (error) return(error);

   error = dbadd_cnf(DEPTH_RANGE, (char *) &(db_ens->depth_range),
      sizeof(DEPTH_RANGE_TYPE), "adding DEPTH RANGE");
   if (error) return(error);

   /* various data arrays */
   if (ens->vel_present)
   {
      error = dbadd_cnf(U, (char *) ens->u,
         db_ens->conf1.num_bins * sizeof(SHORT), "adding U");
      if (error) return(error);
      error = dbadd_cnf(V, (char *) ens->v,
         db_ens->conf1.num_bins * sizeof(SHORT), "adding V");
      if (error) return(error);
      error = dbadd_cnf(W, (char *) ens->w,
         db_ens->conf1.num_bins * sizeof(SHORT), "adding W");
      if (error) return(error);
      error = dbadd_cnf(ERROR_VEL, (char *) ens->e,
         db_ens->conf1.num_bins * sizeof(SHORT), "adding ERROR_VEL");
      if (error) return(error);
   }

   if (ens->amp_present)
   {
      error = dbadd_cnf(RAW_AMP, (char *) ens->raw_amp,
         4 * db_ens->conf1.num_bins * sizeof(UBYTE), "adding RAW_AMP");
      if (error) return(error);
      error = dbadd_cnf(AMP_SOUND_SCAT, (char *) ens->mean_amp,
         db_ens->conf1.num_bins * sizeof(UBYTE), "adding AMP_SOUND_SCAT");
      if (error) return(error);
   }

   if (ens->pg_present)
   {
      error = dbadd_cnf(PERCENT_GOOD, (char *) ens->pg,
         db_ens->conf1.num_bins * sizeof(UBYTE), "adding PERCENT_GOOD");
      if (error) return(error);
   }

   /* ignore discharge
   if (ens->discharge_present)
   {
      error = dbadd_cnf(DISCHARGE, (char *) ens->discharge,
         db_ens->conf1.num_bins * sizeof(LONG), "adding DISCHARGE");
      if (error) return(error);
   }
   */

   /* various placeholders */
   error = dbadd_cnf(PROFILE_FLAGS, (char *) db_ens->pf,
      db_ens->conf1.num_bins * sizeof(UBYTE), "adding PROFILE_FLAGS");
   if (error) return(error);
   return(OK);
}

int get_prof_time_BB(ptime, ens)
YMDHMS_TIME_TYPE *ptime;
BBP_ENSEMBLE_TYPE *ens;
{
   LONG ens_end_seconds;
   YMDHMS_TIME_TYPE basetime = {1900,1,1,0,0,0};  /* prior to y2k */
   YMDHMS_TIME_TYPE day_time;
   ULONG base_minutes;   /* TIMMIN wants ULONG */
   LONG day_minutes;
   extern long add_seconds, ADD_SECONDS;

   if (ens->conf.avg_method == TEMPORAL)
      ens_end_seconds = (ens->leader.ens_start_seconds
                        + ens->conf.avg_interval)/100
                        + add_seconds + ADD_SECONDS;
   else
   {
      printf("\n SPATIAL averaging!  not yet coded...\n");
      exit(-1);
   }

   /* 29200 days in 80 years; if less than this, assume
      year >= 2000, so base year is 2000, not 1900.
      This matches the Transect documentation.
      Note: for some datasets this may be off by a day anyway.
   */
   if (ens->leader.ens_start_day < 29200) basetime.year = 2000;
   base_minutes = TIMMIN(&basetime);
   day_minutes = base_minutes + ens->leader.ens_start_day * 1440;
   MINTIM(&day_time, &day_minutes);  /* sets day_time from day_minutes */

   DIFTIM(&day_time, ptime, &ens_end_seconds);

   return(invalid_time(ptime));
}

