/** \file
 *
 *  Contains the NetCdf3Reader class declaration.
 *
 *  Warning - this only reads classic NetCDF version 3 data files
 *    with fixed (non-record) dimensions
 *
 *  To make sure a file is compatable use the following ncks
 *    (NetCDF Kitchen Sink) command:
 *
 *    ncks --fix_rec_dmn time -3 -o output.nc3 input.nc
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef NETCDF3READER_H_
#define NETCDF3READER_H_

#include "utils/NetCdf3.h"

#include "io/FileInStream.h"

/**
 *  A very compact, very simple netCDF reader with a minimal feature set.
 *
 *  \ingroup utils
 */
class NetCdf3Reader : public NetCdf3
{
public:

    /// Returns an newNetCdf3Reader of a NetCdf3Reader for a file
    static NetCdf3Reader* NewNetCdf3Reader( const char* fileName );

    /// Destructor
    virtual ~NetCdf3Reader();

    /// Indicates intialization success
    bool isOk() const
    {
        return ok_;
    }

    /// Reads the value of the variable at the 1D index as the specified type
    /// Returns true if no error.
    bool read1D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                 const unsigned long index1 );

    /// Reads the value of the variable at the 2D index as the specified type
    /// Returns true if no error.
    bool read2D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                 const unsigned long index1, const unsigned long index2 );

    /// Reads the value of the variable at the 3D index as the specified type
    /// Returns true if no error.
    bool read3D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                 const unsigned long index1, const unsigned long index2,
                 const unsigned long index3 );

    /// Reads the value of the variable at the 4D index as the specified type
    /// Returns true if no error.
    bool read4D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                 const unsigned long index1, const unsigned long index2,
                 const unsigned long index3, const unsigned long index4 );

    /// Reads a 1D range of the variable between a pair of indices
    /// (a <= index <= b) into an existing 1D array of the specified type
    /// Returns true if no error.
    bool read1DArray( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                      const unsigned long index1a, const unsigned long index1b );

    /// Reads a 2D range of the variable between 2 pairs of indices
    /// (a <= index <= b) into an existing 2D array of the specified type
    /// Returns true if no error.
    bool read2DArray( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                      const unsigned long index1a, const unsigned long index1b,
                      const unsigned long index2a, const unsigned long index2b );

    /// Reads a 3D range of the variable between 3 pairs of indices
    /// (a <= index <= b) into an existing 3D array of the specified type
    /// Returns true if no error.
    bool read3DArray( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                      const unsigned long index1a, const unsigned long index1b,
                      const unsigned long index2a, const unsigned long index2b,
                      const unsigned long index3a, const unsigned long index3b );

    /// Reads a 4D range of the variable between 4 pairs of indices
    /// (a <= index <= b) into an existing 3D array of the specified type
    /// Returns true if no error.
    bool read4DArray( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                      const unsigned long index1a, const unsigned long index1b,
                      const unsigned long index2a, const unsigned long index2b,
                      const unsigned long index3a, const unsigned long index3b,
                      const unsigned long index4a, const unsigned long index4b );

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

    /// Reads the value of the attribute
    /// Returns true if no error.
    bool readAtt( void* assignTo, const NetCdfType assignType, const NetCdfAtt* att );

    /// Finds the first variable with the indicated attribute name and attribute value
    /// Returns null if no match
    NetCdfVar* findNetCdfVarByAttribute( const Str& name, const Str& value );

protected:

    /// Protected Constructor
    NetCdf3Reader( const char* fileName );

    /// Opens a NetCdf3Reader for a file
    /// Returns true if no error.
    bool initialize( const char* fileName );

    /// Reads an array of attributes from an input stream
    /// Returns true if no error.
    bool readAttArray( NetCdfAtts& atts );

    /// Reads bytes from the stream, reversing them if this is a little endian machine
    /// Returns true if no error.
    bool readEndian( char* buffer, unsigned int num );

    /// Reads a netCDF Str from the input stream
    /// Returns true if no error.
    bool readNext( Str& theString );

    /// Reads the value of the next variable in the stream as the specified type
    /// Returns true if no error.
    bool readNext( void* assignTo, const NetCdfType assignType, const NetCdfVar& var );

    bool readFromBytes( void* assignTo, const NetCdfType assignType, void* fromValue, const NetCdfType fromType,
                        void* invalidValue, const NetCdfType invalidType );

    bool ok_;
    FileInStream fin_;
};

#endif /*NETCDF3READER_H_*/
