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

#ifndef NETCDF3_H_
#define NETCDF3_H_

#include "utils/FastMap.h"
#include "utils/FlexArray.h"
#include "utils/Str.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 */
#define NC_MAX_VAR_DIMS NC_MAX_DIMS /* max per variable dimensions */

#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 NetCdf3
{
public:

    typedef enum
    {
        NC_ABSENT,
        NC_BYTE = 1,          // data is array of 8 bit signed integer
        NC_CHAR = 2,          // data is array of characters, i.e., text
        NC_SHORT = 3,          // data is array of 16 bit signed integer
        NC_INT = 4,          // data is array of 32 bit signed integer
        NC_FLOAT = 5,          // data is array of IEEE single precision float
        NC_DOUBLE = 6,          // data is array of IEEE double precision float
        NC_DIMENSION = 10,
        NC_VARIABLE = 11,
        NC_ATTRIBUTE = 12,
        NUM_NC_TYPES = 13
    } NetCdfType;

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

    typedef struct
    {
        char magic_[ 3 ];
        unsigned char version_;
        unsigned int numrecs_;
    } NetCdfHeader;

    typedef struct
    {
        unsigned int dimSize_;
        bool isRecord_;
        unsigned int dimIndex_;
    } NetCdfDim;

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

    typedef struct
    {
        NetCdfType netCdfType_;
        unsigned int nelems_;
        char* values_;
    } NetCdfAtt;

    typedef FastMap<const Str, NetCdfAtt*> NetCdfAtts;

    typedef struct
    {
        unsigned int nelems_;
        unsigned int* dimIds_;
        NetCdfAtts atts_;
        NetCdfType netCdfType_;
        unsigned int vSize_;
        unsigned long long begin_;
        bool isRecord_;
        unsigned int varIndex_;
        DataValue* dataValue_;
        const Unit* unit_;
        NetCdfAtt* invalidValue_;
    } NetCdfVar;

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

    /// Destructor
    virtual ~NetCdf3();

    /// 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
    /// 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 );

    /// Reads the value of the attribute as a Str
    /// Returns true if no error.
    bool readStr( Str& assignTo, const NetCdfAtt& att );

protected:

    /// Protected constructor
    NetCdf3();

    /// Reverse bytes if this is a little endian machine
    void flipEndian( char* buffer, unsigned int num );

    /// Reads a netCDF Str from the input stream
    /// Pads a number to the next 4-byte boundary
    unsigned int pad( unsigned int size );

    /// 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 );

    bool littleEndian_;
    NetCdfDimArray dimArray_;
    NetCdfDims dims_;
    NetCdfAtts gatts_;
    NetCdfVarArray varArray_;
    NetCdfVars vars_;
    unsigned int recordSize_;
};

#endif /*NETCDF3_H_*/