/*  makenetcdf                                          Aug 05          */
/*      JGOFS data -> netcdf file capability                            */

/*      Arg 1 - object spec                                             */
/*      Arg 2 - output file name                                        */
/*      Arg 3 - (optional) comma-separated list of variable names to be */
/*              treated as alphas, or keyword #NONE#.  Elements of this */
/*              list are NOT checked for being legit varnames for the   */
/*              object.                                                 */
/*                In absence of this arg, value of var that occurs in   */
/*              1st rec is examined.  If the value is missing, data     */
/*              are assumed numeric (compatibility w/matlab stuff)      */
/*              Note that problems can still occur if an alpha          */
/*              variable's first value happens to be numeric (eg,       */
/*              "standard station" variable)                            */
/*      Switch -maxwidth - (optional; any position in arg list)         */
/*              Use "JGOFS system maximum" for width of alpha variables */
/*              In absence of switch, widths attribute is used          */
/*              At issue is size of netcdf output file                  */
/*              (Note that if sys max for makenetcdf's installation is  */
/*              < sys max for installation serving object, problems can */
/*              still occur)                                            */

/*      Missing data not written to output file; accordingly, acquires  */
/*        netcdf "never written" value                                  */

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

#define MAKENETCDF_VERSION "makenetcdf 1.2  3 Dec 2012"
/*   21 Jul 16.  WJS                                                    */
/*	Comment change/clarification					*/
/*    3 Dec 12.  WJS                                                    */
/*      Bug fix: if JGOFS dataset is empty, program was netcdf-closing  */
/*    without netcdf-opening.                                           */
/*              [Begin v 1.2]                                           */
/*   22 Nov 08.  WJS                                                    */
/*      Allow strings w/trailing blanks to be considered numeric        */
/*              [Needs utils 2.4]                                       */
/*              [Needs core.h 2.0]                                      */
/*              [Begin v 1.1]                                           */

#include INNEROPTIONS
#include "jdbfuncdefns.h"
#include <netcdf.h>

  /*  utils routines                                                    */
Logical add_id_to_err();
char *buildstring();
void errn();
int get_integer_attribute();
char *lengthen_str();
char *lengthen_str_nl();
Logical string_in_list();
Logical string_is_numeric();

int level;
int handle= -1;
int nvar;

char names[NVAR][VARNAMESIZE+1];
int namesize=VARNAMESIZE+1;

char vals[NVAR][DATUMSIZE+1];
int valuesize=DATUMSIZE+1;

#define MAXLEN_DIM_NAME_PREFIX "maxlen_"
#define LEN_MAXLEN_DIM_NAME_PREFIX 7
char maxlen_dim_name_buffer[7+VARNAMESIZE+1] = MAXLEN_DIM_NAME_PREFIX;

char attrbuf[ATTRSIZE+1];

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


char *makenetcdf_return_vers()
/*  Dummy routine.  Exists only to force .h file version string into    */
/*  this module.  Note string must not be global or we'll have con-     */
/*  flicts if another routine similarly includes the version string     */
{
  static char version[] = MAKENETCDF_VERSION"/"FULL_JDBFUNCDEFNSH_VERSION;
  return version;
}

void err(s,t)
char *s,*t;
{
  char *ss,*tt;
  add_id_to_err(&ss,&tt,s,t,MAKENETCDF_VERSION);
  printf ("%s\n%s\n",s,t);
  exit(ERROR_EXIT_STATUS);
}

int *widths_from_attributes(unit,nvar)
int nvar,unit;
{
  int *widths;
  int i;

  widths = (int *)malloc(nvar * (sizeof(int)));
  if (widths == NULL)
    errn ("widths malloc failure. # bytes in attempted allocation=",
                                                        nvar * (sizeof(int)));

  for (i=0; i<nvar; i++) {
    widths[i] = valuesize;
    while (jdbattributes_(&unit,&i,attrbuf))
      if (strncmp(attrbuf,"width=",6) == 0) {
        widths[i] = get_integer_attribute(attrbuf) + 1;
        break;
      }
  }

  return widths;
}

void testsize (size,max,name,datum)
int size,max;
char *name,*datum;
{
  char *err_ptr_1,*err_ptr_2;
  char *limit_exceeded;
  char *suggest_using_flag;

  if (size <= max) return;

  suggest_using_flag = (max == valuesize) ?
                                NULL : "using -maxwidth switch or ";
  limit_exceeded = (max == valuesize) ?
                                "JGOFS system max" :
                                "variable's width attribute value";

  err_ptr_1 = lengthen_str (    err_ptr_1,
                                "Length of datum for variable ",
                                name,
                                              500, "Too long error, part 1_1");
  err_ptr_1 = lengthen_str (    err_ptr_1,
                                " exceeds ",
                                limit_exceeded,
                                                0, "Too long error, part 1_2");

  err_ptr_2 = lengthen_str_nl ( err_ptr_2,
                                "Datum = ",
                                datum,
                                              500, "Too long error, part 2_1");
  err_ptr_2 = lengthen_str (    err_ptr_2,
                                "Consider ",
                                suggest_using_flag,
                                                0, "Too long error, part 2_2");
  err_ptr_2 = lengthen_str (    err_ptr_2,
                                "not including this variable",
                                NULL,
                                                0, "Too long error, part 3_2");

  err(err_ptr_1,err_ptr_2);
  exit(1);                      /*  Not that it should get here...              */
}

