/*********************************************************************
 *   Copyright 1992, University Corporation for Atmospheric Research
 *   See netcdf/README file for copying and redistribution conditions.
 *
 *   Purpose:   C++ class interface for netCDF
 *
 *   $Header: /upc/share/CVS/netcdf-3/cxx/netcdf.hh,v 1.6 1997/11/19 20:33:31 russ Exp $
 *********************************************************************/

#ifndef NETCDF_HH
#define NETCDF_HH

extern "C" {
#include "netcdf.h"             // the C interface
}
#include "ncvalues.hh"          // arrays that know their element type

typedef const char* NcToken;    // names for netCDF objects
typedef unsigned int NcBool;    // many members return 0 on failure

class NcDim;                    // dimensions
class NcVar;                    // variables
class NcAtt;                    // attributes


/*
 * ***********************************************************************
 * A netCDF file.
 * ***********************************************************************
 */
class NcFile
{
  public:

    virtual ~NcFile( void );

    enum FileMode {
	ReadOnly,	// file exists, open read-only
	Write,		// file exists, open for writing
        Replace,	// create new file, even if already exists
	New		// create new file, fail if already exists
      };

    NcFile( const char * path, FileMode = ReadOnly );

    NcBool is_valid( void ) const;         // opened OK in ctr, still valid

    int num_dims( void ) const;            // number of dimensions
    int num_vars( void ) const;            // number of variables
    int num_atts( void ) const;            // number of (global) attributes

    NcDim* get_dim( NcToken ) const;       // dimension by name
    NcVar* get_var( NcToken ) const;       // variable by name
    NcAtt* get_att( NcToken ) const;       // global attribute by name

    NcDim* get_dim( int ) const;           // n-th dimension
    NcVar* get_var( int ) const;           // n-th variable
    NcAtt* get_att( int ) const;           // n-th global attribute
    NcDim* rec_dim( void ) const;          // unlimited dimension, if any
    
    // Add new dimensions, variables, global attributes.
    // These put the file in "define" mode, so could be expensive.
    NcDim* add_dim( NcToken dimname, long dimsize );
    NcDim* add_dim( NcToken dimname );     // unlimited

    NcVar* add_var( NcToken varname, NcType type,       // scalar
                    const NcDim* dim0=0,                // 1-dim
                    const NcDim* dim1=0,                // 2-dim
                    const NcDim* dim2=0,                // 3-dim
                    const NcDim* dim3=0,                // 4-dim
                    const NcDim* dim4=0 );              // 5-dim
    NcVar* add_var( NcToken varname, NcType type,       // n-dim
                          int ndims, const NcDim** dims );

    NcBool add_att( NcToken attname, char );             // scalar attributes
    NcBool add_att( NcToken attname, ncbyte );
    NcBool add_att( NcToken attname, short );
    NcBool add_att( NcToken attname, long );
    NcBool add_att( NcToken attname, int );
    NcBool add_att( NcToken attname, float );
    NcBool add_att( NcToken attname, double );
    NcBool add_att( NcToken attname, const char*);       // string attribute
    NcBool add_att( NcToken attname, int, const char* ); // vector attributes
    NcBool add_att( NcToken attname, int, const ncbyte* );
    NcBool add_att( NcToken attname, int, const short* );
    NcBool add_att( NcToken attname, int, const long* );
    NcBool add_att( NcToken attname, int, const int* );
    NcBool add_att( NcToken attname, int, const float* );
    NcBool add_att( NcToken attname, int, const double* );

    enum FillMode {
        Fill = NC_FILL,                    // prefill (default)
        NoFill = NC_NOFILL,                // don't prefill
        Bad
      };

    NcBool set_fill( FillMode = Fill );    // set fill-mode
    FillMode get_fill( void ) const;       // get fill-mode

    NcBool sync( void );                   // synchronize to disk
    NcBool close( void );                  // to close earlier than dtr
    NcBool abort( void );                  // back out of bad defines
    
    // Needed by other Nc classes, but users will not need them
    NcBool define_mode( void ); // leaves in define mode, if possible
    NcBool data_mode( void );   // leaves in data mode, if possible
    int id( void ) const;       // id used by C interface

  protected:
    int the_id;
    int in_define_mode;
    NcDim** dimensions;
    NcVar** variables;
    NcVar* globalv;             // "variable" for global attributes
};

/*
 * For backward compatibility.  We used to derive NcOldFile and NcNewFile
 * from NcFile, but that was over-zealous inheritance.
 */
#define NcOldFile NcFile
#define NcNewFile NcFile
#define Clobber Replace
#define NoClobber New

/*
 * **********************************************************************
 * A netCDF dimension, with a name and a size.  These are only created
 * by NcFile member functions, because they cannot exist independently
 * of an open netCDF file.
 * **********************************************************************
 */
class NcDim
{
  public:
    NcToken name( void ) const;
    long size( void ) const;
    NcBool is_valid( void ) const;
    NcBool is_unlimited( void ) const;
    NcBool rename( NcToken newname );
    int id( void ) const;
    NcBool sync( void );

