/*
 *  $Id: cdf2splus.c,v 1.14 1994/08/29 22:06:53 mintha Exp mintha $
 *
 *  Routine to read cdf file from Splus
 *
 *  $Log: cdf2splus.c,v $
 * Revision 1.14  1994/08/29  22:06:53  mintha
 * Added patch for CDF type short (temporary)
 *
 * Revision 1.13  1994/04/21  10:55:34  mintha
 * Using temp file in /tmp now
 *
 * Revision 1.12  1994/04/19  10:40:42  mintha
 * mistook
 *
 * Revision 1.11  1994/04/19  10:39:41  mintha
 * Splus passes the name of the temp file created.
 *
 * Revision 1.10  1994/04/18  15:31:31  mintha
 * cleaning up
 *
 * Revision 1.9  1994/04/18  10:53:49  mintha
 * Many changes - trying something new so I will check it in
 *
 * Revision 1.8  1993/12/03  17:21:37  mintha
 * Added integer value for attributes
 *
 * Revision 1.7  1993/08/31  00:36:17  mintha
 * Fixed bug in getting dim labels.
 *
 * Revision 1.6  1993/08/25  23:58:24  mintha
 * Added parse_string function to replace spluses parse
 *
 * Revision 1.5  1993/08/25  19:57:03  mintha
 * Nothing changed
 *
 * Revision 1.4  1993/08/20  21:01:16  mintha
 * Got rid of old chk_cdf
 *
 * Revision 1.3  1993/08/20  07:30:45  mintha
 * Added logic to cdf_attr_info to look up dim id of an attribute that
 * ends in .lab (dim label)
 *
 * Revision 1.2  1993/08/19  22:30:28  mintha
 * Cleaned up, non-fatal on cdf errors, allow open for writing.
 *
 * Revision 1.1  1993/08/10  23:29:48  mintha
 * Initial revision
 *
 */

#include "stdio.h"
#include "math.h"
#include "netcdf.h"
#include "fcntl.h"
#include "make_splus.h"
#include <S.h>

#define MAX_LEN 128
#define FAIL -1

boolean chk_write(int, char *, int);
void do_error(char *);

/*
 * chk_cdf - check the return value from an CDF call.  If it is fail
 *	     print an appropriate error message.
 */

#define chk_cdf(rtn_val, mesg) if(rtn_val==FAIL) { fprintf(stderr,"CDF error: %s\n",mesg);return;}

/*
 * cdf_open - Opens a netcdf file.  Returns the netcdf file ID number and 
 * 	      the number of variables.
 */

void
cdf_open(char *file_name[], int *ncid, int *write, int *num_dims,
	 int *num_vars, int *num_gattrs, int *unlim_dim)
{
  ncopts = NC_VERBOSE;
  
  /* Open the HDF data file */

  *ncid = ncopen(*file_name, (*write == TRUE) ? NC_WRITE : NC_NOWRITE);
  if((*ncid)==FAIL)
    {
      fprintf(stderr, "CDF error: Opening data file\n");
      return;
    }
  chk_cdf(ncinquire(*ncid, num_dims, num_vars, num_gattrs, unlim_dim), "Getting file info");
}


/*
 * cdf_var_info - Get information for a given variable ID.  Name, type, dimension ID's,
 *                and the number of attributes
 */

void
cdf_var_info(int *ncid, int *id, char **name[], int *name_len, int *type,
	     int *dim_ids[], int *dimid_len, int *num_attrs)
{
  char *var_name;
  int *dimids;
  
  var_name=(char *) S_alloc(MAX_LEN, 1);
  dimids=(int *) S_alloc(MAX_LEN, 4);
  
  chk_cdf(ncvarinq(*ncid, *id, var_name, (nc_type *) type, dimid_len, dimids, num_attrs), "Getting variable info");

  *name_len = 1;
  *name = &var_name;
  *dim_ids = dimids;
}

  
/*
 * cdf_dim_info - Get information for a given dimension ID.  Name, & size.
 */


void
cdf_dim_info(int *ncid, int *id, char **name[], int *name_len, int *size)
{
  char *dim_name;
  
  dim_name=(char *) S_alloc(MAX_LEN, 1);
  chk_cdf(ncdiminq(*ncid, *id, dim_name, size), "Getting variable info");

  *name_len = 1;
  *name = &dim_name;
}

  
/*
 * cdf_attr_info - Get information for a given variable attribute number.  Return
 *                 name, type, and value in the given type.
 */

