/* iapi.c */


/*
 * Vis5D system for visualizing five dimensional gridded data sets.
 * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul,
 * Dave Santek, and Andre Battaiola.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * As a special exception to the terms of the GNU General Public
 * License, you are permitted to link Vis5D with (and distribute the
 * resulting source and executables) the LUI library (copyright by
 * Stellar Computer Inc. and licensed for distribution with Vis5D),
 * the McIDAS library, and/or the NetCDF library, where those
 * libraries are governed by the terms of their own licenses.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../config.h"



/***************************************************************/
/***************************************************************/
/* This file contains a set of functions that will act as an   */
/* interface for reading and obtaining various nessecary info  */
/* about a NetCDF file.                                        */
/* The functions in 'file.c' will use these.                   */
/***************************************************************/
/***************************************************************/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_LIBNETCDF

#ifdef HAVE_NETCDF_H
#  include <netcdf.h>
#else
#  include "netcdf.h"
#endif

#include "iapi.h"
#include "igui.h"

NetCDF_Format_Info UNIDATA_METAR;
NetCDF_Format_Info FSL_METAR;
NetCDF_Format_Info FSL_PROFILE;

/************************/
/* Close a NetCDF file  */
/************************/
/* Input: fid - File ID */
/************************/
int Close_NetCDF( int fid )
{
   int status;

   status = nc_close(fid);
   if (status != NC_NOERR){
      return 0;
   }
   return 1;
}

/*****************************************************/
/* This will initialize the structures which contain */ 
/* the needed information, such as var names, for the*/
/* different NetCDF file types                       */
/* When adding the ability to read more NetCDF files */
/* more formats will have to be added here           */
/*****************************************************/
void Initialize_NetCDF_Format_Info( void )
{
   /************************************************/
   /* go through the list of formats and init them */
   /************************************************/

   /*************/
   /* FSL_METAR */
   /*************/
   FSL_METAR = (NetCDF_Format_Info) calloc( 1, sizeof(struct netcdf_format_info));
   if (!FSL_METAR){
      printf("Error in creating FSL_METAR struct\n");
      exit(0);
   }
   {
      strcpy(FSL_METAR->METAR_REPORT_TYPE_LENGTH, "maxRepLen");
      strcpy(FSL_METAR->METAR_CLD_LAYERS, "maxSkyCover");
      strcpy(FSL_METAR->METAR_REPORT_TYPE, "reportType");
      strcpy(FSL_METAR->TIME, "timeNominal");
      strcpy(FSL_METAR->LAT, "latitude");
      strcpy(FSL_METAR->LON, "longitude");
      strcpy(FSL_METAR->HGT, "elevation");
      strcpy(FSL_METAR->METAR_CLD_TYPE, "skyCover");
      strcpy(FSL_METAR->METAR, "METAR");
      strcpy(FSL_METAR->SPECI, "SPECI");
      strcpy(FSL_METAR->REC_NUM, "recNum");
      strcpy(FSL_METAR->LOCATION_FILL, "_FillValue");
   }

   /*****************/
   /* UNIDATA_METAR */
   /*****************/
   UNIDATA_METAR = (NetCDF_Format_Info) calloc( 1, sizeof(struct netcdf_format_info));
   if (!UNIDATA_METAR){
      printf("Error in creating UNIDATA_METAR struct\n");
      exit(0);
   }
   {
      strcpy(UNIDATA_METAR->METAR_REPORT_TYPE_LENGTH, "rep_type_len");
      strcpy(UNIDATA_METAR->METAR_CLD_LAYERS, "cloud_layers");
      strcpy(UNIDATA_METAR->METAR_REPORT_TYPE, "rep_type");
      strcpy(UNIDATA_METAR->TIME, "time_nominal");
      strcpy(UNIDATA_METAR->LAT, "lat");
      strcpy(UNIDATA_METAR->LON, "lon");
      strcpy(UNIDATA_METAR->HGT, "elev");
      strcpy(UNIDATA_METAR->METAR_CLD_TYPE, "cloud_type");
      strcpy(UNIDATA_METAR->METAR, "METAR");
      strcpy(UNIDATA_METAR->SPECI, "SPECI");
      strcpy(UNIDATA_METAR->REC_NUM, "recNum");
      strcpy(UNIDATA_METAR->LOCATION_FILL, "_FillValue");
   }


   /***************/   
   /* FSL PROFILE */
   /***************/
   FSL_PROFILE = (NetCDF_Format_Info) calloc( 1, sizeof(struct netcdf_format_info));
   if (!FSL_PROFILE){
      printf("Error in creating FSL_PROFILEstruct\n");
      exit(0);
   }
   {
      strcpy(FSL_PROFILE->LEVELDIM, "level");
      strcpy(FSL_PROFILE->LEVELVAR, "levels");
      strcpy(FSL_PROFILE->LAT, "staLat");
      strcpy(FSL_PROFILE->LON, "staLon");
      strcpy(FSL_PROFILE->HGT, "staElev");
      strcpy(FSL_PROFILE->TIME, "timeObs");
      strcpy(FSL_PROFILE->REC_NUM, "recNum");
      strcpy(FSL_PROFILE->LOCATION_FILL, "_FillValue");
   }

}   


/****************************************************/
/* This helper function goes through a list of data */
/* and finds the min and max values, and disregards */
/* any values matching fillvalue                    */
/****************************************************/
/* Input: numdata - the size of the data array      */
/*        data - array containing the number data   */
/*        fillvalue - values which are considered   */ 
/*                    missing                       */
/* Output: min, max - the min and max values        */
/****************************************************/
void get_min_and_max( int numdata, double *data,
                      double fillvalue, double *min, double *max)
{
   int i, j;
   double tmin, tmax;

   /*************************************/   
   /* first get first min and max since */
   /* first data value may be fillvalue */
   /*************************************/
   j = 0;
   do{
     tmin = tmax = data[j];
     j++;
   } while(tmin == fillvalue && j < numdata);

   for (i = j; i < numdata; i++){
      if (data[i] != fillvalue){
         if (data[i] < tmin){
            tmin = data[i];
         }
         else if (data[i] > tmax){
            tmax = data[i];
         }
      }
   }
   *min = tmin;
   *max = tmax;
}


