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

#include "utils/NetCdf3.h"

// lookup table that indicates #of bytes for each NetCdfType
unsigned int NetCdf3::NetCdfTypeSize_[NUM_NC_TYPES] = {0, 1, 1, 2, 4, 4, 8, 0, 0, 0, 0, 0, 0};

/// Destructor
NetCdf3::~NetCdf3()
{
    // Dims deleted by dimArray_ destructor
    // Delete Global Attributes
    while( gatts_.size() > 0 )
    {
        NetCdfAtt * netCdfAtt = gatts_.popIndexed( gatts_.size() - 1 );
        delete[] netCdfAtt->values_;
        delete netCdfAtt;
    }
    // Delete Variables & Variable Attributes
    while( vars_.size() > 0 )
    {
        NetCdfVar * netCdfVar = vars_.popIndexed( vars_.size() - 1 );
        delete[] netCdfVar->dimIds_;
        while( netCdfVar->atts_.size() > 0 )
        {
            NetCdfAtt * netCdfAtt = netCdfVar->atts_.popIndexed( netCdfVar->atts_.size() - 1 );
            delete[] netCdfAtt->values_;
            delete netCdfAtt;
        }
        delete netCdfVar;
    }
}

/// Finds a dimension by name
/// Returns NULL if error.
NetCdf3::NetCdfDim* NetCdf3::findDim( const Str& name )
{
    return findDim( dims_, name );
}

/// Finds a global attribute by name
/// Returns NULL if error.
NetCdf3::NetCdfAtt* NetCdf3::findAtt( const Str& name )
{
    return findAtt( gatts_, name );
}

/// Finds a variable by name
/// Returns NULL if error.
NetCdf3::NetCdfVar* NetCdf3::findVar( const Str& name )
{
    return findVar( vars_, name );
}

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

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

/// Protected Constructor
NetCdf3::NetCdf3()
    : dimArray_( true, 4, NC_MAX_DIMS ),
      dims_(),
      gatts_(),
      varArray_( false, 8, NC_MAX_VARS ),
      vars_(),
      recordSize_( 0 )
{
    char endianChars[ 2 ] = { 1, 0 };
    littleEndian_ = * ( short * ) endianChars == 1 ;
}

/// Reverse bytes if this is a little endian machine
void NetCdf3::flipEndian( char* buffer, unsigned int num )
{
    if( littleEndian_ )
    {
        for( unsigned int i = 0; i < ( num >> 1 ) ; ++i )
        {
            unsigned char temp = buffer[ i ];
            buffer[ i ] = buffer[ num - i - 1 ];
            buffer[ num - i - 1 ] = temp;
        }
    }
}

/// Pads a number to the next 4-byte boundary if necessary
unsigned int NetCdf3::pad( unsigned int number )
{
    return ( number & 0xFFFFFFFC ) + ( ( number & 3 ) ? 4 : 0 );
}

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

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

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