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

   FILE: LOAD_ODF.C
   USAGE: LOAD_ODF <control file name>

   This program loads CTD data from ODF ASCII files into
   a CODAS database.  Unlike load_ctd where the header information
   are prepended to the data files, this program reads the header
   information from a single table file, one line per cast,
   and then reads the CTD data from separate data files, one
   per cast, each named after the station number.
   Below is a sample of the header information read in:

 131    1  150893  ROS   3   0.0  N  178  59.9  E  0218 5481
 132    1  150893  ROS   2  45.1  N  179   0.0  E  0719 5482
 133    1  150893  ROS   2  30.0  N  179   0.0  E  1239 4840

   The <control file> must contain the following information:

      DATABASE NAME - the name to use for the CODAS database to be created
      PRODUCER DEFINITION FILE NAME
      SHIP NAME - maximum of 11 characters or "none" to use default NODC code
      START_PRESSURE - smallest possible pressure value (usually 0 or 1)
      MAX_PRESSURE - largest pressure value to store in database
      PRESSURE_INCREMENT - the pressure interval in decibars
      LIST_FILE - table of header information, one line per cast
      DATA_FILE_ROOT - path/root specification for data files, one per cast

   All the data are loaded into one block file unless the
   OPTIMUM_PROFILES_PER_BLOCK figure has been exceeded, in which case a
   new block file is started.

   EF 99/04/15 quick change from 3 to 4 digits in the station
   number part of the ctd data file names; the right way to do
   it would be to switch from parameter to option input, and 
   then make it an option.

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

#include "geninc.h"
#include "data_id.h"
#include "ioserv.h"    /* error_found() */
#include "use_db.h"    /* db*_cnf() */

#define  NMAX    3000          /* maximum array size -- adjust if needed     */

int  MAX_PRESSURE; /* largest pressure value to use in loading pressure var. */
int  START_PRESSURE;    /* smallest possible pressure value (usually 0 or 1) */
int  PRESSURE_INCREMENT; /* set in control file to pressure interval used    */
int  N_PRESSURE;  /* no. of pressure values to store in database */
SHORT pressure[NMAX];
FLOAT data[NMAX];
FILE_NAME_TYPE dbname, prdname, listname, ascroot, ascfn, ascname;
char SHIP_NAME[12];

typedef struct
{
   CHAR   ship_name[12];
   CHAR   cruise_id[12];
   CHAR   unused[24];
} CONFIGURATION_1_TYPE;

typedef struct
{
   USHORT station;
   USHORT cast_num;
   USHORT end_time;             /* station end-time (hour & minute only) */
   SHORT  inst;
   FLOAT  max_pressure;
   FLOAT  aver;
   FLOAT  rate;
   FLOAT  ocean_depth;
   CHAR   unused[20];
} ANCILLARY_1_TYPE;

#if PROTOTYPE_ALLOWED
int get_header(FILE *fpasc, CONFIGURATION_1_TYPE *conf,
   ANCILLARY_1_TYPE *anc, YMDHMS_TIME_TYPE *prftime, DMSH_POSITION_TYPE
   position[]);
int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[]);
int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[]);
int convert_time(long ztime, long dmy, YMDHMS_TIME_TYPE *time);
int dbaddf_cnf(int type, FLOAT data[], unsigned int n, char *msg);
#else
int get_header();
int get_temperature();
int get_salinity();
int get_oxygen();
int convert_time();
int dbaddf_cnf();
#endif