int main(argc,argv)
char **argv;
int argc;
{
  int status;
  int i,maxsize;
  double df;

  int netcdf_id,netcdf_dimension;
  int *netcdf_varid;
  int unlimited_dim,maxlen_dim;


#define N_ALPHA_DIMS 2
/*  dimensions for alpha variables.  1st is the unlimited, "record"     */
/*  dimension.  2nd is string length.                                   */
  int alpha_dims[N_ALPHA_DIMS];

/*  index within netcdf file on which we are working                    */
/*  First dimension increments.  2nd dimension is where within an       */
/*    alpha variable we are working; always = 0                         */
  size_t rec_index[N_ALPHA_DIMS] = {0,0};

/*  set up to write a whole text string at once.                        */
/*  array is paralle to alpha_dims.  values are "how many of this       */
/*  dimension are getting written w/this write?"  1st value, the        */
/*  "record" dimension, is therefore always 1.  2nd dimension is        */
/*  strlen(datum), filled in later.                                     */
  size_t alpha_count[N_ALPHA_DIMS] = {1,0};

  char *ptr,*ptr1,*ptr2;

  Logical max_width_flag = FALSE;
  char *object_name = NULL;
  char *out_file_name = NULL;  /* netcdf won't accept stderr or stdout  */
  char *alpha_var_list = NULL;
#define ALPHA_VAR_LIST_SEP  ','
/*  Next special string needs to differ from any legit var name.  Also, */
/*  must not include ALPHA_VAR_LIST_SEP                                 */
#define ALPHA_VAR_LIST_NONE "#NONE#"

  Logical *var_is_numeric,datum_is_numeric,all_numeric;
  int *var_widths;

    /*  Don't know why QUERY_STRING is squashed...                      */
    /*  Presumably we don't want QUERY_STRING getting to the child      */
    /*  processes accessing local input objects.  However, that should  */
    /*  only be a problem if the children think they're in html mode,   */
    /*  and that's already the PATH_INFO control problem alluded to     */
    /*  in the 30 Sep 97 comment... [think this comment comes from      */
    /*  makemat.c.  WJS Jun 05  Yes, but the 30 Sep 97 referent is in   */
    /*  join.c.  WJS Jul 16]                                            */
  putenv("QUERY_STRING=");

  for (i = 1; i < argc; i++)
    if (argv[i][0] == '-') {
      if (strcmp(argv[i],"-maxwidth") == 0) {
        if (max_width_flag) {
          err ("Max width flag specified more than once","");
        } else {
          max_width_flag = TRUE;
        }
      } else {
        err ("Illegal switch ",argv[i]);
      }
    } else {
      if (object_name == NULL)
        object_name = argv[i];
      else if (out_file_name == NULL)
        out_file_name = argv[i];
      else if (alpha_var_list == NULL)
        alpha_var_list = argv[i];
      else
        err ("Too many non-switch arguments.  First bad arg = ",argv[i]);
    }
  if (object_name == NULL) err("Missing input object name","");
  if (out_file_name == NULL) err("Missing output file name","");
  if (alpha_var_list != NULL) {
    all_numeric = (strcmp(alpha_var_list,ALPHA_VAR_LIST_NONE) == 0);
    if (string_in_list(alpha_var_list,ALPHA_VAR_LIST_NONE) && ! all_numeric)
      err("Cannot supply an alpha variable along with the \
'no alpha variables' choice.  Selections were ",alpha_var_list);
  }

  nvar = -NVAR;
  level = jdbopen_(&handle,object_name,names,&namesize,&nvar);
  if (level < 0)
    errn(
          buildstring("jdbopen failure on object ",
                      object_name,
                      "\njdbopen return code=",
                      "jdbopen err msg"),
          level
        );

  status = nc_create(out_file_name,NC_CLOBBER,&netcdf_id);
  if (status != NC_NOERR)
    err (
                buildstring (
                                "netcdf error reported by nc_create to file ",
                                out_file_name,
                                ".  netcdf err string: ",
                                "nc_open error"
                            ),
                nc_strerror(status)
        );

  if ( ! max_width_flag) var_widths = widths_from_attributes(handle,nvar);

  if (  (level = jdbreada_(&handle,vals,&valuesize)) >= 0  ) {

    var_is_numeric = (Logical *)malloc(nvar * (sizeof(Logical)));
    if (var_is_numeric == NULL)
      errn ("var_is_numeric malloc failure. # bytes in attempted allocation=",
        nvar * (sizeof(Logical)));

      /*  Because we have to declare netcdf variable types before       */
      /*  writing any, we have only the first JGOFS data record on      */
      /*  which to base the info (unless we do 2 passes on the object)  */
      /*  Therefore, an "nd" in the first data record forces us to guess */
      /*  at the type for that variable.  We guess "alpha", since the   */
      /*  user will at least have numeric data that way...              */
      /*    Alternatively, user may have given us list of alpha vars.   */
      /*  If so, use that                                               */
    for (i = 0; i < nvar; i++)
      if (alpha_var_list == NULL)
        var_is_numeric[i] = string_is_numeric(vals[i],TRUE);
      else
        var_is_numeric[i] = (all_numeric) ?
            TRUE :
            ( ! string_in_list(alpha_var_list,names[i],ALPHA_VAR_LIST_SEP));


    status = nc_def_dim (netcdf_id,"unlimited",NC_UNLIMITED,&unlimited_dim);
    if (status != NC_NOERR) err ("nc_def_dim failure. netcdf err string: ",
                                                        nc_strerror(status));
/*    unlimited dimension must be first                                 */
    alpha_dims[0] = unlimited_dim;
/*    if we want all alpha vars to be sized "to the max", define that   */
/*    dimension now.  Otherwise, we'll define a per-variable size based */
/*    on width attribute                                                */
    if (max_width_flag) {
      status = nc_def_dim (netcdf_id,maxlen_dim_name_buffer,
                                                        valuesize,&maxlen_dim);
      if (status != NC_NOERR)
            err (
                  buildstring("nc_def_dim failure for dimension ",
                              maxlen_dim_name_buffer,
                              " netcdf err string: ",
                              "def_dim err msg"                         ),
                  nc_strerror(status)
                );
      alpha_dims[1] = maxlen_dim;
    }


    netcdf_varid = (int *)malloc(nvar * (sizeof(int)));
    if (netcdf_varid == NULL)
      errn ("netcdf_varid malloc failure. # bytes in attempted allocation=",
        nvar * (sizeof(int)));

    for (i=0; i<nvar; i++) {
      if (var_is_numeric[i]) {
        status = nc_def_var(netcdf_id,names[i],NC_DOUBLE,1,
                                        &unlimited_dim,&netcdf_varid[i]);
      } else {
        if ( ! max_width_flag) {
          strcpy (maxlen_dim_name_buffer+LEN_MAXLEN_DIM_NAME_PREFIX,names[i]);
          status = nc_def_dim (netcdf_id,maxlen_dim_name_buffer,
                                                var_widths[i],&maxlen_dim);
          if (status != NC_NOERR)
            err (
                  buildstring("nc_def_dim failure for dimension ",
                              maxlen_dim_name_buffer,
                              " netcdf err string: ",
                              "def_dim err msg"                         ),
                  nc_strerror(status)
                );
          alpha_dims[1] = maxlen_dim;
        }
        status = nc_def_var(netcdf_id,names[i],NC_CHAR,N_ALPHA_DIMS,
                                                alpha_dims,&netcdf_varid[i]);
      }
      if (status != NC_NOERR)
        err (
             buildstring("nc_def_var failure for variable ",
                          names[i],
                          " netcdf err string: ",
                          "def_var err msg"                             ),
             nc_strerror(status)
            );
    }

    status = nc_enddef (netcdf_id);
    if (status != NC_NOERR) errn ("nc_enddef failure. netcdf err string: ",
                                                        nc_strerror(status));
  }


  rec_index[0] = 0;

  while (level >= 0) {
    for (i=0; i<nvar; i++) {
        /*  Don't write "nd"s - value becomes netcdf's special "never-  */
        /*  written" value                                              */
      if (strcmp(vals[i],MISSING_VALUE_STRING) != 0) {
        if (var_is_numeric[i]) {
          GET_NUMBER_FROM_STRING(df,vals[i],datum_is_numeric);
          if ( ! datum_is_numeric) {
            ptr1 = buildstring("Non-numeric found in variable assumed numeric",
                               ".  Variable = ",
                               names[i],
                               "ptr1 err msg");
            ptr2 = buildstring("\nDatum value = ",
                               vals[i],
                               "\nError occurred in flat record number ",
                               "ptr2 err msg");
            errn(buildstring(ptr1,ptr2,NULL,"concat ptr1 ptr2"),rec_index[0]+1);
          }
          status = nc_put_var1_double(netcdf_id,netcdf_varid[i],&rec_index[0],
                                                                        &df);
        } else {
/*          +1 for trailing \0                                          */
          alpha_count[1] = strlen(vals[i]) + 1;
          maxsize = (max_width_flag) ? valuesize : var_widths[i];
          testsize (alpha_count[1],maxsize,names[i],vals[i]);
          status = nc_put_vara_text(netcdf_id,netcdf_varid[i],rec_index,
                                                alpha_count,vals[i]);
        }
        if (status != NC_NOERR)
          err ("netcdf error after nc_put_var1_XXXX.  netcdf err string: ",
                                                        nc_strerror(status));
      }
    }
    rec_index[0]++;
    level = jdbreada_(&handle,vals,&valuesize);
  }

  if (level != JDB_CONDITION_EOF) errn ("Bad jdbread.  status=",level);

  status = nc_close (netcdf_id);
  if (status != NC_NOERR) err ("nc_close failure. netcdf err string: ",
                                                        nc_strerror(status));

  exit(0);
}
