#include <dbinc.h>   /* includes geninc.h */
#include <time.h>    /* time() and ctime() */
#include <ioserv.h>
#include <use_db.h>
#include <adcp.h>
#include <data_id.h>
#include "tran_nbp.h"
/** 1999/07/06 Pierre Jaccard
    The next file must be included for function declaration **/
#include "tran_dcl.h"

#define TEMPORAL 0  /* avg_method */

#if PROTOTYPE_ALLOWED
void load_data(FILE *fpcnt, FILE *fpscan, long next_ens_start_seconds);
#else
void load_data();
#endif

char msg[256];  /* for message strings in all modules */
static FILE_NAME_TYPE dbname, prdname, logname, filename, scanname;
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,
   "ADD_SECONDS:"               , TYPE_LONG   ,   op_get_param, (char *) &ADD_SECONDS,
   "YEAR_BASE:"                 , TYPE_INT   , op_get_param, (char *) &YearBase,
   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, *fpscan;
   long time_stamp, save_offset, next_ens_start_seconds;
   int db_id = 1, mem_mode = 0, error = 0;
   FILE_NAME_TYPE dirblkname;
   static NBP_ENSEMBLE_TYPE ens;
   static DB_ENSEMBLE_TYPE db_ens;
   YMDHMS_TIME_TYPE prof_time, prev_time;

   strcpy(dbname, "");
   strcpy(prdname, "");
   strcpy(logname, "");
   strcpy(scanname, "loadtnbp.scn");
   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));
   }
   fpscan = check_fopen(scanname, "wt");

   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");
   error = 0;
   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, ECHO) <= 0) return;          
      sprintf(msg, "\n   add_seconds = %ld\n", add_seconds);
      report_msg(msg);
      if (error_found( (fp_in = check_fopen(filename, "rb")) == NULL, "Can't open input file."))
         goto close_files;
      while (!error)
      {
         ens.leader_present = 0; /* flag needed by read_ensemble */
         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."))
               goto close_files;
            fprintf(fpscan, "%ld\n", ens.leader.ens_start_seconds);
            sprintf(msg, " %ld\n", ens.leader.ens_start_seconds);
            report_msg(msg);
         }

      }
      if (error == EOF)
      {
         report_msg("   EOF\n");
         fclose(fp_in);
         error = 0;
      }
      else goto close_files;
   }
   fclose(fpscan);

   /* goto close_files; */

   /* pass 2 -- load data into database */
   fpscan = check_fopen(scanname, "rt");
   fscanf(fpscan, "%ld", &next_ens_start_seconds); /* to position for next */
   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;
   db_ens.conf1.num_bins = 0; /* flag for read_ensemble: find
            and flag new configuration so depth will be set */
   load_data(fpcnt, fpscan, next_ens_start_seconds);
   DBCLOSE(&error);
   DBERROR(&error, "closing database");

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

#if PROTOTYPE_ALLOWED
void load_data(FILE *fpcnt, FILE *fpscan, long next_ens_start_seconds)
#else
void load_data(fpcnt)
   FILE *fpcnt;
   FILE *fpscan;
   long next_ens_start_seconds;
#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 NBP_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((UBYTE *)&ens, '\0', sizeof(NBP_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);
      if (execute_options(fpcnt, file_options, NOECHO) <= 0) 
         return;          
      if (error_found( (fp_in = check_fopen(filename, "rb")) == NULL, "Can't open input file."))
         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 (fscanf(fpscan, "%ld", &next_ens_start_seconds) != 1)
               next_ens_start_seconds = 0;
            if (error_found(get_prof_time(
                     &prof_time, &ens, next_ens_start_seconds),
                     "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 nbytes, 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 != 0.0)
   {
      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(0);
}

int add_profile_variables(db_ens, ens)
DB_ENSEMBLE_TYPE *db_ens;
NBP_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(0);
}

#define FEB28 5097600L /* no. of seconds from Jan 1 to Feb 28 = 59 days * 86400 */
#define is_leap(y) ( ((y) % 4 == 0) && ((y) % 100 != 0 || (y) % 400 == 0) ? 1 : 0 )

int get_prof_time(ptime, ens, next_ens_start_seconds)
YMDHMS_TIME_TYPE *ptime;
NBP_ENSEMBLE_TYPE *ens;
long next_ens_start_seconds;
{
   LONG ens_end_seconds;
   YMDHMS_TIME_TYPE t1;
   extern long add_seconds;

   if (error_found((ens->conf.avg_method != TEMPORAL),
      "\n SPATIAL averaging!  not supported...\n")) return(-1);

   t1.year = YearBase;
   t1.month = t1.day = 1;
   t1.hour = t1.minute = t1.second = 0;

   /* Based on present ensemble only, guess that the end of the ensemble
      is the start time plus the nominal averaging interval.  The actual
      averaging interval, unfortunately, can vary by up to half the "ping"
      duration, which is the averaging interval in the profiler as opposed
      to the PC.
   */
   ens_end_seconds = ens->leader.ens_start_seconds
                         + ens->conf.avg_interval/100;
   /* If the difference between this and the next start time is less than
      1/5 the nominal avg interval, then assume the next start time is
      a better estimate of the present end time.
   */
   sprintf(msg, " %ld %ld\n", ens_end_seconds, next_ens_start_seconds);
   report_msg(msg);
   if (abs(ens_end_seconds - next_ens_start_seconds)
                         < ens->conf.avg_interval/500)
      ens_end_seconds = next_ens_start_seconds;

   ens_end_seconds +=  (add_seconds + ADD_SECONDS);

   /* DIFTIM below works only with signed LONG, so check that ens_start_seconds is not negative;
      it should never be negative if it is the offset in seconds from Jan 1 of the year,
      Transect manual gives range as 0 to 2147483647 (MAXLONG) seconds */

   if (ens_end_seconds < 0L)
      return(INVALID_TIME);
   /*
      NB ADCP does NOT record the year and ALWAYS assumes 28
      days in February.  NB Transect also does NOT record the
      year in the processed data file, and does NOT come up with
      a different date in March, for example, if the PC clock is
      a leap year vs a non-leap year.  In fact, no Transect screen
      or data structure includes year info.  Therefore, contrary
      to what Chris Humphrey claimed in a message on March 5,
      1996, times in Transect files for leap years are inherently
      ambiguous after Feb. 28.  If the ADCP clock was set before
      then, times will be correct, because what gets recorded is
      the seconds since the beginning of the year (but see below).
      If the clock was set on or after March 1, then times will 
      be early by one day; the number of seconds will be missing
      February 29.


      Presumably, if someone noticed the incorrect time on Feb. 29
      and tried to reset it, he/she would have been frustrated! 

      Since loadtran can't know when the clock was set, there is
      no way to solve this problem automatically.  Time corrections,
      file by file, must be made manually using the add_seconds
      option in the control file.

   */
   /* Despite Chris Humphrey's assurance to the contrary, however, we
   find empirically that both versions of Transect actually start with
   86400 on Jan. 1, not 0, so we subtract a day here: */
/*   ens_end_seconds -= 86400L; */
/* Sat  99/02/20 EF on the Kaiyo--this does not seem to be
needed with this dataset. */
  

   DIFTIM(&t1, ptime, &ens_end_seconds);  /* ptime = t1 + ens_end_seconds */
   return(invalid_time(ptime));
}