#if PROTOTYPE_ALLOWED
void do_it(FILE *fpcnt)
#else
void do_it(fpcnt)
FILE *fpcnt;
#endif
{
   int                  dbid = 1, memory_mode = 0, ierr, iprf, n, i, status;
   long                 filepos;
   FILE                 *fpasc, *fplist;
   CONFIGURATION_1_TYPE conf;
   ANCILLARY_1_TYPE     anc;
   DMSH_POSITION_TYPE   position[2];
   YMDHMS_TIME_TYPE     prftime;

   static PARAMETER_LIST_ENTRY_TYPE ctdcnt[] =
   {
      " DATABASE_NAME:"     , " %79s", dbname             , TYPE_STRING,
      " DEFINITION_FILE:"   , " %79s", prdname            , TYPE_STRING,
      " SHIP_NAME:"         , " %11s", SHIP_NAME          , TYPE_STRING,
      " START_PRESSURE:"    , " %d"  , &START_PRESSURE    , TYPE_INT,
      " MAX_PRESSURE:"      , " %d"  , &MAX_PRESSURE      , TYPE_INT,
      " PRESSURE_INCREMENT:", " %d"  , &PRESSURE_INCREMENT, TYPE_INT,
      " LIST_FILE:"         , " %79s", listname           , TYPE_STRING,
      " DATA_FILE_ROOT:"    , " %79s", ascroot            , TYPE_STRING,
      NULL                  , NULL   , NULL               , 0
   };

   if (get_parameters(fpcnt, ctdcnt, NULL)) return;
   dbname[79] = prdname[79] = '\0';
   print_parameters(stdout, ctdcnt, NULL, "\n");


/*
  --> INITIALIZATION OF STRUCTURES FOR UNUSED FIELDS
*/
   set_byte((UBYTE *) &conf, '\0', sizeof(CONFIGURATION_1_TYPE));
   set_byte((UBYTE *) &anc, '\0', sizeof(ANCILLARY_1_TYPE));

   /*---------------------------------------------------------------
       NOTE:  pressures are initialized by equation rather than
              read from the file because there have been instances
              of missing data toward the surface.
     ---------------------------------------------------------------*/
   N_PRESSURE = (MAX_PRESSURE - START_PRESSURE + PRESSURE_INCREMENT) /
      PRESSURE_INCREMENT;
   if (error_found( (N_PRESSURE > NMAX), "array limit exceeded\n\n")) return;
   for (i = 0; i < N_PRESSURE; i++)
      pressure[i] = START_PRESSURE + i * PRESSURE_INCREMENT;

/*
  --> CREATE DATABASE
*/
   DBCREATE(&dbid, dbname, prdname, &memory_mode, &ierr);
   if (error_found(ierr, "DBCREATE\n\n")) return;

   iprf = 0;
   /*------------------------------------------------------
       NOTE THAT THERE IS ONE ASCII FILE FOR EACH STATION
     ------------------------------------------------------*/
   if ((fplist = fopen(listname, "r")) == NULL)
   {
      printf("\n ERROR: Cannot open list file %s\n\n", listname);
      goto close;
   }


   while ( (status = get_header(fplist, &conf, &anc, &prftime, position))
      == 0 )
   {
      strncpy(ascname, ascroot, 79);
      sprintf(ascfn, "%04d.%02d", anc.station, anc.cast_num);
      strncat(ascname, ascfn, 79);
      ascname[79] = '\0';
      if ((fpasc = fopen(ascname, "r")) == NULL)
      {
         printf("\n ERROR: Cannot open ASCII file %s\n\n", ascname);
         goto close;
      }
      printf("\n    %s ", ascname);

      if (iprf == 0)
      {
         /*----------------
             CREATE BLOCK
           ----------------*/
         DBNEWBLK(&ierr);
         if (error_found(ierr, "DBNEWBLK")) goto close;

         if (dbadd_cnf(CONFIGURATION_1, (char *) &conf,
            sizeof(CONFIGURATION_1_TYPE), "adding configuration")) goto close;

         if (dbadd_cnf(P, (char *) pressure, N_PRESSURE * sizeof(SHORT),
            "adding pressure")) goto close;
      }
      /*------------------ CREATE PROFILE ------------------*/
      DBNEWPRF(&prftime, &ierr);
      if (error_found(ierr, "DBNEWPRF")) goto close;
      printf(" ...Profile %3d => ", iprf);
      print_ymdhms_time(stdout, &prftime);

      if (dbadd_cnf(POSITION, (char *) position, sizeof(position),
         "adding position")) goto close;

      /*----------------------------------------------------------------
          NOTE:  The data in the ASCII file are read in a column at a
                 time, rather than a line at a time, to cover the case
                 where the number of observations is so large as to
                 cause a memory shortage.  Hence, to conserve memory,
                 only one data array is used to read all the variables.
        ----------------------------------------------------------------*/
      filepos = 0L;
      if (get_temperature(fpasc, &n, data)) goto close;
      if (dbaddf_cnf(TEMPERATURE, data, n, "adding temperature")) goto close;

      if (error_found(fseek(fpasc, filepos, 0L), "seek error")) goto close;
      if (get_salinity(fpasc, &n, data)) goto close;
      if (dbaddf_cnf(SALINITY, data, n, "adding salinity")) goto close;

      if (dbadd_cnf(ANCILLARY_1, (char *) &anc, sizeof(ANCILLARY_1_TYPE),
         "adding ancillary")) goto close;

      DBENDPRF(&ierr);
      if (error_found(ierr, "DBENDPRF")) goto close;
      iprf++;
      if (iprf == OPTIMUM_PROFILES_PER_BLOCK)
      {
         DBENDBLK(&ierr);
         if (error_found(ierr, "DBENDBLK")) goto close;
         iprf = 0;
      }
      fclose(fpasc);
   }

close:
   printf("\n\n");
   DBENDBLK(&ierr);
   error_found(ierr, "DBENDBLK");
   dbclose_cnf();
}