/*************************************************/
/* This opens a NetCDF file and gets it's file ID*/
/*************************************************/
/* Input: filename - the name of the file to open*/
/* Output: fid - NetCDF file ID                  */
/*************************************************/
int Open_NetCDF( char *filename, int *fid )
{
   int status, nc_id;

   /********************************************/
   /* open file and see if it is a netCDF file */
   /********************************************/
   status = nc_open( filename, NC_NOWRITE, &nc_id);
   if(status != NC_NOERR){
      return 0;
   }
   *fid = nc_id;
   return 1;
}


/***************************************************/
/* This opens a NetCDF file and returns the type   */
/* It is fairly general right now and may need to  */
/* be altered when more types need to be           */
/* distinguished                                   */
/***************************************************/
/* Input: filename - the name of the file to open  */
/* Output: general_type - one of NETCDF_SURFACE or */
/*                        NETCDF_SOUNDING          */
/*         specific_type- one of fsl_netcdf_metar  */
/*                        fsl_netcdf_bouy          */
/*                        fsl_netcdf_profile       */
/*                        unidata_netcdf_metar     */
/*                        unidata_netcdf_upperair  */
/*         fid - NetCDF file ID                    */
/***************************************************/
int Read_NetCDF( char *filename, int *general_type, int *specific_type, int *fid )
{
   int id1, id2;
   int status, status2, status3, status4;
   size_t len1;
   int nc_id;
   char *text1;

   *general_type = -1;
   *specific_type = -1;

   /********************************************/
   /* open file and see if it is a netCDF file */
   /********************************************/
   status = nc_open( filename, NC_NOWRITE, &nc_id);
   if(status != NC_NOERR){
      printf("Error: can't open netcdf file\n");
      return 0;
   }
   *fid = nc_id;


   /****************************/   
   /* Try FSL_NETCDF_METAR 1st */
   /****************************/
   status = nc_inq_dimid( nc_id, FSL_METAR->METAR_REPORT_TYPE_LENGTH, &id1);

	if(status !=  NC_NOERR){
	  status = nc_inq_varid( nc_id, FSL_METAR->TIME, &id1);
	}

   if (status == NC_NOERR){
		/* 28-10-2000 this test doesn't work on my my FSL-like synop files */
      /* If you see some reason that this test should be required please 
			contact me and I will happily seek to resolve the problem in a 
			mutually suitable manner.  Jim Edwards jedwards@fsl.noaa.gov
			jedwards@inmet.gov.br  */
	  /*
      status = nc_inq_dimlen( nc_id, id1, &len1);
      if (status != NC_NOERR){
         nc_close(nc_id);
         return 0;
      }


      status = nc_inq_varid( nc_id, FSL_METAR->METAR_REPORT_TYPE, &id2);
      if (status != NC_NOERR){
         nc_close(nc_id);
         return 0;
      }
      text1 = (char *) malloc(len1+1);
      memset(text1, 0, len1+1);
      {
         size_t temp[] = {0,0};
         int i;
         for (i = 0; i < len1; i++){
            nc_get_var1_text(nc_id, id2, temp, &text1[i]);
            temp[1]++;
         }
      }


 
		if (strcmp(FSL_METAR->METAR, text1)==0 ||
				  strcmp(FSL_METAR->SPECI, text1)==0){  

         free(text1);  
	  */
         *general_type = NETCDF_SURFACE;
         *specific_type = fsl_netcdf_metar;
         return 1;
			/*      }
      else{
		  
		  printf("Looks like an FSL type file but %s doesn't match %s or %s\n",
					text1, FSL_METAR->METAR, FSL_METAR->SPECI);

         free(text1);
         nc_close(nc_id);
         return 0;
      }
			*/
   }

   /*********************************/   
   /* Try UNIDATA_NETCDF_METAR next */
   /*********************************/
   status = nc_inq_dimid( nc_id, UNIDATA_METAR->METAR_REPORT_TYPE_LENGTH, &id1);
   if (status == NC_NOERR){
      status = nc_inq_dimlen( nc_id, id1, &len1);
      if (status != NC_NOERR){
         nc_close(nc_id);
         return 0;
      }
      status = nc_inq_varid( nc_id, UNIDATA_METAR->METAR_REPORT_TYPE, &id2);
      if (status != NC_NOERR){
         nc_close(nc_id);
         return 0;
      }
      text1 = (char *) malloc(len1+1);
      memset(text1, 0, len1+1);
      {
         size_t temp[] = {0,0};
         int i;
         for (i = 0; i < len1; i++){
            nc_get_var1_text(nc_id, id2, temp, &text1[i]);
            temp[1]++;
         }
      }

      if (strcmp(UNIDATA_METAR->METAR, text1)==0 ||
          strcmp(UNIDATA_METAR->SPECI, text1)==0){
         free(text1);
         *general_type = NETCDF_SURFACE;
         *specific_type = unidata_netcdf_metar;
         return 1;
      }
      else{
         free(text1);
         nc_close(nc_id);
         return 0;
      }
   }


   /**************************************/
   /* now check to see if it's a PROFILE */
   /**************************************/
   /* for now the only way i'm assuming  */
   /* it's a wind profile file, is if its*/
   /* got LEVELVAR as one of it's variables*/
   /* this should change later           */
   /**************************************/
   status = nc_inq_varid( nc_id, FSL_PROFILE->LEVELVAR, &id1);
   status2 = nc_inq_varid( nc_id, FSL_PROFILE->LAT, &id1);
   status3 = nc_inq_varid( nc_id, FSL_PROFILE->LON, &id1);
   status4 = nc_inq_varid( nc_id, FSL_PROFILE->HGT, &id1);
   if (status == NC_NOERR && status2 == NC_NOERR &&
       status3== NC_NOERR && status4 == NC_NOERR){
      *general_type = NETCDF_SOUNDING;
      *specific_type = fsl_netcdf_profile;
      return 1;
   }

   printf("Error: unrecognized file type\n");
   return 0;
}