  private:
    NcFile *the_file;		// not const because of rename
    int the_id;
    char *the_name;

    NcDim(NcFile*, int num);	// existing dimension
    NcDim(NcFile*, NcToken name, long sz); // defines a new dim
    virtual ~NcDim( void );
    
    // to construct dimensions, since constructor is private
  friend NcFile::NcFile( const char *, NcFile::FileMode );
  friend NcBool NcFile::sync( void );
  friend NcDim* NcFile::add_dim( NcToken, long );
    // to delete dimensions, since destructor is private
  friend NcBool NcFile::close( void );
};


/*
 * **********************************************************************
 * Abstract base class for a netCDF variable or attribute, both of which
 * have a name, a type, and associated values.  These only exist as
 * components of an open netCDF file.
 * **********************************************************************
 */
class NcTypedComponent
{
  public:
    virtual NcToken name( void ) const = 0;
    virtual NcType type( void ) const = 0;
    virtual NcBool is_valid( void ) const = 0;
    virtual long num_vals( void ) const = 0; 
    virtual NcBool rename( NcToken newname ) = 0;
    virtual NcValues* values( void ) const = 0; // block of all values

    // The following member functions provide conversions from the value
    // type to a desired basic type.  If the value is out of range,
    // the default "fill-value" for the appropriate type is returned.

    virtual ncbyte as_ncbyte( long n ) const;    // nth value as an unsgnd char
    virtual char as_char( long n ) const;        // nth value as char
    virtual short as_short( long n ) const;      // nth value as short
    virtual int as_int( long n ) const;	         // nth value as int
    virtual int as_nclong( long n ) const;       // nth value as nclong (deprecated)
    virtual long as_long( long n ) const;        // nth value as long
    virtual float as_float( long n ) const;      // nth value as floating-point
    virtual double as_double( long n ) const;    // nth value as double
    virtual char* as_string( long n ) const;     // nth value as string

  protected:
    NcFile *the_file;
    NcTypedComponent( NcFile* );
    virtual NcValues* get_space( long numVals = 0 ) const;  // to hold values
};


/*
 * **********************************************************************
 * netCDF variables.  In addition to a name and a type, these also have
 * a shape, given by a list of dimensions
 * **********************************************************************
 */
class NcVar : public NcTypedComponent
{
  public:
    virtual ~NcVar( void );
    NcToken name( void ) const;
    NcType type( void ) const;
    NcBool is_valid( void ) const;
    int num_dims( void ) const;         // dimensionality of variable
    NcDim* get_dim( int ) const;        // n-th dimension
    long* edges( void ) const;          // dimension sizes
    int num_atts( void ) const;         // number of attributes
    NcAtt* get_att( NcToken ) const;    // attribute by name
    NcAtt* get_att( int ) const;        // n-th attribute
    long num_vals( void ) const;        // product of dimension sizes
    NcValues* values( void ) const;     // all values
    
    // Put scalar or 1, ..., 5 dimensional arrays by providing enough
    // arguments.  Arguments are edge lengths, and their number must not
    // exceed variable's dimensionality.  Start corner is [0,0,..., 0] by
    // default, but may be reset using the set_cur() member.  FALSE is
    // returned if type of values does not match type for variable.
    NcBool put( const ncbyte* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const char* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const short* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const int* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const long* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const float* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );
    NcBool put( const double* vals,
                long c0=0, long c1=0, long c2=0, long c3=0, long c4=0 );

    // Put n-dimensional arrays, starting at [0, 0, ..., 0] by default,
    // may be reset with set_cur().
    NcBool put( const ncbyte* vals, const long* counts );
    NcBool put( const char* vals, const long* counts );
    NcBool put( const short* vals, const long* counts );
    NcBool put( const int* vals, const long* counts );
    NcBool put( const long* vals, const long* counts );
    NcBool put( const float* vals, const long* counts );
    NcBool put( const double* vals, const long* counts );