/* Note that the header comes from the list file, not
   the "ascii file" which has only the data.
*/
#if PROTOTYPE_ALLOWED
int get_header(FILE *fpasc,
               CONFIGURATION_1_TYPE *conf,
               ANCILLARY_1_TYPE     *anc,
               YMDHMS_TIME_TYPE     *time,
               DMSH_POSITION_TYPE   position[])
#else
int get_header(fpasc, conf, anc, time, position)
FILE                 *fpasc;
CONFIGURATION_1_TYPE *conf;
ANCILLARY_1_TYPE     *anc;
YMDHMS_TIME_TYPE     *time;
DMSH_POSITION_TYPE   position[];
#endif
{
   long   stime;
   long   dmy;
   int    status;
   int    lat_degrees, lon_degrees;
   double lat_minutes, lon_minutes;
   char   lat_NS, lon_EW;
   long   hundredths;

   anc->end_time = BADUSHORT;
   anc->inst     = BADSHORT;
   anc->max_pressure = BADFLOAT;
   anc->aver     = BADFLOAT;
   anc->rate     = BADFLOAT;

   printf("get_header ");
   status = fscanf(fpasc, "%hu %hu %ld  ROS  %d %lf %c %d %lf %c %ld %f",
                          &(anc->station), &(anc->cast_num),
                          &dmy,
                          &lat_degrees, &lat_minutes, &lat_NS,
                          &lon_degrees, &lon_minutes, &lon_EW,
                          &stime,
                          &(anc->ocean_depth));

   if (status == 0)
   {
      printf("End of list file.\n");
      return(1); /* Normal end. */
   }

   printf(" status = %d, station = %d\n", status, anc->station);
   if (error_found(status != 11, "bad line in header table?"))
      return(1);

   if (error_found(convert_time(stime, dmy, time), "converting time")) return(1);
   hundredths = (lat_degrees * 360000L) + (long) (lat_minutes * 6000.0);
   if (lat_NS == 'S')  hundredths = - hundredths;
   HUNPOS(&position[1], &hundredths);
   if (error_found(invalid_position(&position[1], 1), "invalid latitude"))
      return(1);
   hundredths = (lon_degrees *  360000L) + (long) (lon_minutes * 6000.0);
   if (lon_EW == 'W')  hundredths = - hundredths;
   HUNPOS(&position[0], &hundredths);
   if (error_found(invalid_position(&position[0], 0), "invalid longitude"))
      return(1);

   if (anc->ocean_depth < 0) anc->ocean_depth = BADFLOAT; /* BAD coded as -99 */

   return(0);
}