/***************************************************/
/* This reads a NetCDF location for a given record */
/***************************************************/
/* Input: finfo - the NetCDF format info           */
/*        nc_id - NetCDF file ID                   */
/*        recid - Record number or ID for desired  */
/*                location                         */
/* Output: lat, lon, hgt - Latitude, Longitude and */
/*                         Altitude for record     */
/***************************************************/
int Read_NetCDF_Location( NetCDF_Format_Info finfo,
                          int nc_id, int recid, float *lat, float *lon,
                          float *hgt)
{
   int latid, lonid, hgtid;
   static size_t index[1];
   int status;

   index[0] = recid;
   status = nc_inq_varid( nc_id, finfo->LAT, &latid);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->LON, &lonid);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->HGT, &hgtid);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   
   status = nc_get_var1_float( nc_id, latid, index, lat);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   status = nc_get_var1_float( nc_id, lonid, index, lon);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   status = nc_get_var1_float( nc_id, hgtid, index, hgt);
   if(status != NC_NOERR){
      printf("error in int Read_NetCDF_Location\n");
      return 0;
   }
   return 1;
}

/****************************************************************/
/* This reads a 1 Dimensional interger value from a NetCDF file */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/* Output: data - the integer data                              */
/****************************************************************/
int Read_1D_NetCDF_Var_Int_Data( int nc_id, int recid, int varid,
                                      int *data)
{
   static size_t index[1];
   int status;

   index[0] = recid;
   status = nc_get_var1_int( nc_id, varid, index, data);
   if (status != NC_NOERR){
      printf("error in int int Read_NetCDF_METAR_Var_int_Data\n");
      return 0;
   }
   return 1;
}


/***************************************************/
/* This reads the sounding data from a NetCDF file */
/***************************************************/
/* Input: nc_id - NetCDF file ID                   */
/*        recid - Record ID                        */
/*        varid - Variable ID                      */
/*        levels - the number of sounding levels   */
/* Output: data - sounding data                    */
/***************************************************/
int Read_Sounding_NetCDF_Var_Data( int nc_id, int recid, int varid,
                                   int levels, double *data)
{
   static size_t start[2], end[2];
   int  status;

   start[0] = recid;
   start[1] = 0;
   end[0] = 1;
   end[1] = levels;
   status = nc_get_vara_double(nc_id, varid, start, end, data);
   if (status != NC_NOERR){
      printf("error in Read_Sounding_NetCDF_Var_Data\n");
      return 0;
   }
   return 1;
}


/****************************************************************/
/* This Reads a 1 Dimensional double value from a NetCDF file   */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/* Output: data - the double data                               */
/****************************************************************/
int Read_1D_NetCDF_Var_Double_Data( int nc_id, int recid, int varid,
                                      double *data)
{
   static size_t index[1];
   int status;

   index[0] = recid;
   status = nc_get_var1_double( nc_id, varid, index, data);
   if (status != NC_NOERR){
      printf("error in int int Read_NetCDF_METAR_Var_Double_Data\n");
      return 0;
   }
   return 1;
}

/****************************************************************/
/* This reads a 1 Dimensional string from a NetCDF file         */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/*        len - length of character string to be read           */
/* Output: data - char data                                     */
/****************************************************************/
int Read_1D_NetCDF_Var_Char_Data( int nc_id, int recid, int varid, 
                                     int len, char *data)
{
   size_t index[2];
   int i;
   int status;

   index[0] = recid;
   index[1] = 0;
   for (i=0; i<len; i++){
      status = nc_get_var1_text(nc_id, varid, index, &data[i]);
      if(status != NC_NOERR){
         printf("error in int int Read_NetCDF_METAR_Var_Char_Data\n");
         return 0;
      }
      index[1]++;
   }
   return 1;
}

/****************************************************************/
/* This reads a 2 Dimensional interger value froma NetCDF file  */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/*        other_dim - second index into the NetCDF data array   */
/* Output: data - the interger data                             */
/****************************************************************/
int Read_2D_NetCDF_Var_Int_Data( int nc_id, int recid, int varid,
                                      int other_dim, int *data)
{
   static size_t index[2];
   int status;

   index[0] = recid;
   index[1] = other_dim;
   status = nc_get_var1_int( nc_id, varid, index, data);
   if (status != NC_NOERR){
      printf("error in int int Read_NetCDF_METAR_Var_Int_Data\n");
      return 0;
   }
   return 1;
}

/****************************************************************/
/* This reads a 2 Dimensional double   value froma NetCDF file  */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/*        other_dim - second index into the NetCDF data array   */
/* Output: data - the double   data                             */
/****************************************************************/
int Read_2D_NetCDF_Var_Double_Data( int nc_id, int recid, int varid, 
                                      int other_dim, double *data)
{
   static size_t index[2];
   int status;

   index[0] = recid;
   index[1] = other_dim;
   status = nc_get_var1_double( nc_id, varid, index, data);
   if (status != NC_NOERR){
      printf("error in int int Read_NetCDF_METAR_Var_Double_Data\n");
      return 0;
   }
   return 1;
}