    // Get scalar or 1, ..., 5 dimensional arrays by providing enough
    // arguments.  Arguments are edge lengths, and their number must not
    // exceed variable's dimensionality.  Start corner is [0,0,..., 0] by
    // default, but may be reset using the set_cur() member.
    NcBool get( ncbyte* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( char* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( short* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( int* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( long* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( float* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const;
    NcBool get( double* vals, long c0=0, long c1=0,
                long c2=0, long c3=0, long c4=0 ) const; 

    // Get n-dimensional arrays, starting at [0, 0, ..., 0] by default,
    // may be reset with set_cur().
    NcBool get( ncbyte* vals, const long* counts ) const;
    NcBool get( char* vals, const long* counts ) const;
    NcBool get( short* vals, const long* counts ) const;
    NcBool get( int* vals, const long* counts ) const;
    NcBool get( long* vals, const long* counts ) const;
    NcBool get( float* vals, const long* counts ) const;
    NcBool get( double* vals, const long* counts ) const;

    NcBool set_cur(long c0=-1, long c1=-1, long c2=-1,
                         long c3=-1, long c4=-1);
    NcBool set_cur(long* cur);

    // these put file in define mode, so could be expensive
    NcBool add_att( NcToken, char );             // add scalar attributes
    NcBool add_att( NcToken, ncbyte );
    NcBool add_att( NcToken, short );
    NcBool add_att( NcToken, int );
    NcBool add_att( NcToken, long );
    NcBool add_att( NcToken, float );
    NcBool add_att( NcToken, double );
    NcBool add_att( NcToken, const char* );      // string attribute
    NcBool add_att( NcToken, int, const char* ); // vector attributes
    NcBool add_att( NcToken, int, const ncbyte* );
    NcBool add_att( NcToken, int, const short* );
    NcBool add_att( NcToken, int, const int* );
    NcBool add_att( NcToken, int, const long* );
    NcBool add_att( NcToken, int, const float* );
    NcBool add_att( NcToken, int, const double* );

    NcBool rename( NcToken newname );

    long rec_size ( void );             // number of values per record

    // Though following are intended for record variables, they also work
    // for other variables, using first dimension as record dimension.

    // Get a record's worth of data
    NcValues *get_rec(void);	        // get current record
    NcValues *get_rec(long rec);        // get specified record

    // Put a record's worth of data in current record
    NcBool put_rec( const ncbyte* vals );
    NcBool put_rec( const char* vals );
    NcBool put_rec( const short* vals );
    NcBool put_rec( const int* vals );
    NcBool put_rec( const long* vals );
    NcBool put_rec( const float* vals );
    NcBool put_rec( const double* vals );

    // Put a record's worth of data in specified record
    NcBool put_rec( const ncbyte* vals, long rec );
    NcBool put_rec( const char* vals, long rec );
    NcBool put_rec( const short* vals, long rec );
    NcBool put_rec( const int* vals, long rec );
    NcBool put_rec( const long* vals, long rec );
    NcBool put_rec( const float* vals, long rec );
    NcBool put_rec( const double* vals, long rec );

    // Get first record index corresponding to specified key value(s)
    long get_index( const ncbyte* vals );
    long get_index( const char* vals );
    long get_index( const short* vals );
    long get_index( const int* vals );
    long get_index( const long* vals );
    long get_index( const float* vals );
    long get_index( const double* vals );

    // Set current record
    void set_rec ( long rec );

    int id( void ) const;               // rarely needed, C interface id
    NcBool sync( void );
    
  private:
    int the_id;
    long* the_cur;
    char* the_name;
    long cur_rec;

    // private constructors because only an NcFile creates these
    NcVar( void );
    NcVar(NcFile*, int);

    int attnum( NcToken attname ) const;
    NcToken attname( int attnum ) const;
    void init_cur( void );

    // to make variables, since constructor is private
  friend NcFile::NcFile( const char *, NcFile::FileMode );
  friend NcBool NcFile::sync( void );
  friend NcVar* NcFile::add_var( NcToken varname, NcType type,
                                 const NcDim*,
                                 const NcDim*,
                                 const NcDim*,
                                 const NcDim*,
                                 const NcDim*);
  friend NcVar* NcFile::add_var( NcToken, NcType, int,
                                 const NcDim** );
};


/*
 * **********************************************************************
 * netCDF attributes.  In addition to a name and a type, these are each
 * associated with a specific variable, or are global to the file.
 * **********************************************************************
 */
class NcAtt : public NcTypedComponent
{
  public:          
    virtual ~NcAtt( void );
    NcToken name( void ) const;
    NcType type( void ) const;
    NcBool is_valid( void ) const;
    long num_vals( void ) const; 
    NcValues* values( void ) const;
    NcBool rename( NcToken newname );
    NcBool remove( void );

  private:
    const NcVar* the_variable;
    char* the_name;
    // protected constructors because only NcVars and NcFiles create
    // attributes
    NcAtt( NcFile*, const NcVar*, NcToken);
    NcAtt( NcFile*, NcToken); // global attribute
    
    // To make attributes, since constructor is private
  friend NcFile::NcFile( const char *, NcFile::FileMode );
  friend NcAtt* NcVar::get_att( NcToken ) const;
};


/*
 * **********************************************************************
 * To control error handling.  Declaring an NcError object temporarily
 * changes the error-handling behavior until the object is destroyed, at
 * which time the previous error-handling behavior is restored.
 * **********************************************************************
 */
class NcError {
  public:
    enum Behavior {
        silent_nonfatal = 0,
        verbose_nonfatal = NC_VERBOSE,
        silent_fatal = NC_FATAL,
        verbose_fatal = NC_FATAL | NC_VERBOSE      
      };

    // constructor saves previous error state, sets new state
    NcError( Behavior b = verbose_fatal );

    // destructor restores previous error state
    virtual ~NcError( void );

    int get_err( void );                 // returns most recent error

  private:
    int the_old_state;
    int the_old_err;
};

#endif                          /* NETCDF_HH */
