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

#include "utils/NetCdf3Reader.h"
#include <climits>
#include <math.h>

/// Returns an newNetCdf3Reader of a NetCdf3Reader for a file
NetCdf3Reader* NetCdf3Reader::NewNetCdf3Reader( const char* fileName )
{
    NetCdf3Reader * netCdf3Reader = new NetCdf3Reader( fileName );
    if( !netCdf3Reader->isOk() )
    {
        delete netCdf3Reader;
        netCdf3Reader = NULL;
    }
    return netCdf3Reader;
}

/// Destructor
NetCdf3Reader::~NetCdf3Reader()
{
}

/// Reads the value of the variable at the 1D index as the indicated type
/// Returns true if no error.
bool NetCdf3Reader::read1D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                            const unsigned long index1 )
{
    unsigned int typeSize = NetCdfTypeSize_[ var.netCdfType_ ];
    unsigned long location = var.begin_;
    if( var.isRecord_ )
    {
        if( 0 == var.vSize_ )
        {
            return false;
        }
        unsigned long record = ( index1 * typeSize ) / var.vSize_;
        unsigned long offset = ( index1 * typeSize ) % var.vSize_;
        location += record * recordSize_ + offset;
    }
    else
    {
        location += typeSize * index1;
    }
    if( !fin_.seek( location ) || fin_.eof() )
    {
        return false;
    }
    return readNext( assignTo, assignType, var );
}

/// Reads the value of the variable at the 2D index as the indicated type
/// Returns true if no error.
bool NetCdf3Reader::read2D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                            const unsigned long index1, const unsigned long index2 )
{
    if( var.nelems_ < 2 )
    {
        return read1D( assignTo, assignType, var, index1 );
    }
    NetCdfDim* dim = dimArray_[ var.dimIds_[ 1 ] ];
    unsigned int dim1Size = dim->dimSize_;
    return read1D( assignTo, assignType, var, index1 * dim1Size + index2 );
}

/// Reads the value of the variable at the 3D index as the indicated type
/// Returns true if no error.
bool NetCdf3Reader::read3D( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                            const unsigned long index1, const unsigned long index2,
                            const unsigned long index3 )
{
    if( var.nelems_ < 3 )
    {
        return read2D( assignTo, assignType, var, index1, index2 );
    }
    NetCdfDim* dim1 = dimArray_[ var.dimIds_[ 1 ] ];
    unsigned int dim1Size = dim1->dimSize_;
    NetCdfDim* dim2 = dimArray_[ var.dimIds_[ 2 ] ];
    unsigned int dim2Size = dim2->dimSize_;
    unsigned long index = ( index1 * dim1Size + index2 ) * dim2Size + index3;
    return read1D( assignTo, assignType, var, index );
}

/// Reads the value of the variable at the 4D index as the indicated type
/// Returns true if no error.
bool NetCdf3Reader::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 )
{
    if( var.nelems_ < 4 )
    {
        return read3D( assignTo, assignType, var, index1, index2, index3 );
    }
    NetCdfDim* dim1 = dimArray_[ var.dimIds_[ 1 ] ];
    unsigned int dim1Size = dim1->dimSize_;
    NetCdfDim* dim2 = dimArray_[ var.dimIds_[ 2 ] ];
    unsigned int dim2Size = dim2->dimSize_;
    NetCdfDim* dim3 = dimArray_[ var.dimIds_[ 3 ] ];
    unsigned int dim3Size = dim3->dimSize_;
    unsigned long index = ( ( index1 * dim1Size + index2 ) * dim2Size + index3 ) * dim3Size + index4;
    return read1D( assignTo, assignType, var, index );
}

/// Reads a 1D range of the variable between a pair of indices
/// (a <= index <= b) into an existing 1D array of the indicated type
/// Returns true if no error.
bool NetCdf3Reader::read1DArray( void* assignTo, const NetCdfType assignType, const NetCdfVar& var,
                                 const unsigned long index1a, const unsigned long index1b )
{
    unsigned long size1 = index1b - index1a + 1;
    for( unsigned long i1 = 0; i1 < size1; ++i1 )
    {
        if( !read1D( ( void* )( ( char* )assignTo + i1 * NetCdfTypeSize_[ assignType ] ),
                     assignType, var, i1 + index1a ) )
        {
            return false;
        }
    }
    return true;
}

