/** \file
 *
 *  Contains the NetCdf class declaration.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef NETCDF_H_
#define NETCDF_H_

#define USE_NETCDF4

#include "utils/FastMap.h"
#include "utils/FlexArray.h"
#include "utils/Str.h"

#include <netcdf.h>

class DataValue;
class Unit;

// from official netcdf.h
#define NC_MAX_DIMS     1024     /* max dimensions per file */
#define NC_MAX_ATTRS    8192     /* max global or per variable attributes */
#define NC_MAX_VARS     8192     /* max variables per file */
#define NC_MAX_NAME     256      /* max length of a name */
#ifndef NC_MAX_VAR_DIMS
#define NC_MAX_VAR_DIMS NC_MAX_DIMS /* max per variable dimensions */
#endif

#define NC_MAX_ATTR_SIZE  8192   /* max size of an attribute */
#define NC_MAX_STRING_SIZE 8192  /* max size of a string */

/**
 *  Abstract base class for NetCcfReader and NetCdfWriter
 *
 *  \ingroup utils
 */
class NetCdf
{
public:

    typedef enum
    {
        NC_ABSENT_TYPE = 0,
        NC_BYTE_TYPE = NC_BYTE,          // data is array of 8 bit signed integer
        NC_CHAR_TYPE = NC_CHAR,          // data is array of characters, i.e., text
        NC_SHORT_TYPE = NC_SHORT,          // data is array of 16 bit signed integer
        NC_INT_TYPE = NC_INT,          // data is array of 32 bit signed integer
        NC_FLOAT_TYPE = NC_FLOAT,          // data is array of IEEE single precision float
        NC_DOUBLE_TYPE = NC_DOUBLE,          // data is array of IEEE double precision float
        NC_UBYTE_TYPE = NC_UBYTE,         //  Netcdf-4 only, unsigned 1 byte int
        NC_USHORT_TYPE = NC_USHORT,         //  Netcdf-4 only, unsigned 2-byte int
        NC_UINT_TYPE = NC_UINT,         //  Netcdf-4 only, unsigned 4-byte int
        NC_INT64_TYPE = NC_INT64,         //  Netcdf-4 only, signed 8-byte int
        NC_UINT64_TYPE = NC_UINT64,          //  Netcdf-4 only, unsigned 8-byte int
        NC_STRING_TYPE = NC_STRING,       // Netcdf-4 only, string type.
    } NetCdfType;

    // lookup table that indicates #of bytes for each NetCdfType
    static unsigned int NetCdfTypeSize_[NC_DOUBLE_TYPE + 1];

    typedef struct
    {
        int dimId_;
        unsigned int dimSize_;
    } NetCdfDim;

    typedef FastMap<const Str, NetCdfDim*> NetCdfDims;
    typedef FlexArray<NetCdfDim*> NetCdfDimArray;

    typedef struct
    {
        int attId_;
        int varId_;  // Can be NC_GLOBAL
        Str name_;
        NetCdfType netCdfType_;
        unsigned int nelems_;
    } NetCdfAtt;

    typedef FastMap<const Str, NetCdfAtt*> NetCdfAtts;

    struct NetCdfVar
    {
        int varId_;
        int nDims_;
        int* dimIds_;
        NetCdfAtts atts_;
        bool isRecord_;
        NetCdfType netCdfType_;
        unsigned int varIndex_;
        DataValue* dataValue_;
        size_t numRecs_;
        int ncId_;
        NetCdfVar();
        ~NetCdfVar();
    };

    typedef FastMap<const Str, NetCdfVar*> NetCdfVars;
    typedef FlexArray<NetCdfVar*> NetCdfVarArray;

    /// Destructor
    virtual ~NetCdf();

    /// Returns the dimensions
    const NetCdfDimArray& getDimArray()
    {
        return dimArray_;
    }

    /// Returns the global attributes
    const NetCdfAtts& getGlobalAtts()
    {
        return gatts_;
    }

    /// Returns the variables
    const NetCdfVars& getVars()
    {
        return vars_;
    }

    /// Finds a dimension by name
    /// Returns NULL if error.
    NetCdfDim* findDim( const Str& name );

    /// Finds a global attribute by name        NetCdfAtt* invalidValue_;

    /// Returns true if no error.
    NetCdfAtt* findAtt( const Str& name );

    /// Finds a variable by name
    /// Returns NULL if error.
    NetCdfVar* findVar( const Str& name );

    /// Finds a variable's attribute by name
    /// Returns NULL if error.
    NetCdfAtt* findAtt( NetCdfVar& var, const Str& name );

    const char* status();

protected:

    /// Protected constructor
    NetCdf();

    /// Finds a dimension by name in the set of NetCdfDims
    /// Returns NULL if error.
    NetCdfDim* findDim( NetCdfDims& dims, const Str& name );

    /// Finds a global attribute by name in the set of NetCdfAtts
    /// Returns NULL if error.
    NetCdfAtt* findAtt( NetCdfAtts& atts, const Str& name );

    /// Finds a variable by name in the set of NetCdfVars
    /// Returns NULL if error.
    NetCdfVar* findVar( NetCdfVars& vars, const Str& name );

    int  status_;                      /* error status */
    int  ncId_;                        /* netCDF ID */
    NetCdfDimArray dimArray_;          /* dimensions */
    NetCdfDims dims_;                  /* dimension id map */
    NetCdfAtts gatts_;                 /* global attribute map */
    NetCdfVarArray varArray_;          /* variables */
    NetCdfVars vars_;                  /* variable map */


};

#endif /*NETCDF_H_*/