void
cdf_attr_info(int *ncid, int *var_id, int *attr_num, 
	      int *type, int *dim_id,
	      char **name[], int *name_len,
	      char **ch_val[], int *ch_len,
	      long *lng_val[], int *lng_len,
	      float *flt_val[], int *flt_len,
	      double *dbl_val[], int *dbl_len)
{
  void *value, *value2;
  char *attr_name, temp_name[MAX_LEN], *ptr, *ptr2;
  int len = 0, ctr;
  char tmp[80];
  
  *ch_len=0;
  *flt_len=0;
  *lng_len=0;
  *dbl_len=0;
  *dim_id=-1;
  *lng_val = NULL;
  *flt_val = NULL;
  *dbl_val = NULL;

  attr_name=(char *) S_alloc(MAX_LEN,1);
  chk_cdf(ncattname(*ncid, *var_id, *attr_num, attr_name), "Getting attribute name");
  *name_len = 1;
  *name = &attr_name;
  
  chk_cdf(ncattinq(*ncid, *var_id, attr_name, (nc_type *)type, &len), "Getting attribute info");
  
  switch(*type)
    {
    case NC_CHAR:
      value = S_alloc(len + 1, sizeof(char));
      *ch_len = 1;
      *ch_val= (char **) &value;
      break;
    case NC_SHORT:
      value = malloc((len + 1) * 2);
      value2 = S_alloc(len + 1, sizeof(long));
      *lng_len = len;
      *lng_val = (long *) value2;
      break;
    case NC_LONG:
      value = S_alloc(len + 1, sizeof(long));
      *lng_len = len;
      *lng_val = (long *) value;
      break;
    case NC_FLOAT:
      value = S_alloc(len + 1, sizeof(float));
      *flt_len = len;
      *flt_val = (float *) value;
      break;
    case NC_DOUBLE:
      value = S_alloc(len + 1, sizeof(double));
      *dbl_len = len;
      *dbl_val = (double *) value;
      break;
    }
  
  chk_cdf(ncattget(*ncid, *var_id, attr_name, value), "Getting attribute value");

  if(*type == NC_CHAR)
    {
      ptr = (char *) value;
      ptr[len]='\0';
    }
  
  if(*type == NC_SHORT)
    {
      ptr = (char *) value;
      ptr2 = (char *) value2;
      for(ctr = 0; ctr < len; ctr++)
	{
	  ptr2[ctr*4] = 0;
	  ptr2[ctr*4+1] = 0;
	  ptr2[ctr*4+2] = ptr[ctr*2];
	  ptr2[ctr*4+3] = ptr[ctr*2+1];
	}
    }
	  
  len=strlen(attr_name)-4;
  if(*type == NC_CHAR && len>0)
      if(strncmp(".lab", attr_name+len, 4) == 0 && strncmp(attr_name, "unit", 4) != 0)
	{
	  strncpy(temp_name, attr_name, len);
	  temp_name[len]='\0';
	  *dim_id = ncdimid(*ncid, temp_name);
	  if(*dim_id == FAIL)
	    fprintf(stderr, "Warning: Attribute that looks like a dim label\n");
	  else
	    attr_name[len] = '\0';
	}
}

/*
 * cdf_get_data - Given a hyperslap,and type of data, read and return the data.
 */

void
cdf_read_data(int *ncid, int *var_id, int *type, long start_dims[], long slice_dims[],
	      char **ch_data, float *flt_data, double *dbl_data)
{
  void *data=NULL;
  
  if((*type) == NC_CHAR) data=ch_data;
  if((*type) == NC_FLOAT) data=flt_data;
  if((*type) == NC_DOUBLE) data=dbl_data;
  
  chk_cdf(ncvarget(*ncid, *var_id, start_dims, slice_dims, data), "Reading data=\n");
}

/*
 * cdf_close - Closes a CDF file.
 */

void
cdf_close(int *ncid)
{
  chk_cdf(ncclose(*ncid), "Closing cdf data file");
}

/*
 * parse_string - Break a string into little strings.  Maximum of 500 labels
 */