/// Reads a 2D range of the variable between 2 pairs of indices
/// (a <= index <= b) into an existing 2D array of the indicated type
/// Returns true if no error.
bool NetCdf3Reader::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 )
{
    unsigned long size1 = index1b - index1a + 1;
    unsigned long size2 = index2b - index2a + 1;
    for( unsigned long i1 = 0; i1 < size1; ++i1 )
    {
        for( unsigned long i2 = 0; i2 < size2; ++i2 )
        {
            if( !read2D( ( void* )( ( char* )assignTo + ( i1 * size2 + i2 ) * NetCdfTypeSize_[ assignType ] ),
                         assignType, var, i1 + index1a, i2 + index2a ) )
            {
                return false;
            }
        }
    }
    return true;
}

/// Reads a 3D range of the variable between 3 pairs of indices
/// (a <= index <= b) into an existing 3D array of the indicated type
/// Returns true if no error.
bool NetCdf3Reader::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 )
{
    unsigned long size1 = index1b - index1a + 1;
    unsigned long size2 = index2b - index2a + 1;
    unsigned long size3 = index3b - index3a + 1;
    for( unsigned long i1 = 0; i1 < size1; ++i1 )
    {
        for( unsigned long i2 = 0; i2 < size2; ++i2 )
        {
            for( unsigned long i3 = 0; i3 < size3; ++i3 )
            {
                if( !read3D( ( void* )( ( char* )assignTo + ( ( i1 * size2 + i2 ) * size3 + i3 ) * NetCdfTypeSize_[ assignType ] ),
                             assignType, var, i1 + index1a, i2 + index2a, i3 + index3a ) )
                {
                    return false;
                }
            }
        }
    }
    return true;
}

/// Reads a 4D range of the variable between 4 pairs of indices
/// (a <= index <= b) into an existing 4D array of the indicated type
/// Returns true if no error.
bool NetCdf3Reader::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 )
{
    unsigned long size1 = index1b - index1a + 1;
    unsigned long size2 = index2b - index2a + 1;
    unsigned long size3 = index3b - index3a + 1;
    unsigned long size4 = index4b - index4a + 1;
    for( unsigned long i1 = 0; i1 < size1; ++i1 )
    {
        for( unsigned long i2 = 0; i2 < size2; ++i2 )
        {
            for( unsigned long i3 = 0; i3 < size3; ++i3 )
            {
                for( unsigned long i4 = 0; i4 < size4; ++i4 )
                {
                    if( !read4D( ( void* )( ( char* )assignTo + ( ( ( i1 * size2 + i2 ) * size3 + i3 ) * size4 + i4 ) * NetCdfTypeSize_[ assignType ] ),
                                 assignType, var, i1 + index1a, i2 + index2a, i3 + index3a, i4 + index4a ) )
                    {
                        return false;
                    }
                }
            }
        }
    }
    return true;
}

/// Protected Constructor
NetCdf3Reader::NetCdf3Reader( const char* fileName )
    : NetCdf3(),
      fin_( fileName )
{
    ok_ = initialize( fileName );
}