/****************************************************************/
/* This reads a 2 Dimensional string from a NetCDF file         */
/****************************************************************/
/* Input: nc_id - NetCDF file ID                                */
/*        recid - Record ID                                     */
/*        varid - Variable ID                                   */
/*        len - length of character string to be read           */
/*        other_dim - second index into the NetCDF data array   */
/* Output: data - char data                                     */
/****************************************************************/
int Read_2D_NetCDF_Var_Char_Data( int nc_id, int recid, int varid,
                                     int len, int other_dim, char *data)
{
   size_t index[3];
   int i;
   int status;

   index[0] = recid;
   index[1] = other_dim;
   index[2] = 0;
   for (i=0; i<len; i++){
      status = nc_get_var1_text(nc_id, varid, index, &data[i]);
      if(status != NC_NOERR){
         printf("error in int int Read_NetCDF_METAR_Var_Char_Data\n");
         return 0;
      }
      index[2]++;
   }
   return 1;
}
   

 
/************************************************************/
/* Get the NetCDF Variable ID from the NetCDF variable name */
/************************************************************/
/* Input: fid - NetCDF file ID                              */
/*        varname - variable name                           */
/* Ouput: varid - NetCDF variable ID associated with the    */
/*        desired variable name                             */
/************************************************************/
int Read_netCDF_Var_ID( int fid, char *varname, int *varid)
{
   int status;

   status = nc_inq_varid( fid, varname, varid);
   if (status == NC_ENOTVAR){
      *varid = -1;
      return 1;
   }
   else if (status != NC_NOERR){
      printf("error in Read_netCDF_METAR_Var_ID\n");
      return 0;
   }
   return 1;
}


/******************************************************************/
/* Get the fill or Missing values for a certain variable, this is */
/* useful for determining if a value should be disregarded or not */
/******************************************************************/
/* Input: fid - NetCDF file ID                                    */
/*        varid - NetCDF variable ID                              */
/* Output: fillvalue - Fill Value                                 */
/******************************************************************/
int Read_NetCDF_Fill( int fid, int varid, double *fillvalue)
{
   int status;

   status = nc_get_att_double( fid, varid, "_FillValue", fillvalue);
   if (status == NC_ENOTATT){
      *fillvalue = NC_FILL_DOUBLE;
   }
   else if (status != NC_NOERR){
      return 0;
   }
   return 1;
}
      

/*******************************************************************/
/* This will get the NetCDF record ID's for a specific time. These */
/* rec ID's indexed to [0..numrecs(time)] for future calls         */
/*******************************************************************/
/* Input: finfo - the NetCDF format info                           */
/*        nc_id - NetCDF file ID                                   */
/*        thetime - the NetCDF time, in seconds since              */
/*                  1970-01-01 00 UTC                              */
/* Output: ids - an array of rec IDs belonging to thetime          */
/*******************************************************************/
int Read_NetCDF_Record_IDs( NetCDF_Format_Info finfo,
                                  int nc_id, int thetime, int *ids)
{
   int  *times, status;
   int tnid, i, idcount, numrecsid;
   size_t numrecs;


   /*************************/
   /* get number of records */
   /*************************/
   status = nc_inq_dimid(nc_id, finfo->REC_NUM, &numrecsid);
   if(status != NC_NOERR){
      return 0;
   }
   status = nc_inq_dimlen(nc_id, numrecsid, &numrecs);
   if(status != NC_NOERR){
      return 0;
   }

   /*************/
   /* get times */
   /*************/
   status = nc_inq_varid( nc_id, finfo->TIME, &tnid);
   if(status != NC_NOERR){
      return 0;
   }

   times = (int *) malloc(sizeof(int)*numrecs);
   if (!times){
      return 0;
   }

   status = nc_get_var_int( nc_id, tnid, times);
   if(status != NC_NOERR){
      free(times);
      return 0;
   }


   /**********************************************/
   /* get the station rec num  matching the time */
   /* assume no duplicates for now               */
   /**********************************************/
   idcount = 0;
   for (i = 0; i < numrecs; i++){
      if (times[i] == thetime){
         ids[idcount] = i;
         idcount++;
      }
   }
   /*
   *rpt = idcount;

   for (i = idcount; i < num_ids; i++){
      ids[i] = -1;
   }
   */
   free(times);
   return 1;
}