#if PROTOTYPE_ALLOWED
int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[])
#else
int get_temperature(fpasc, nvalues, temp)
FILE *fpasc;
int *nvalues;
FLOAT temp[];
#endif
{
   int i, j, jrem;
   FLOAT t, p;
   char line[80];

   /*----------------------------------------------------------
       INITIALIZE ARRAY TO BADFLOAT IN CASE OF MISSING DATA,
       PARTICULARLY FOR INITIAL PRESSURE INTERVALS
     ----------------------------------------------------------*/
   for (i = 0; i < N_PRESSURE; i++) temp[i] = BADFLOAT;

   /*-----------------------------------------------
       SCAN AVAILABLE PROFILE DATA FROM ASCII FILE
     -----------------------------------------------*/
   j = 0;
   while (fgets(line, 80, fpasc) != NULL && j < N_PRESSURE)
   {
      if (sscanf(line, " %f %f %*f", &p, &t) < 2)
      {
         printf("\n ERROR: Scanning temperature for n = %d", *nvalues+1);
         return(1);
      }
      j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data */
      jrem = ((int) p - (int) START_PRESSURE) % (int) PRESSURE_INCREMENT; /* may have missing data */
      if (jrem == 0 && j < N_PRESSURE && t >= -98) temp[j] = t;  /* BADs coded as negative values */
   }  /* end while not eof loop */
   if (j == N_PRESSURE)    *nvalues = j;
   else                    *nvalues = j + 1;
   return(0);
}

#if PROTOTYPE_ALLOWED
int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[])
#else
int get_salinity(fpasc, nvalues, sal)
FILE *fpasc;
int *nvalues;
FLOAT sal[];
#endif
{
   int i, j, jrem;
   FLOAT s, p;
   char line[80];

   /*----------------------------------------------------------
       INITIALIZE ARRAYS TO BADFLOAT IN CASE OF MISSING DATA,
       PARTICULARLY FOR INITIAL PRESSURE INTERVALS
     ----------------------------------------------------------*/
   for (i = 0; i < N_PRESSURE; i++) sal[i] = BADFLOAT;

   /*-----------------------------------------------
       SCAN AVAILABLE PROFILE DATA FROM ASCII FILE
     -----------------------------------------------*/
   j = 0;
   while (fgets(line, 80, fpasc) != NULL && j < N_PRESSURE)
   {
      if (sscanf(line, " %f %*f %f", &p, &s) < 2)
      {
         printf("\n ERROR: Scanning salinity data for n = %d", *nvalues+1);
         return(1);
      }
      j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data at initial pressures */
      jrem = ((int) p - (int) START_PRESSURE) % (int) PRESSURE_INCREMENT; /* may have missing data */
      if (jrem == 0 && j < N_PRESSURE && s >= 0) sal[j] = s;
   }
   if (j == N_PRESSURE)    *nvalues = j;
   else                    *nvalues = j + 1;
   return(0);
}

#if PROTOTYPE_ALLOWED
int convert_time(long ztime, long dmy, YMDHMS_TIME_TYPE *time)
#else
int convert_time(ztime, dmy, time)
long ztime;
long   dmy;
YMDHMS_TIME_TYPE *time;
#endif
{
   int dm;
   time->year = yr4digit( (int)(dmy % 100) );
   dm = dmy / 100;
   time->month = dm % 100;
   time->day   = dm / 100;
   time->hour = ztime / 100;
   time->minute = ztime % 100;
   time->second = 0;
   print_ymdhms_time(stdout, time);
   if (error_found(invalid_time(time), "invalid time "))
   {
      print_ymdhms_time(stdout, time);
      return(1);
   }
   return(0);
}

#if PROTOTYPE_ALLOWED
int dbaddf_cnf(int type, FLOAT data[], unsigned int n, char *msg)
#else
int dbaddf_cnf(type, data, n, msg)
int type;
FLOAT data[];
unsigned int n;
char *msg;
#endif
{
   int ierr;
   unsigned int nbad = 0;
   char badmsg[40];

   DBADD_F(&type, data, &n, &nbad, &ierr);
   if (error_found(ierr, msg)) return(ierr);
   strcpy(badmsg, "bad values ");
   strcat(badmsg, msg);
   error_found(nbad, badmsg);
   return(nbad);
}