/// Opens a NetCdf3Reader for a file
bool NetCdf3Reader::initialize( const char* fileName )
{
    if( fin_.eof() )
    {
        return false;
    }
    NetCdfHeader netCdfHeader;
    fin_.read( ( char* )( &netCdfHeader ), sizeof( NetCdfHeader ) );
    flipEndian( ( char* ) & ( netCdfHeader.numrecs_ ), 4 );
    if( fin_.eof() || netCdfHeader.magic_[ 0 ] != 'C' || netCdfHeader.magic_[ 1 ] != 'D'
            || netCdfHeader.magic_[ 2 ] != 'F' || netCdfHeader.version_ < 1 || netCdfHeader.version_ > 2 )
    {
        return false;
    }
    NetCdfType nc_type;
    if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
    {
        return false;
    }

    // Read some optional zeroes if no dimensions
    if( nc_type == NC_ABSENT )
    {
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) )
                || nc_type != NC_ABSENT )
        {
            return false;
        }
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
        {
            return false;
        }
    }

    // Now we read the dim array
    if( nc_type == NC_DIMENSION )
    {
        unsigned int nDims;
        if( !readEndian( ( char * )( &nDims ), sizeof( nDims ) )
                || nDims > NC_MAX_DIMS )
        {
            return false;
        }
        for( unsigned int i = 0; i < nDims; ++i )
        {
            Str name;
            if( !readNext( name ) )
            {
                return false;
            }
            NetCdfDim* netCdfDim = new NetCdfDim();
            if( !readEndian( ( char * )( &netCdfDim->dimSize_ ), sizeof( netCdfDim->dimSize_ ) ) )
            {
                delete netCdfDim;
                return false;
            }
            netCdfDim->isRecord_ = netCdfDim->dimSize_ == 0;
            if( netCdfDim->isRecord_ )
            {
                netCdfDim->dimSize_ = netCdfHeader.numrecs_;
            }
            netCdfDim->dimIndex_ = dimArray_.size();
            dims_.put( name, netCdfDim );
            dimArray_.push( netCdfDim );
        }
        // Read the next value of nc_type
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
        {
            return false;
        }
    }

    // Read some optional zeroes if no global attributes
    if( nc_type == NC_ABSENT )
    {
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) )
                || nc_type != NC_ABSENT )
        {
            return false;
        }
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
        {
            return false;
        }
    }

    // Now we read the global attribute array
    if( nc_type == NC_ATTRIBUTE )
    {
        if( !readAttArray( gatts_ ) )
        {
            return false;
        }
        // Read the next value of nc_type
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
        {
            return false;
        }
    }

    // Read some optional zeroes if no variables
    if( nc_type == NC_ABSENT )
    {
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) )
                || nc_type != NC_ABSENT )
        {
            return false;
        }
        // The next value of nc_type should be zero
        if( !readEndian( ( char * )( &nc_type ), sizeof( nc_type ) ) )
        {
            return false;
        }
    }

    // Now we read the variable array
    if( nc_type == NC_VARIABLE )
    {
        unsigned int nVars;
        if( !readEndian( ( char * )( &nVars ), sizeof( nVars ) )
                || nVars > NC_MAX_VARS )
        {
            return false;
        }
        for( unsigned int i = 0; i < nVars; ++i )
        {
            Str name;
            if( !readNext( name ) )
            {
                return false;
            }
            NetCdfVar* netCdfVar = new NetCdfVar();
            if( !readEndian( ( char * )( &netCdfVar->nelems_ ), sizeof( netCdfVar->nelems_ ) )
                    || netCdfVar->nelems_ > NC_MAX_VAR_DIMS )
            {
                delete netCdfVar;
                return false;
            }
            netCdfVar->dimIds_ = new unsigned int[ netCdfVar->nelems_ ];
            for( unsigned int j = 0; j < netCdfVar->nelems_; ++j )
            {
                if( !readEndian( ( char * )( ( netCdfVar->dimIds_ ) + j ), sizeof( unsigned int ) ) )
                {
                    delete netCdfVar;
                    return false;
                }
                netCdfVar->isRecord_ = j == 0 && dimArray_[ netCdfVar->dimIds_[ j ] ] ->isRecord_;
            }
            // Read the type -- this could indicate the presence of attributes
            if( !readEndian( ( char * )( &netCdfVar->netCdfType_ ), sizeof( netCdfVar->netCdfType_ ) ) )
            {
                delete netCdfVar;
                return false;
            }
            // maybe there are attributes...
            if( netCdfVar->netCdfType_ == NC_ATTRIBUTE )
            {
                if( !readAttArray( netCdfVar->atts_ ) )
                {
                    delete netCdfVar;
                    return false;
                }
                // Read the type for the data
                if( !readEndian( ( char * )( &netCdfVar->netCdfType_ ), sizeof( netCdfVar->netCdfType_ ) ) )
                {
                    delete netCdfVar;
                    return false;
                }
            }
            if( !readEndian( ( char * )( &netCdfVar->vSize_ ), sizeof( netCdfVar->vSize_ ) ) )
            {
                delete netCdfVar;
                return false;
            }
            if( netCdfVar->isRecord_ )
            {
                recordSize_ += netCdfVar->vSize_;
            }
            if( netCdfHeader.version_ == 1 )
            {
                unsigned int begin;
                if( !readEndian( ( char * )( &begin ), sizeof( begin ) ) )
                {
                    delete netCdfVar;
                    return false;
                }
                netCdfVar->begin_ = begin;
            }
            else if( netCdfHeader.version_ == 2 )
            {
                if( !readEndian( ( char * )( &netCdfVar->begin_ ), sizeof( netCdfVar->begin_ ) ) )
                {
                    delete netCdfVar;
                    return false;
                }
            }
            netCdfVar->invalidValue_ = netCdfVar->atts_.get( "_FillValue" );
            if( NULL == netCdfVar->invalidValue_ )
            {
                netCdfVar->invalidValue_ = netCdfVar->atts_.get( "missing_value" );
            }
            vars_.put( name, netCdfVar );
        }
    }
    return true;
}