/*********************************************************/
/* This will determine how many time steps there are and */
/* how many record for each time step.                   */
/*********************************************************/
/* Input: finfo - the NetCDF format info                 */
/*        nc_id - the NetCDF file ID                     */
/* Output: num_times - the number of time steps for the  */
/*                     file                              */
/*         ts - array of time stamps,size [0..num_times] */
/*              in HHMMSS                                */
/*         ds - array of date stamps,size [0..num_times] */
/*              in YYDDD                                 */
/*         time - array of times, size [ 0..num_times]   */
/*                is seconds since 1970-01-01 00 UTC     */
/*         trecs - array containing number of records    */
/*                 per time step                         */
/*********************************************************/
int Read_NetCDF_Times_and_Recs( NetCDF_Format_Info finfo,
                                      int nc_id, int *num_times, int *ts, int *ds,
                                      int time[], int trecs[])
{
   int numrecsid;
   int nt, ntimes[MAXTIMES], uniquetimes[MAXTIMES];
   int numuniquetimes;
   int status, tnid, *times, temp, i, j, t;
   int year, day, hour, minute, second;
   size_t numrecs;

   *num_times = 0;

   /*************************/
   /* get number of records */
   /*************************/
   status = nc_inq_dimid(nc_id, finfo->REC_NUM, &numrecsid);
   if(status != NC_NOERR){
      return 0;
   }
   status = nc_inq_dimlen(nc_id, numrecsid, &numrecs);
   if(status != NC_NOERR){
      return 0;
   }


   /****************/
   /* get numtimes */
   /****************/
   for (i = 0; i < MAXTIMES; i++){
      uniquetimes[i] = -1;
   }

   status = nc_inq_varid( nc_id, finfo->TIME, &tnid);
   if(status != NC_NOERR){
      return 0;
   }

   times = (int *) malloc(sizeof(int)*numrecs);
   if (!times){
      return 0;
   }

   status = nc_get_var_int( nc_id, tnid, times);
   if(status != NC_NOERR){
      free(times);
      return 0;
   }

   numuniquetimes = 1;
   uniquetimes[0] = times[0];
   ntimes[0] = 1;
   for (i = 1; i < numrecs; i++){
      for (j = 0; j < numuniquetimes; j++){
         if (times[i] == uniquetimes[j]){
            ntimes[j]++;
            break;
         }
      }
      if (j == numuniquetimes){
         uniquetimes[j] = times[i];
         ntimes[j] = 1;
         numuniquetimes++;
      }
   }
   *num_times = numuniquetimes;
   nt = numuniquetimes;

   /***********************************************/
   /* get the largest number of record for a time */
   /***********************************************/
   /*
   nrecs = 0;
   for (i = 0; i < nt; i++){
      if (ntimes[i] > nrecs){
         nrecs = ntimes[i];
      }
   }
   *mrecs = nrecs; 
   */

   /****************************/   
   /* bubble sort unique times */
   /****************************/
   for (i = 0; i < numuniquetimes; i++){
      for (j = 0; j < numuniquetimes-1-i; j++){
         if (uniquetimes[j+1] < uniquetimes[j]){
            temp = uniquetimes[j];
            uniquetimes[j] = uniquetimes[j+1];
            uniquetimes[j+1] = temp;
            temp = ntimes[j];
            ntimes[j] = ntimes[j+1];
            ntimes[j+1] = temp;
         }
      }
   }
   for (i = 0; i < numuniquetimes; i++){
      trecs[i] = ntimes[i];
   } 

   /***********************/
   /* get day/time stamps */
   /***********************/
   for (i=0; i < nt; i++){
      t = uniquetimes[i];
      time[i] = t;
      day = t/86400;
      t -= 86400 * day;
      if (day > 730){
         day  -= 730;
         year  = (4*day)/1461;
         day   = day - (365*year+(year-1)/4);
         year += 72;
      }
      else{
         year  = day / 365;
         day   = day - (365*year);
      }
      hour = t/3600;
      t -= 3600*hour;
      minute = t/60;
      t -= 60*minute;
      second = t;
      ts[i] = 10000*hour+100*minute+second;
      ds[i] = 1000*year+day;
   }
   free(times);
   return 1;
}

/****************************************************/
/* This will get the number of levels from a NetCDF */
/* file containing 2D or sounding data              */
/****************************************************/
/* Input: finfo - the NetCDF format info            */
/*        nc_id - NetCDF file ID                    */
/* Output: numlevels - the number of levels each    */
/*                     record will have             */
/****************************************************/
int Read_NetCDF_Num_of_Levels( NetCDF_Format_Info finfo,
                              int nc_id, int *numlevels)
{
   int status;
   int levelid;
   size_t nl;

   status = nc_inq_dimid( nc_id, finfo->LEVELDIM, &levelid);
   if(status != NC_NOERR){
      return 0;
   }
   status = nc_inq_dimlen(nc_id, levelid, &nl);
   if(status != NC_NOERR){
      return 0;
   }
   *numlevels = (int) nl;
   return 1;
}

/****************************************************/
/* This will read the height for each of the levels */
/* in a 2D or sounding NetCDF file in meters        */
/****************************************************/
/* Input: finfo - the NetCDF format info            */
/*        nc_id - NetCDF file ID                    */
/*        recid - the record number of ID           */
/*        numlevels - the number of levels to get   */
/*                    the height for                */
/* Output: data - an array containing the height    */
/*                in meters for each level          */
/****************************************************/
int Read_NetCDF_Levels( NetCDF_Format_Info finfo,
                              int nc_id, int recid, int numlevels, float *data)
{
   int status;
   int levelid;
   static size_t tstart[2], tend[2];

   status = nc_inq_varid( nc_id, finfo->LEVELVAR, &levelid);
   if(status != NC_NOERR){
      return 0;
   }

   tstart[0] =  recid;
   tstart[1] =  0;
   tend[0] = 1;
   tend[1] =  numlevels;

   status = nc_get_vara_float(nc_id, levelid, tstart, tend, data);
   if(status != NC_NOERR){
      return 0;
   }
   return 1;
}

