/*********************************************************************
 *   Copyright 2018, University Corporation for Atmospheric Research
 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
 *   
 *   Stuff that's common to both ncdump and nccopy
 *
 *********************************************************************/
#ifndef _UTILS_H
#define _UTILS_H

#include "config.h"

#ifndef NCSTREQ
#define	NCSTREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
#endif

/* Delimiter for separating netCDF groups in absolute pathnames, same as for HDF5 */
#define NC_GRP_DELIM '/'

typedef int bool_t;
enum {false=0, true=1};

struct safebuf_t;
/* Buffer structure for implementing growable strings, used in
 * preventing buffer overflows when the size needed for a character
 * buffer cannot be easily predicted or limited to any specific
 * maximum, such as when used in recursive function calls for nested
 * vlens and nested compound types. */
typedef struct safebuf_t {
    size_t len;			/* current length of buffer */
    size_t cl;			/* current length of string in buffer, < len-1 */
    char *buf;
} safebuf_t;

/* structure for list of ids, such as varids or grpids specified with -v or -g option */
typedef struct idnode {
    struct idnode* next;
    int id;
} idnode_t;

/* node in stack of group ids */
typedef struct grpnode {
    int grpid;
    struct grpnode *next;
} grpnode_t;

/* 
 * The opaque structure to hold state of iteration over groups.
 * (Just implemented as a stack of group ids.)
 */
typedef struct {
    int ngrps;			/* number of groups left to visit */
    grpnode_t *top;		/* group ids left to visit */
} ncgiter_t;

extern char *progname;		/* for error messages */

#ifndef NO_NETCDF_2
#define NO_NETCDF_2		/* assert we aren't using any netcdf-2 stuff */
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* For NDEBUG builds, provide a version of NC_CHECK that does not
 * include a file name. Including a file name causes heartache for the
 * debian package builders. They already use NDEBUG to turn off the
 * file names in asserts. */
#ifdef NDEBUG
#define NC_CHECK(fncall) {int ncstat=fncall;if(ncstat!=NC_NOERR)check(ncstat,"",__LINE__);}
#else
#define NC_CHECK(fncall) {int ncstat=fncall;if(ncstat!=NC_NOERR)check(ncstat,__FILE__,__LINE__);}
#endif /* NDEBUG */

/* Print error message to stderr and exit */
extern void	error ( const char *fmt, ... );

/* Check error on malloc and exit with message if out of memory */
extern void*    emalloc ( size_t size );
/* Ditto calloc */
extern void*    ecalloc ( size_t size );
/* Ditto realloc */
extern void*    erealloc (void* p, size_t size );

/* Check error return.  If bad, print error message and exit. */
extern void check(int err, const char* file, const int line);

/* Return malloced name with chars special to CDL escaped. */
char* escaped_name(const char* cp);

/* Print name of netCDF var, dim, att, group, type, member, or enum
 * symbol with escaped special chars */
void print_name(const char *name);

/* Get dimid from a full dimension path name that may include group
 * names */
extern int  nc_inq_dimid2(int ncid, const char *dimname, int *dimidp);

/* Convert a full path name to a group to the specific groupid. */
extern int  nc_inq_grpid2(int ncid, const char *grpname0, int *grpidp);

/* Convert a full path name to a varid to the specific varid + grpid */
extern int nc_inq_varid2(int ncid, const char *path0, int* varidp, int* grpidp);

/* Test if variable is a record variable */
extern int  isrecvar ( int ncid, int varid );

/* Get a new, empty id list. */
extern idnode_t* newidlist(void);

/* Add id to id list  */
extern void idadd(idnode_t* idlist, int id);

/* Test if id is in id list */
extern bool_t	idmember ( const idnode_t* idlist, int id );

/* Test if a group id is in group list */
extern bool_t	group_wanted ( int grpid, int nlgrps, const idnode_t* grpids );

/* Check group list for missing groups */
extern int grp_matches(int ncid, int nlgrps, char** lgrps, idnode_t *grpids);

/* Returns 1 if string s1 ends with string s2, 0 otherwise. */
extern int strendswith(const char *s1, const char *s2);

/* Within group with id ncid, get varid of variable with name varname
 * using nested group syntax "gp1/gp2/var" */
extern int nc_inq_gvarid ( int ncid, const char *varname, int *varidp );

/* Get variable id varid within group grpid using absolute or relative pathname for variable */
    extern int nc_inq_gvarid(int grpid, const char *varname, int *varidp);

/* Return how many variables are named varname in any groups in ncid */
extern size_t nc_inq_varname_count(int ncid, char *varname);

/* Check if any variable names specified in the list lvars (of length nlvars) are missing. */
extern int missing_vars(int ncid, int nlvars, char **lvars);

/* Make list of variables from comma-delimited string */
extern void make_lvars(char *optarg, int *nlvarsp, char ***lvarsp);

/* Make list of groups from comma-delimited string */
extern void make_lgrps(char *optarg, int *nlgrpsp, char*** lgrpsp, idnode_t **grpidsp);

/* Release an id list */
extern void freeidlist(idnode_t *idlist);

/* 
 * Simplest interface for group iteration: get total number of groups
 * (including all descendant groups, recursively) and all group ids
 * for start group and its descendants, in preorder.  Note that this
 * loses information about subgroup relationships, just flattening all
 * groups into a serial list.
 */
extern int nc_inq_grps_full(int ncid, int *numgrps, int *ncids);

/* 
 * More complex iterator interface: get group iterator for start group
 * ncid and all its descendant groups.
 */
extern int nc_get_giter(int ncid, ncgiter_t **iterp);

/* 
 * Get group id of next group.  On first call returns start group,
 * subsequently returns other subgroup ids in preorder.  Returns grpid
 * of 0 (never an actual group number) when no more groups.
 */
extern int nc_next_giter(ncgiter_t *iterp, int *grpid);

/*
 * Release memory allocated for group iterator.
 */
extern void nc_free_giter(ncgiter_t *iterp);
extern int getrootid(int grpid);

#ifdef __cplusplus
}
#endif

#endif /* _UTILS_H */