/// Reads an array of attributes from an input stream
bool NetCdf3Reader::readAttArray( NetCdfAtts& atts )
{
    unsigned int nAtts;
    if( !readEndian( ( char * )( &nAtts ), sizeof( nAtts ) )
            || nAtts > NC_MAX_ATTRS )
    {
        return false;
    }
    for( unsigned int i = 0; i < nAtts; ++i )
    {
        Str name;
        if( !readNext( name ) )
        {
            return false;
        }
        NetCdfAtt* netCdfAtt = new NetCdfAtt();
        NetCdfType netCdfType;  // Define this to avoid coverity tainted scalar warning
        if( !readEndian( ( char * )( &netCdfType ), sizeof( netCdfType ) )
                || netCdfType < NC_BYTE || netCdfType > NC_DOUBLE )
        {
            delete netCdfAtt;
            return false;
        }
        netCdfAtt->netCdfType_ = netCdfType;
        unsigned int typeSize = NetCdfTypeSize_[ netCdfType ];
        if( !readEndian( ( char * )( &netCdfAtt->nelems_ ), sizeof( netCdfAtt->nelems_ ) )
                || netCdfAtt->nelems_ > NC_MAX_ATTR_SIZE )
        {
            delete netCdfAtt;
            return false;
        }
        if( netCdfAtt->nelems_ >= UINT_MAX / typeSize )
        {
            delete netCdfAtt;
            return false;
        }
        unsigned int size = pad( netCdfAtt->nelems_ * typeSize );
        netCdfAtt->values_ = new char[ size ];
        for( unsigned int j = 0; j < size; j += typeSize )
        {
            if( !readEndian( netCdfAtt->values_ + j, typeSize ) )
            {
                delete netCdfAtt;
                return false;
            }
        }
        atts.put( name, netCdfAtt );
    }
    return true;
}

/// Reads bytes from the stream, reversing them if this is a little endian machine
bool NetCdf3Reader::readEndian( char* buffer, unsigned int num )
{
    fin_.read( buffer, num );
    if( fin_.eof() )
    {
        return false;
    }
    flipEndian( buffer, num );
    return true;
}

/// Reads the next netCdf Str from the file
bool NetCdf3Reader::readNext( Str& theString )
{
    unsigned int size;
    if( !readEndian( ( char * )( &size ), sizeof( size ) )
            || size > NC_MAX_STRING_SIZE )
    {
        return false;
    }
    unsigned int buffSize = pad( size );
    char * charbuffer = new char[ buffSize + 1 ];
    fin_.read( charbuffer, buffSize );
    unsigned int bytesRead = fin_.bytesRead();
    if( fin_.eof() || bytesRead > buffSize )
    {
        delete[] charbuffer;
        return false;
    }
    charbuffer[ bytesRead ] = 0;
    theString = Str( charbuffer, size );
    delete[] charbuffer;

    return true;
}

/// Reads the value of the next variable in the stream as a the specified type
/// Returns true if no error.
bool NetCdf3Reader::readNext( void* assignTo, const NetCdfType assignType, const NetCdfVar& var )
{
    /// An 8-byte "buffer" for holding anything.
    unsigned long long value;
    if( !readEndian( ( char * )( &value ), NetCdfTypeSize_[ var.netCdfType_ ] ) )
    {
        return false;
    }
    return readFromBytes( assignTo, assignType, ( void* ) &value, var.netCdfType_,
                          var.invalidValue_ == NULL ? NULL : ( void* ) var.invalidValue_->values_,
                          var.invalidValue_ == NULL ? NC_ABSENT : var.invalidValue_->netCdfType_ );
}