/****************************************************/
/* This will look through all the records in a      */
/* NetCDF file and determine the bounding region    */
/* which will be used to determine the vis5dboudries*/
/****************************************************/
/* Input: finfo - the NetCDF format info            */
/*        nc_id - NetCDF file ID                    */
/* Output: west, east, north, south - the horizontal*/
/*                boundries in degrees              */
/*         top, bottom - the vertical boundries in  */
/*                kilometers above sea level        */
/****************************************************/
int Read_NetCDF_Bounds( NetCDF_Format_Info finfo,
                              int nc_id, float *west, float *east,
                              float *north, float *south, float *top, float *bottom)
{
   int i, status, latid, lonid, hgtid, numrecsid;
   size_t numrecs;
   float nb, sb, wb, eb, tb, bb;
   float *latdata, *londata, *hgtdata;
   float fill_value;

   /*************************/
   /* get number of records */
   /*************************/
   status = nc_inq_dimid(nc_id, finfo->REC_NUM, &numrecsid);
   if(status != NC_NOERR){
      return 0;
   }
   status = nc_inq_dimlen(nc_id, numrecsid, &numrecs);
   if(status != NC_NOERR){
      return 0;
   }

   /*****************************/
   /* malloc lat, lon, hgt data */
   /*****************************/
   latdata = (float *) malloc(numrecs * sizeof(float));
   if (!latdata){
      printf("couldn't allocate enough memory\n");
      return 0;
   }
   londata = (float *) malloc(numrecs * sizeof(float));
   if (!londata){
      printf("couldn't allocate enough memory\n");
      free(latdata);
      return 0;
   }
   hgtdata = (float *) malloc(numrecs * sizeof(float));
   if (!hgtdata){
      printf("couldn't allocate enough memory\n");
      free(latdata);
      free(londata);
      return 0;
   }


   status = nc_inq_varid( nc_id, finfo->LAT, &latid);
   if(status != NC_NOERR){
      free(latdata);
      free(londata);
      free(hgtdata);
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->LON, &lonid);
   if(status != NC_NOERR){
      free(latdata);      
      free(londata);      
      free(hgtdata);      
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->HGT, &hgtid);
   if(status != NC_NOERR){
      free(latdata);      
      free(londata);      
      free(hgtdata);      
      return 0;
   }

   status = nc_get_att_float( nc_id, latid, finfo->LOCATION_FILL, &fill_value);
   if(status != NC_NOERR){
      free(latdata);      
      free(londata);      
      free(hgtdata);      
      return 0;
   }

   /************************************************/
   /* get the lat, lon, hgt data in one big chunck */
   /************************************************/
   status = nc_get_var_float( nc_id, latid, latdata);
   if(status != NC_NOERR){
      printf("error getting bounds\n");
      free(latdata);
      free(londata);
      free(hgtdata);
      return 0;
   }
   status = nc_get_var_float( nc_id, lonid, londata);
   if(status != NC_NOERR){
      printf("error getting bounds\n");
      free(latdata);
      free(londata);
      free(hgtdata);
      return 0;
   }
   status = nc_get_var_float( nc_id, hgtid, hgtdata);
   if(status != NC_NOERR){
      printf("error getting bounds\n");
      free(latdata);
      free(londata);
      free(hgtdata);
      return 0;
   }

   
   nb = -180.0;
   sb = 180.0;
   eb = -180.0;
   wb = 180.0;
   tb = -10000.0;
   bb =  10000.0;

   for (i = 0; i < numrecs; i++){
      if (latdata[i] != fill_value){
         if (latdata[i] > nb){
            nb = latdata[i];
         }
         if (latdata[i] < sb){
            sb = latdata[i];
         }
      }
   }

   for (i = 0; i < numrecs; i++){
      if (londata[i] != fill_value){
         if (londata[i] > eb){
            eb = londata[i];
         }
         if (londata[i] < wb){
            wb = londata[i];
         }
      }
   }
   for (i = 0; i < numrecs; i++){
      if (hgtdata[i] != fill_value){
         if (hgtdata[i] > tb){
            tb = hgtdata[i];
         }
         if (hgtdata[i] < bb){
            bb = hgtdata[i];
         }
      }
   }
   *west = -wb;
   *east = -eb;
   *north = nb;
   *south = sb;
   *top = tb/1000.0;
   *bottom = bb/1000.0;

   free(latdata);
   free(londata);
   free(hgtdata);

   return 1;
}
   