void
parse_string(char *string[], char **results[], int *len)
{
  int ctr=0;

  *results = (char **) S_alloc(500,1);
  (*results)[ctr++]=strtok(*string," ");
  
  while(((*results)[ctr++]=strtok(NULL," ")) != NULL);

  *len=ctr-1;
  
}


/* 
 * cdf_attr_info2 - Try a different way to get attribute info that
 *                  may just work.
 */

void
cdf_attr_info2(int *ncid, int *var_id, int *attr_num, int *dim_id,
	       char *fname[], char **name[], int *name_len)
{
  void *value;
  char *attr_name, temp_name[MAX_LEN], *ptr;
  int len = 0, s_type, type;
  
  *dim_id=-1;
  attr_name=(char *) S_alloc(MAX_LEN,1);
  chk_cdf(ncattname(*ncid, *var_id, *attr_num, attr_name), "Getting attribute name");
  *name_len = 1;
  *name = &attr_name;
  
  chk_cdf(ncattinq(*ncid, *var_id, attr_name, (nc_type *) &type, &len), "Getting attribute info");

  switch(type)
    {
    case NC_CHAR:
      value = (void *) malloc((len + 1) * sizeof(char));
      s_type = S_STRING;
      break;
    case NC_SHORT:
      value = (void *) malloc(len * 2);
      s_type = S_SHORT;
      break;
    case NC_LONG:
      value = (void *) malloc(len * sizeof(long));
      s_type = S_INT;
      break;
    case NC_FLOAT:
      value = (void *) malloc(len * sizeof(float));
      s_type = S_FLOAT;
      break;
    case NC_DOUBLE:
      value = (void *) malloc(len * sizeof(double));
      s_type = S_DOUBLE;
      break;
    }

  chk_cdf(ncattget(*ncid, *var_id, attr_name, value), "Getting attribute value");  
  write_sfile(*fname, s_type, len, value);

  len = strlen(attr_name) - 4;
  if(type == NC_CHAR && len > 0)
    if(strncmp(".lab", attr_name+len, 4) == 0 && strncmp(attr_name, "unit", 4) != 0)
      {
	strncpy(temp_name, attr_name, len);
	temp_name[len]='\0';
	*dim_id = ncdimid(*ncid, temp_name);
	
	if(*dim_id == FAIL)
	  fprintf(stderr, "Warning: Attribute that looks like a dim label\n");
	else
	  attr_name[len] = '\0';
      }
}

write_sfile(char *fname, int type, int len, void *data)
{
  int ctr, fd, curr_pos, param, num_vals = 1;
  char blank[4];
  char tmp_name[255];
  
  strcpy(tmp_name, "/tmp/");
  strcat(tmp_name, fname);
  
  fd=open(tmp_name, O_CREAT | O_WRONLY, 0666);

  if(fd < 0)
    do_error("Error opening splus file for writing");
  
  chk_write(fd, SPLUS_ID, 8);      /* Splus header */

  param = (type == S_SHORT) ? S_INT : type;
  chk_write(fd, (void *) &param, 4);        /* data type */

  if(type != S_STRING)
    num_vals = len;
  
  chk_write(fd, (void *) &num_vals, 4);     /* Number of data values */

  if(type == S_STRING)
    {
      param = len;
      chk_write(fd, (void *) &param, 4);    /* length of string */
    }
  
  /* write data */
  num_vals = len;

  /* for type short we write it out longhand into integers */
  if(type == S_SHORT)
    {
      blank[0] = 0;
      blank[1] = 0;
      for(ctr = 0; ctr < num_vals; ctr++)
	{
	  chk_write(fd, (void *) blank, 2);
	  chk_write(fd, (void *) data+(ctr*2), 2);
	}
    }
  else
    chk_write(fd, (void *) data, num_vals * type_size[type]);
    
  close(fd);
}

/*
 * chk_write - Writes a block of 'size' bytes from 'buff' into 'file'.
 *             Checks to ensure that the block was written correctly.
 *             Returns FALSE if it wasn't.
 */

boolean
chk_write(int file, char *buff, int size)
{
  int nwrote;
  
  nwrote = write(file, buff, size);
  
  if(nwrote != size)
    {
      do_error("Error writing to file");
      return FALSE;
    }
  
  return TRUE;
}

void
do_error(char *msg)
{
  printf("Error in make_splus: %s\n",msg);
  fflush(stdout);
}