bool NetCdf3Reader::readFromBytes( void* assignTo, const NetCdfType assignType, void* fromValue, const NetCdfType fromType, void* invalidValue, const NetCdfType invalidType )
{
    switch( fromType )
    {
    case NC_BYTE:
        switch( assignType )
        {
        case NC_BYTE:
            *( char* )assignTo = *( ( char* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( unsigned char* )assignTo = *( ( char* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = *( ( char* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = *( ( char* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = *( ( char* )( fromValue ) );
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = *( ( char* )( fromValue ) );
            return true;
        default:
            return false;
        }
    case NC_CHAR:
        switch( assignType )
        {
        case NC_BYTE:
            *( char* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( unsigned char* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = *( ( unsigned char* )( fromValue ) );
            return true;
        default:
            return false;
        }
    case NC_SHORT:
        switch( assignType )
        {
        case NC_BYTE:
            *( char* )assignTo = *( ( short* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( unsigned char* )assignTo = *( ( short* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = *( ( short* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = *( ( short* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = *( ( short* )( fromValue ) );
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = *( ( short* )( fromValue ) );
            return true;
        default:
            return false;
        }
    case NC_INT:
        switch( assignType )
        {
        case NC_BYTE:
            *( char* )assignTo = *( ( int* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( unsigned char* )assignTo = *( ( int* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = *( ( int* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = *( ( int* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = *( ( int* )( fromValue ) );
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = *( ( int* )( fromValue ) );
            return true;
        default:
            return false;
        }
    case NC_FLOAT:
        switch( assignType )
        {
        case NC_BYTE:
            *( unsigned char* )assignTo = ( unsigned char ) * ( ( float* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( signed char* )assignTo = ( signed char ) * ( ( float* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = ( short ) * ( ( float* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = ( int ) * ( ( float* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = *( ( float* )( fromValue ) );
            if( NULL != invalidValue )
            {
                if( invalidType == NC_FLOAT )
                {
                    if( *( float* )assignTo == *( float* )invalidValue )
                    {
                        *( float* )assignTo = nanf( "" );
                    }
                }
                else if( invalidType == NC_DOUBLE )
                {
                    if( *( float* )assignTo == ( float ) * ( ( double* )invalidValue ) )
                    {
                        *( float* )assignTo = nanf( "" );
                    }
                }
            }
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = ( double ) * ( ( float* )( fromValue ) );
            if( NULL != invalidValue )
            {
                if( invalidType == NC_FLOAT )
                {
                    if( *( double* )assignTo == ( double ) * ( ( float* )invalidValue ) )
                    {
                        *( double* )assignTo = nan( "" );
                    }
                }
                else if( invalidType == NC_DOUBLE )
                {
                    if( *( double* )assignTo == *( double* )invalidValue )
                    {
                        *( double* )assignTo = nanf( "" );
                    }
                }
            }
            return true;
        default:
            return false;
        }
    case NC_DOUBLE:
        switch( assignType )
        {
        case NC_BYTE:
            *( signed char* )assignTo = ( signed char ) * ( ( double* )( fromValue ) );
            return true;
        case NC_CHAR:
            *( unsigned char* )assignTo = ( unsigned char ) * ( ( double* )( fromValue ) );
            return true;
        case NC_SHORT:
            *( short* )assignTo = ( short ) * ( ( double* )( fromValue ) );
            return true;
        case NC_INT:
            *( int* )assignTo = ( int ) * ( ( double* )( fromValue ) );
            return true;
        case NC_FLOAT:
            *( float* )assignTo = ( float ) * ( ( double* )( fromValue ) );
            if( NULL != invalidValue )
            {
                if( invalidType == NC_FLOAT )
                {
                    if( *( float* )assignTo == *( float* )invalidValue )
                    {
                        *( float* )assignTo = nanf( "" );
                    }
                }
                else if( invalidType == NC_DOUBLE )
                {
                    if( *( float* )assignTo == ( float ) * ( ( double* )invalidValue ) )
                    {
                        *( float* )assignTo = nanf( "" );
                    }
                }
            }
            return true;
        case NC_DOUBLE:
            *( double* )assignTo = *( ( double* )( fromValue ) );
            if( NULL != invalidValue )
            {
                if( invalidType == NC_FLOAT )
                {
                    if( *( double* )assignTo == ( double ) * ( ( float* )invalidValue ) )
                    {
                        *( double* )assignTo = nan( "" );
                    }
                }
                else if( invalidType == NC_DOUBLE )
                {
                    if( *( double* )assignTo == *( double* )invalidValue )
                    {
                        *( double* )assignTo = nan( "" );
                    }
                }
            }
            return true;
        default:
            return false;
        }
    default:
        return false;
    }

}

/// Reads the value of the attribute as a Str
/// Returns true if no error.
bool NetCdf3Reader::readAttStr( Str& assignTo, const NetCdfAtt* att )
{
    assignTo = Str( att->values_, att->nelems_ );
    return true;
}

/// Reads the value of the attribute
/// Returns true if no error.
bool NetCdf3Reader::readAtt( void* assignTo, const NetCdfType assignType, const NetCdfAtt* att )
{
    if( att->nelems_ == 0 )
    {
        return false;
    }
    return readFromBytes( assignTo, assignType, att->values_, att->netCdfType_, NULL, NC_ABSENT );
}

NetCdf3::NetCdfVar* NetCdf3Reader::findNetCdfVarByAttribute( const Str& name, const Str& value )
{
    const NetCdfVars& vars = getVars();
    for( unsigned int i = 0; i < vars.size(); ++i )
    {
        NetCdfVar* var = vars.getIndexed( i );
        NetCdfAtts& varAtts = var->atts_;
        NetCdfAtt* varAtt = varAtts.get( name );
        if( varAtt )
        {
            Str varAttValue;
            if( readAttStr( varAttValue, varAtt ) && value == varAttValue )
            {
                return var;
            }
        }
    }
    return NULL;
}