/****************************************************/
/* This will get information about the variables in */
/* a NetCDF file                                    */
/****************************************************/
/* Input: finfo - the NetCDF format info            */
/*        nc_id - NetCDF file ID                    */
/* Output: numvars - the number of variable         */
/*         varname - an array of the variable names */
/*         vartype - this array of size [0..numvars */
/*                   contains the variable type,    */
/*                   one of...                      */
/*                   CHAR_VAR                       */
/*                   INT_VAR                        */
/*                   FLOAT_VAR                      */
/*                   DOUBLE_VAR                     */
/*         vardim - this array of size [0..numvars] */
/*         contains the dimension of the variable   */
/*                  which is used to determine how  */
/*                  to read it from the NetCDF file */
/*                  later.  One of...               */
/*                   VAR_1D                         */
/*                   VAR_2D                         */
/*                   VAR_3D                         */
/*                   VAR_4D                         */
/*         charvarlength - this array of size       */
/*                [0..num_vars] contains the        */
/*                 length of the char string for    */
/*                 each of the variables.  If the   */
/*                 vartype is CHAR_VAR it will be   */
/*                 > 0 if vartype != CHAR_VAR then  */
/*                 it will be 0 length              */
/*         varmin, varmax - this array of size      */
/*               [0..num_vars] contains the         */
/*               variable min and max for each of   */
/*               the variables if vartype !=CHAR_VAR*/
/****************************************************/
int Read_NetCDF_Vars( NetCDF_Format_Info finfo,
                            int nc_id,  int *num_vars,
                            char varname[MAXVARS][MAX_VAR_LENGTH],
                            int vartype[], int vardim[], int charvarlength[],
                            double varmin[MAXVARS], double varmax[MAXVARS])
{
   int status, numvars, var_offset ;
   int numdims, cloud_layer_id;
   int numrecsid;
   int s, i, k, c, latid, lonid, hgtid;
   int dimids[10];
   nc_type type;
   size_t numrecs, stringsize, num_cloud_layers;
   char vname[1000];
   double *mmdata;
   double fillvalue;
   static size_t start[2];
 

   status = nc_inq_nvars( nc_id, &numvars);
   if(status != NC_NOERR){
      return 0;
   }

   /*************************/
   /* get number of records */
   /*************************/
   status = nc_inq_dimid(nc_id, finfo->REC_NUM, &numrecsid);
   if(status != NC_NOERR){
      return 0;
   }
   status = nc_inq_dimlen(nc_id, numrecsid, &numrecs);
   if(status != NC_NOERR){
      return 0;
   }
   /***********************************/
   /* allocate min max data array now */
   /***********************************/
   mmdata = (double *) malloc(numrecs * sizeof(double));
   if (!mmdata){
      printf("couldn't allocate min max data array\n");
      return 0;
   }


   /*****************************************************/
   /* check to make sure lat, lon and hgt are there     */
   /*****************************************************/
   status = nc_inq_varid( nc_id, finfo->LAT, &latid);
   if(status != NC_NOERR){
      printf("Could not find lat var\n");
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->LON, &lonid);
   if(status != NC_NOERR){
      printf("Could not find lon var\n");
      return 0;
   }
   status = nc_inq_varid( nc_id, finfo->HGT, &hgtid);
   if(status != NC_NOERR){
      printf("Could not find hgt var\n");
      return 0;
   }
   var_offset = 0;


   /******************************/
   /* get cloud layers if needed */
   /******************************/
   if (finfo == FSL_METAR || finfo == UNIDATA_METAR){
      num_cloud_layers = 0;
      cloud_layer_id = -1;
      status = nc_inq_dimid(nc_id, finfo->METAR_CLD_LAYERS, &cloud_layer_id);
      if(status != NC_NOERR){
         printf("no cloud_layers dimension\n");
      }
      else{
         status = nc_inq_dimlen(nc_id, cloud_layer_id, &num_cloud_layers);
         if(status != NC_NOERR){
            printf("no cloud_layers dimension\n");
            return 0;
         }
         if (num_cloud_layers < 1 ||
             num_cloud_layers > 9){
            printf("number of cloud layers must be between 1..9\n");
            return 0;
         }
      }
   }
 
   
   if (finfo == FSL_METAR || finfo == UNIDATA_METAR){
      for (i = 0; i < numvars; i++){
         k = i - var_offset;
         /****************/      
         /* get var name */
         /****************/
         status = nc_inq_varname( nc_id, i, vname);
         if(status != NC_NOERR){
            return 0;
         }
         /****************/      
         /* get var type */
         /****************/      
         status = nc_inq_vartype( nc_id, i, &type);
         if(status != NC_NOERR){
            return 0;
         }
         if (strncmp(finfo->LAT, vname, strlen(finfo->LAT)) != 0 &&
                  strncmp(finfo->LON, vname, strlen(finfo->LON)) != 0 &&
                  strncmp(finfo->HGT, vname, strlen(finfo->HGT)) != 0){
            if (strlen(vname) < MAX_VAR_LENGTH){
               strncpy(varname[k], vname, MAX_VAR_LENGTH);
               /********************************/            
               /* get number of var dimensions */
               /********************************/
               status = nc_inq_varndims( nc_id, i, &numdims);
               if(status != NC_NOERR){
                  printf("error getting numdims\n");
                  return 0;
               }
               /**************************/            
               /* get var dimension id's */
               /**************************/
               status = nc_inq_vardimid( nc_id, i, dimids);
               if(status != NC_NOERR){
                  printf("error getting dimids\n");
                  return 0;
               }
               /************/
               /* char var */
               /************/
               if (type == NC_CHAR){
                  /****************/
                  /* 3 dimensions */
                  /****************/
                  if (numdims == 3){
                     /*************************************/
                     /* Must be cloud layers, create vars */
                     /*************************************/
                     varname[k][strlen(varname[k])] = '0';
                     for (c = 1; c < num_cloud_layers; c++){
                        strncpy(varname[k+c], vname, MAX_VAR_LENGTH);
                        varname[k+c][strlen(varname[k+c])] = c + '0';
                        var_offset--;
                     }
                     status = nc_inq_dimlen( nc_id, dimids[2], &stringsize);
                     if(status != NC_NOERR){
                        printf("error getting stringsize\n");
                        return 0;
                     }
                     for (c = 0; c < num_cloud_layers; c++){
                        charvarlength[k+c] = stringsize;
                        vartype[k+c] = CHAR_VAR;
                        vardim[k+c] = VAR_2D;
                     }
                  }
                  /****************/
                  /* 2 dimensions */
                  /****************/
                  else if (numdims == 2){
                     status = nc_inq_dimlen( nc_id, dimids[1], &stringsize);
                     if(status != NC_NOERR){
                        printf("error getting stringsize\n");
                        return 0;
                     }
                     charvarlength[k] = (int) stringsize;
                     vartype[k] = CHAR_VAR;
                     vardim[k]  = VAR_1D;
                  }
                  /***************************/
                  /* 1, 4 or more dimensions */
                  /***************************/
                  else{
                     printf("don't know what to do with a char %d dimensional var\n", numdims);
                     return 0;
                  }
               }
               /***********/
               /* num var */
               /***********/
               else{
                  if (type == NC_BYTE ||
                      type == NC_SHORT ||
                      type == NC_INT){
                     vartype[k] = INT_VAR;
                  }
                  else if (type == NC_FLOAT){
                     vartype[k] = FLOAT_VAR;
                  }
                  else if (type == NC_DOUBLE){
                     vartype[k] = DOUBLE_VAR;
                  }
                  else{
                     printf("something wrong with the type\n");
                     exit(0);
                  }
                  /******************/
                  /* get fill value */
                  /******************/
                  status = nc_get_att_double( nc_id, i, finfo->LOCATION_FILL, &fillvalue);
                  if(status != NC_NOERR){
                     printf("ccouldn't get fill value\n");
                     return 0;
                  }       
                  /****************/
                  /* 2 dimensions */
                  /****************/
                  if (numdims == 2){
                     /************************/
                     /* must be cloud height */
                     /************************/
                     if (dimids[1] != cloud_layer_id){
                        printf("error with var second dimension\n");
                        return 0;
                     }
                     varname[k][strlen(varname[k])] = '0';
                     vardim[k] = VAR_2D;
                     for (c = 1; c < num_cloud_layers; c++){
                        strncpy(varname[k+c], vname, MAX_VAR_LENGTH);
                        varname[k+c][strlen(varname[k+c])] = c + '0';
                        var_offset--;
                        charvarlength[k+c] = 0;
                        vartype[k+c] = vartype[k];
                        vardim[k+c] = vardim[k];
                     }
                     /*******************/
                     /* get min and max */
                     /*******************/
                     for (c = 0; c < num_cloud_layers; c++){
                        for (s = 0; s < numrecs; s++){
                           start[0] = s;
                           start[1] = 0;
                           nc_get_var1_double( nc_id, i, start, mmdata+s);
                        }
                        get_min_and_max( numrecs, mmdata, fillvalue, &varmin[k+c],
                                         &varmax[k+c]);
                     }   
                  }
                  /****************/
                  /* 1 dimension  */
                  /****************/
                  else if (numdims == 1){ 
                     vardim[k] = VAR_1D;
                     charvarlength[k] = 0;
                     for (s = 0; s < numrecs; s++){
                        start[0] = s;
                        nc_get_var1_double( nc_id, i, start, mmdata+s);
                     }
                     get_min_and_max( numrecs, mmdata, fillvalue, &varmin[k],
                                      &varmax[k]);
							
                  }
                  /******************************/   
                  /* other number of dimesnions */
                  /******************************/
                  else{
                     printf("don't know what to do with an number %d dimesional var\n", numdims);
                     return 0;
                  }
               }
            }
            /******************************************/
            /* var is too long excluding the variable */
            /******************************************/
            else{
               printf("variable %s is too long, ommitting it\n", vname);
               var_offset++;
            }
         }
         else{
            var_offset++;
         }
      }
      *num_vars = k;
      return 1;
   }
   else if (finfo == FSL_PROFILE){
      for (i = 0; i < numvars; i++){
         k = i - var_offset;
         /****************/
         /* get var name */
         /****************/
         status = nc_inq_varname( nc_id, i, vname);
         if(status != NC_NOERR){
            return 0;
         }
         /****************/
         /* get var type */
         /****************/
         status = nc_inq_vartype( nc_id, i, &type);
         if(status != NC_NOERR){
            return 0;
         }
         if (strncmp(finfo->LAT, vname, strlen(finfo->LAT)) != 0 &&
               strncmp(finfo->LON, vname, strlen(finfo->LON)) != 0 &&
               strncmp(finfo->HGT, vname, strlen(finfo->HGT)) != 0 &&
               strncmp(finfo->LEVELVAR, vname, strlen(finfo->LEVELVAR)) != 0){
            if (strlen(vname) < MAX_VAR_LENGTH){
               strncpy(varname[k], vname, MAX_VAR_LENGTH);
               /********************************/
               /* get number of var dimensions */
               /********************************/
               status = nc_inq_varndims( nc_id, i, &numdims);
               if(status != NC_NOERR){
                  printf("error getting numdims\n");
                  return 0;
               }
               /**************************/
               /* get var dimension id's */
               /**************************/
               status = nc_inq_vardimid( nc_id, i, dimids);
               if(status != NC_NOERR){
                  printf("error getting dimids\n");
                  return 0;
               }
               /************/
               /* char var */
               /************/
               if (type == NC_CHAR){
                  /****************/
                  /* 2 dimensions */
                  /****************/
                  if (numdims == 2){
                     status = nc_inq_dimlen( nc_id, dimids[1], &stringsize);
                     if(status != NC_NOERR){
                        printf("error getting stringsize\n");
                        return 0;
                     }
                     charvarlength[k] = (int) stringsize;
                     vartype[k] = CHAR_VAR;
                     vardim[k]  = VAR_1D;
                  }
                  /******************************/
                  /* 1, 3, 4 or more dimensions */
                  /******************************/
                  else{
                     printf("don't know what to do with a char %d dimensional var\n", numdims);
                     return 0;
                  }
               }
               /***********/
               /* num var */
               /***********/
               else{
                  if (type == NC_BYTE ||
                      type == NC_SHORT ||
                      type == NC_INT){
                     vartype[k] = INT_VAR;
                  }
                  else if (type == NC_FLOAT){
                     vartype[k] = FLOAT_VAR;
                  }
                  else if (type == NC_DOUBLE){
                     vartype[k] = DOUBLE_VAR;
                  }
                  else{
                     printf("something wrong with the type\n");
                     exit(0);
                  }
                  /****************/
                  /* 2 dimensions */
                  /****************/
                  if (numdims == 2){
                     /*******************************/
                     /* must be a sounding data var */
                     /*******************************/
                    vardim[k] = VAR_2D;
                    charvarlength[k] = 0;
                  }
                  /****************/
                  /* 1 dimension  */
                  /****************/
                  else if (numdims == 1){
                    vardim[k] = VAR_1D;
                    charvarlength[k] = 0;
                  }
                  /******************************/
                  /* other number of dimesnions */
                  /******************************/
                  else{
                     printf("don't know what to do with an number %d dimesional var\n", numdims);
                     return 0;
                  }
               }
            }
            /******************************************/
            /* var is too long excluding the variable */
            /******************************************/
            else{
               printf("variable %s is too long, ommitting it\n", vname);
               var_offset++;
            }
         }
         else{
            var_offset++;
         }
      }
      *num_vars = k;
      free(mmdata);
      return 1;
   }
	return 0;
}

#endif /* HAVE_LIBNETCDF */


syntax highlighted by Code2HTML, v. 0.9.1