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

#define DEBUG_HDF5 false

#include "data/BlobValue.h"
#include "data/DataValue.h"
#include "data/StrValue.h"
#include "io/FileInStream.h"
#include "io/FileOutStream.h"
#include "utils/HDF5.h"
#include "utils/Timestamp.h"
#include <errno.h>

#define H5Acreate_vers 2
#define H5Dcreate_vers 2
#define H5Gcreate_vers 2

#include <hdf5.h>
#include <time.h>
#include <unistd.h>

bool HDF5Item::finalize()
{
    if( -1 != typeId_ )
    {
        H5Tclose( typeId_ );
    }
    if( -1 != spaceId_ )
    {
        H5Sclose( spaceId_ );
    }
    return true;
}

HDF5Attribute::HDF5Attribute( HDF5Item* parent, const char* name, const char* value )
    : value_( ( void* )value ),
      vlArrayDelete_( false )
{
    typeId_ = H5Tcopy( H5T_C_S1 );
    H5Tset_size( typeId_, strlen( value ) );
    //hsize_t currentDims[1] = {1};
    //spaceId_ = H5Screate_simple( 1, currentDims, NULL );
    spaceId_ = H5Screate( H5S_SCALAR );
    if( DEBUG_HDF5 ) printf( "Creating attribute %s=%s for %d\n", name, value, parent->getId() );
    id_ = H5Acreate( parent->getId(), name, typeId_, spaceId_,
                     H5P_DEFAULT,  H5P_DEFAULT );
}

HDF5Attribute::HDF5Attribute( HDF5Item* parent, const char* name, const int value )
    : value_( ( void* )value ),
      vlArrayDelete_( false )
{
    typeId_ = H5Tcopy( H5T_NATIVE_INT );
    H5Tset_size( typeId_, 1 );
    //hsize_t currentDims[1] = {1};
    //spaceId_ = H5Screate_simple( 1, currentDims, NULL );
    spaceId_ = H5Screate( H5S_SCALAR );
    if( DEBUG_HDF5 ) printf( "Creating attribute %s=%d for %d\n", name, value, parent->getId() );
    id_ = H5Acreate( parent->getId(), name, typeId_, spaceId_,
                     H5P_DEFAULT,  H5P_DEFAULT );
}

HDF5Attribute::HDF5Attribute( HDF5Item* parent, const char* name, FlexArray<Str*>& fields )
    : value_( new hvl_t[fields.size()] ),
      vlArrayDelete_( true )
{
    if( DEBUG_HDF5 ) printf( "Creating field array attribute %s for %d\n", name, parent->getId() );
    typeId_ = H5Tvlen_create( H5T_C_S1 );
    H5Tset_size( typeId_, 1 );
    for( unsigned int i = 0 ; i < fields.size(); ++i )
    {
        const Str* varName = fields.get( i );
        ( ( hvl_t* )value_ )[i].len = varName->length();
        ( ( hvl_t* )value_ )[i].p = ( void* )varName->cStr();
    }
    hsize_t currentDims[1] = {fields.size()};
    spaceId_ = H5Screate_simple( 1, currentDims, NULL );
    id_ = H5Acreate( parent->getId(), name, typeId_, spaceId_,
                     H5P_DEFAULT,  H5P_DEFAULT );
}

HDF5Attribute::~HDF5Attribute()
{
    if( vlArrayDelete_ )
    {
        delete []( const hvl_t* )value_;
    }
}

bool HDF5Attribute::writeHeader()
{
    switch( H5Tget_class( typeId_ ) )
    {
    case H5T_INTEGER:
    {
        size_t temp = ( size_t )value_;
        return -1 != H5Awrite( id_, typeId_, ( void* )&temp );
    }
    default:
        return -1 != H5Awrite( id_, typeId_, value_ );
    }

}

bool HDF5Attribute::finalize()
{
    if( -1 != id_ )
    {
        H5Aclose( id_ );
    }
    return HDF5Item::finalize();
}

void HDF5HasAttributes::addAttribute( const char* name, const char* value )
{
    attributes_.push( new HDF5Attribute( this, name, value ) );
}

void HDF5HasAttributes::addAttribute( const char* name, const int value )
{
    attributes_.push( new HDF5Attribute( this, name, value ) );
}

bool HDF5HasAttributes::writeHeader()
{
    for( unsigned int i = 0; i < attributes_.size(); ++i )
    {
        HDF5Attribute* attribute = attributes_.get( i );
        attribute->writeHeader();
    }
    return true;
}

bool HDF5HasAttributes::finalize()
{
    for( unsigned int i = 0; i < attributes_.size(); ++i )
    {
        HDF5Attribute* attribute = attributes_.get( i );
        attribute->finalize();
    }
    return true;
}

HDF5Group::HDF5Group( HDF5Group* parent, const char* name, bool useMetadata )
    : HDF5HasAttributes( parent ),
      fields_( true ),
      useMetadata_( useMetadata )
{
    if( DEBUG_HDF5 ) printf( "Creating group %s\n", name );
    if( NULL != parent_ )
    {
        id_ = H5Gcreate( parent_->id_, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
        if( isMatlab() && useMetadata_ )
        {
            addAttribute( "MATLAB_class", "struct" );
        }
    }
}

HDF5Group::~HDF5Group()
{
    groupMap_.clear( true );
    datasetMap_.clear( true );
}

bool HDF5Group::writeHeader()
{
    if( NULL != parent_ && useMetadata_ )
    {
        attributes_.push( new HDF5Attribute( this, "MATLAB_fields", fields_ ) );
    }
    HDF5HasAttributes::writeHeader();
    for( unsigned int i = 0; i < groupMap_.size(); ++i )
    {
        HDF5Group* group = groupMap_.getIndexed( i );
        group->writeHeader();
    }
    for( unsigned int i = 0; i < datasetMap_.size(); ++i )
    {
        HDF5Dataset* dataset = datasetMap_.getIndexed( i );
        dataset->writeHeader();
    }
    return true;
}

bool HDF5Group::finalize()
{
    for( unsigned int i = 0; i < groupMap_.size(); ++i )
    {
        HDF5Group* group = groupMap_.getIndexed( i );
        group->finalize();
    }
    for( unsigned int i = 0; i < datasetMap_.size(); ++i )
    {
        HDF5Dataset* dataset = datasetMap_.getIndexed( i );
        dataset->finalize();
    }
    if( NULL != parent_ )
    {
        return -1 != H5Gclose( id_ );
    }
    return HDF5HasAttributes::finalize();
}

HDF5Group* HDF5Group::findGroup( const char* name )
{
    return groupMap_.get( name );
}

HDF5Group* HDF5Group::addGroup( const char* name, bool useMetadata )
{
    HDF5Group* group = findGroup( name );
    if( NULL == group )
    {
        group = new HDF5Group( this, name, useMetadata );
        if( useMetadata_ )
        {
            fields_.push( new Str( name ) );
        }
        groupMap_.put( name, group );
    }
    return group;
}

HDF5Dataset* HDF5Group::findDataset( const char* name )
{
    return datasetMap_.get( name );
}

HDF5Dataset* HDF5Group::addScalarDataset( const char* name, const char* value, size_t length, const Unit& unit )
{
    HDF5Dataset* dataset = findDataset( name );
    if( NULL == dataset )
    {
        dataset = new HDF5Dataset( this, name, value, length, unit );
        if( useMetadata_ )
        {
            fields_.push( new Str( name ) );
        }
        datasetMap_.put( name, dataset );
    }
    return dataset;
}

HDF5Dataset* HDF5Group::addDataset( const char* name, BinaryDataType binaryType,
                                    const Unit& unit, bool useChunks,
                                    BlobType blobType, int rank,
                                    unsigned short m, unsigned short n, unsigned short o )
{
    HDF5Dataset* dataset = findDataset( name );
    if( NULL == dataset )
    {
        dataset = new HDF5Dataset( this, name, binaryType, unit, useChunks, blobType, rank, m, n, o );
        if( useMetadata_ )
        {
            fields_.push( new Str( name ) );
        }
        datasetMap_.put( name, dataset );
    }
    return dataset;
}

HDF5File::HDF5File( const char* filename, bool matlab )
    : HDF5Group( ( HDF5Group* )NULL, "/", false ),
      matlab_( matlab ),
      filename_( filename ),
      tempFilename_( Str::EMPTY_STR )
{
    // Delete the file if it exists...
    int error = remove( filename );
    if( 0 != error && ENOENT != errno )
    {
        printf( "Error clearing file %s due to error %d: %s.\n", filename, errno, strerror( errno ) );
    }

    if( isMatlab() )
    {
        tempFilename_ = Str( filename ) + ".tmp";
        filename = tempFilename_.cStr();
        int error = remove( filename );
        if( 0 != error && ENOENT != errno )
        {
            printf( "Error clearing file %s due to error %d: %s.\n", filename, errno, strerror( errno ) );
        }
    }

    unsigned int openFlags = H5F_ACC_TRUNC;

    hid_t accPropId = H5Pcreate( H5P_FILE_ACCESS );
    H5Pset_fclose_degree( accPropId, H5F_CLOSE_STRONG );

    id_ = H5Fcreate( filename, openFlags,
                     H5P_DEFAULT, accPropId );

}

bool HDF5File::finalize()
{
    HDF5Group::finalize();
    H5Fflush( id_, H5F_SCOPE_GLOBAL );
    if( -1 == H5Fclose( id_ ) )
    {
        return false;
    }

    if( isMatlab() )
    {
        FileOutStream fos( filename_.cStr(), false, false );
        FileInStream fis( tempFilename_.cStr() );
        if( fos.isWritable() && fis.isReadable() )
        {
            const int BUF_SIZE( 0x1000 );
            const int HEADER_SIZE( 0x200 );
            char buffer[BUF_SIZE] = {0};
            memcpy( buffer, "MATLAB 7.3 MAT-file, Platform: GLNX86, Created on: ", 0x33 );
            struct tm now = Timestamp::Now().asStructTm();
            asctime_r( &now, buffer + 0x33 );
            memcpy( buffer + 0x4B, " HDF5 schema 1.00 .                      ", 0x29 );
            memcpy( buffer + 0x7D, "\x02IM", 3 );
            size_t bytesRead( HEADER_SIZE );
            while( bytesRead != 0 )
            {
                fos.write( buffer, bytesRead );
                bytesRead = fis.read( buffer, BUF_SIZE ).bytesRead();
            }
        }
        else
        {
            printf( "Error creating Matlab header in file %s due to error %d: %s.\n", filename_.cStr(), errno, strerror( errno ) );
        }
        fis.close();
        fos.close();
        // Try for up to 15 seconds to delete the temporary file...
        printf( "Cleaning up temporary file...\n" );
        int error = remove( tempFilename_.cStr() );
        for( int deleteTry = 0; deleteTry < 5; ++deleteTry )
        {
            if( 0 == error || ENOENT == errno )
            {
                break;
            }
            sleep( 3 );
            error = remove( tempFilename_.cStr() );;
        }
        if( 0 != error && ENOENT != errno )
        {
            printf( "Error clearing temporary file %s due to error %d: %s.\n", tempFilename_.cStr(), errno, strerror( errno ) );
        }
    }
    return true;
}

HDF5Dataset::HDF5Dataset( HDF5Group* parent, const char* name, const char* value, size_t length, const Unit& unit )
    : HDF5HasAttributes( parent ),
      dataValue_( NULL ),
      blobType_( NOT_BLOB ),
      rank_( 1 ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 ),
      propsId_( -1 ),
      numRecs_( 0 )
{
    if( DEBUG_HDF5 ) printf( "Creating scalar dataset %s for %d\n", name, parent->getId() );
    hid_t memTypeId;
    const char* matlabClass;
    bool needDecode = false;
    if( unit == Units::BYTE )
    {
        memTypeId = H5Tcopy( H5T_NATIVE_UINT8 );
        typeId_ = H5Tcopy( H5T_NATIVE_UINT8 );
        matlabClass = "uint8";
    }
    else
    {
        memTypeId = H5Tcopy( H5T_NATIVE_CHAR );
        typeId_ = H5Tcopy( H5T_STD_U16LE );
        matlabClass = "char";
        needDecode = true;
    }
    hsize_t currentDims[2] = {length, 1};
    spaceId_ = H5Screate_simple( 2, currentDims, NULL );
    id_ = H5Dcreate2( parent->getId(), name, typeId_, spaceId_,
                      H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );

    if( isMatlab() )
    {
        addAttribute( "MATLAB_class", matlabClass );
        if( needDecode )
        {
            addAttribute( "MATLAB_int_decode", 2 );
        }
    }

    H5Dwrite( id_, memTypeId, spaceId_, spaceId_, H5P_DEFAULT, value );
    H5Tclose( memTypeId );
    if( DEBUG_HDF5 ) printf( "dataset id= %d\n", id_ );
}

HDF5Dataset::HDF5Dataset( HDF5Group* parent, const char* name, BinaryDataType binaryType,
                          const Unit& unit, bool useChunks,
                          BlobType blobType, int rank, unsigned short m,
                          unsigned short n, unsigned short o )
    : HDF5HasAttributes( parent ),
      dataValue_( unit.dataValue( binaryType, blobType ) ),
      blobType_( blobType ),
      rank_( rank ),
      m_( m ),
      n_( n ),
      o_( o ),
      propsId_( -1 ),
      numRecs_( 0 )
{
    if( DEBUG_HDF5 ) printf( "Creating dataset %s for %d, rank=%d\n", name, parent->getId(), rank );
    const char* matlabClass = NULL;

    switch( binaryType )
    {
    case MULTIVALUE: // one Str, store as cell array -- also used for blobValues
        if( blobType_ == NOT_BLOB || rank_ < 2 )
        {
            typeId_ = H5Tcopy( H5T_STD_REF_OBJ );
            matlabClass = "cell";
        }
        else
        {
            typeId_ = TypeIdFromBlobType( blobType_ );
            matlabClass = MatlabClassFromBlobType( blobType_ );
        }
        break;
    case UCHAR1: // Internally, an int, but saved as UCHAR1
        typeId_ = H5Tcopy( H5T_NATIVE_UCHAR );
        matlabClass = "uint8";
        break;
    case SHORT2: // Internally, an int, but saved as SHORT2
        typeId_ = H5Tcopy( H5T_NATIVE_SHORT );
        matlabClass = "int16";
        break;
    case USHORT2: // Internally, an int, but saved as USHORT2
        typeId_ = H5Tcopy( H5T_NATIVE_USHORT );
        matlabClass = "uint16";
        break;
    case INT4:
        typeId_ = H5Tcopy( H5T_NATIVE_INT );
        matlabClass = "int32";
        break;
    case FLOAT2: // 1.8.7 (s7e8) float (127 excess)
    case FLOAT3: // 1.8.15 (s15e8) float (127 excess)
    case FLOAT4: // 1.8.23 (s23e8) float (127 excess)
    case DOUBLE4: // 1.8.23 (s23e8) float (127 excess), internally double
        typeId_ = H5Tcopy( H5T_NATIVE_FLOAT );
        matlabClass = "single";
        break;
    case DOUBLE6: // 1.11.36 (s36e11) double (1023 excess)
    case DOUBLE8:
    case NO_TYPE:
        typeId_ = H5Tcopy( H5T_NATIVE_DOUBLE );
        matlabClass = "double";
        break;
    }

    hsize_t currentDims[MAX_RANK] = {0, 1};
    hsize_t maxDims[MAX_RANK] = {H5S_UNLIMITED, 1};
    hsize_t chunk_size[MAX_RANK] = {useChunks ? 1024 : 1, 1 };
    if( rank > 1 )
    {
        currentDims[1] = maxDims[1] = chunk_size[1] = m_;
        chunk_size[0] = AuvMath::Max( ( hsize_t )1, chunk_size[0] / m_ );
    }
    if( rank > 2 )
    {
        currentDims[2] = maxDims[2] = chunk_size[2] = n_;
        chunk_size[0] = AuvMath::Max( ( hsize_t )1, chunk_size[0] / n_ );
    }
    if( rank > 3 )
    {
        currentDims[3] = maxDims[3] = chunk_size[3] = o_;
        chunk_size[0] = AuvMath::Max( ( hsize_t )1, chunk_size[0] / o_ );
    }

    spaceId_ = H5Screate_simple( AuvMath::Max( rank, 2 ), currentDims, maxDims );
    propsId_ = H5Pcreate( H5P_DATASET_CREATE );

    if( H5Pset_chunk( propsId_, AuvMath::Max( rank, 2 ), chunk_size ) < 0 )
    {
        printf( "Error: fail to set chunk\n" );
    }
    if( useChunks && ( H5Pset_deflate( propsId_, 1 ) < 0 ) )
    {
        printf( "Error: fail to set deflate filter\n" );
    }

    id_ = H5Dcreate( parent->getId(), name, typeId_, spaceId_,
                     H5P_DEFAULT, propsId_, H5P_DEFAULT );

    if( isMatlab() )
    {
        addAttribute( "MATLAB_class", matlabClass );
    }
    if( DEBUG_HDF5 ) printf( "dataset id= %d\n", id_ );
}

HDF5Dataset::~HDF5Dataset()
{
    if( NULL != dataValue_ )
    {
        delete dataValue_;
    }
}

DataValue* HDF5Dataset::getDataValue()
{
    return dataValue_;
}

void memfill( void *dest, size_t destsize, size_t elemsize )
{
    char   *nextdest = ( char * ) dest + elemsize;
    size_t movesize,
           donesize = elemsize;
    destsize -= elemsize;
    while( destsize )
    {
        movesize = ( donesize < destsize ) ? donesize : destsize;
        memcpy( nextdest, dest, movesize );
        nextdest += movesize;
        destsize -= movesize;
        donesize += movesize;
    }
}

bool HDF5Dataset::writeDataValue()
{
    size_t nEntries = 1;
    hsize_t start[MAX_RANK] = {numRecs_, 0};
    ++numRecs_;

    hsize_t count[MAX_RANK] = {1, 1};
    hsize_t size[MAX_RANK] = {numRecs_, 1};

    if( rank_ > 1 )
    {
        count[1] = size[1] = m_;
        nEntries *= m_;
    }
    if( rank_ > 2 )
    {
        count[2] = size[2] = n_;
        nEntries *= n_;
    }
    if( rank_ > 3 )
    {
        count[3] = size[3] = o_;
        nEntries *= o_;
    }

    H5Dset_extent( id_, size );
    // Free current space thingy
    H5Sclose( spaceId_ );
    // Re-read it.
    spaceId_ = H5Dget_space( id_ );
    H5Sselect_hyperslab( spaceId_, H5S_SELECT_SET, start, NULL, count, NULL );
    hid_t memSpaceId = H5Screate_simple( AuvMath::Max( rank_, 2 ), count, NULL );
    herr_t status = 0;
    switch( dataValue_->getBinaryType() )
    {
    case MULTIVALUE: // one Str
        if( blobType_ == NOT_BLOB || rank_ < 2 )
        {
            HDF5Group* rootGroup = getRoot();
            HDF5Group* refGroup = rootGroup->findGroup( "#refs#" );
            if( NULL == refGroup )
            {
                refGroup = rootGroup->addGroup( "#refs#", false );
                refGroup->writeHeader();
            }
            const char* const base62 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            int recNum = ( int )refGroup->getDatasetCount() + 1;
            char recName[15] = {"/#refs#/"};
            for( size_t recIndex = 0; recIndex < sizeof( recName ) - 1; ++recIndex )
            {
                recName[recIndex + 8] = base62[recNum % 62];
                recName[recIndex + 9] = 0;
                recNum /= 62;
                if( recNum == 0 )
                {
                    break;
                }
            }
            const Str& str = ( ( StrValue* ) dataValue_ )->asString();
            const Unit& unit = dataValue_->getUnit();
            HDF5Dataset* refDataset = refGroup->addScalarDataset( recName + 8, str.cStr(), str.length(), unit );

            refDataset->writeHeader();
            hobj_ref_t  ref;
            H5Rcreate( &ref, rootGroup->getId(), recName, H5R_OBJECT, -1 );

            status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                               &ref );
        }
        else
        {
            size_t typeSize = H5Tget_size( typeId_ );
            size_t size = nEntries * typeSize;
            const Str& str = ( ( StrValue* ) dataValue_ )->asString();
            unsigned char* data = new unsigned char[size]();   // new foo[bar]() inits to zero.
            // init array to zero (ints) or NaN (floats).
            switch( blobType_ )
            {
            case BLOB_FLOAT32LE: // 4 byte single precision float, little endian (x86 single)
                if( size >= 4 )
                {
                    memcpy( data, "\x00\x00\xc0\xff", 4 );
                    memfill( data, size, 4 );
                }
                break;
            case BLOB_FLOAT32BE: // 4 byte single precision float, big endian (68k single)
                if( size >= 4 )
                {
                    memcpy( data, "\xff\xc0\x00\x00", 4 );
                    memfill( data, size, 4 );
                }
                break;
            case BLOB_FLOAT64LE: // 8 byte double precision float, little endian (x86 double)
                if( size >= 8 )
                {
                    memcpy( data, "\x00\x00\x00\x00\x00\x00\xf8\xff", 8 );
                    memfill( data, size, 8 );
                }
                break;
            case BLOB_FLOAT64BE: // 8 byte double precision float, big endian (68k double)
                if( size >= 8 )
                {
                    memcpy( data, "\xff\xf8\x00\x00\x00\x00\x00\x00", 8 );
                    memfill( data, size, 8 );
                }
                break;
            default:
                memset( data, 0, size );
            }
            memcpy( data, str.cStr(), AuvMath::Min( size, str.size() ) );
            status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                               data );
            delete[] data;
        }
        break;
    case UCHAR1: // Internally, an int, but saved as UCHAR1
    {
        unsigned char data;
        dataValue_->copyTo( dataValue_->getUnit(), data );
        status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                           &data );
    }
    break;
    case SHORT2: // Internally, an int, but saved as SHORT2
    case USHORT2: // Internally, an int, but saved as USHORT2
    case INT4:
    {
        int data;
        dataValue_->copyTo( dataValue_->getUnit(), data );
        status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                           &data );
    }
    break;
    case FLOAT2: // 1.8.7 (s7e8) float (127 excess)
    case FLOAT3: // 1.8.15 (s15e8) float (127 excess)
    case FLOAT4: // 1.8.23 (s23e8) float (127 excess)
    case DOUBLE4: // 1.8.23 (s23e8) float (127 excess), internally double
    {
        float data;
        dataValue_->copyTo( dataValue_->getUnit(), data );
        status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                           &data );
    }
    break;
    case DOUBLE6: // 1.11.36 (s36e11) double (1023 excess)
    case DOUBLE8:
    case NO_TYPE:
    default:
    {
        double data;
        dataValue_->copyTo( dataValue_->getUnit(), data );
        status = H5Dwrite( id_, typeId_, memSpaceId, spaceId_, H5P_DEFAULT,
                           &data );
    }
    break;
    }
    H5Sclose( memSpaceId );

    if( status < 0 )
    {
        printf( "Error: fail to write to dataset\n" );
        return false;
    }
    return true;
}

int HDF5Dataset::TypeIdFromBlobType( BlobType blobType )
{
    switch( blobType )
    {
    default:
    case NOT_BLOB:
        return H5Tcopy( H5T_STD_REF_OBJ );

    case BLOB_INT8:
        return H5Tcopy( H5T_STD_I8LE );
    case BLOB_INT16LE:
        return H5Tcopy( H5T_STD_I16LE );
    case BLOB_INT16BE:
        return H5Tcopy( H5T_STD_I16BE );
    case BLOB_INT32LE:
        return H5Tcopy( H5T_STD_I32LE );
    case BLOB_INT32BE:
        return H5Tcopy( H5T_STD_I32BE );
    case BLOB_INT64LE:
        return H5Tcopy( H5T_STD_I64LE );
    case BLOB_INT64BE:
        return H5Tcopy( H5T_STD_I64BE );

    case BLOB_UINT8:
        return H5Tcopy( H5T_STD_U8LE );
    case BLOB_UINT16LE:
        return H5Tcopy( H5T_STD_U16LE );
    case BLOB_UINT16BE:
        return H5Tcopy( H5T_STD_U16BE );
    case BLOB_UINT32LE:
        return H5Tcopy( H5T_STD_U32LE );
    case BLOB_UINT32BE:
        return H5Tcopy( H5T_STD_U32BE );
    case BLOB_UINT64LE:
        return H5Tcopy( H5T_STD_U64LE );
    case BLOB_UINT64BE:
        return H5Tcopy( H5T_STD_U64BE );

    case BLOB_FLOAT32LE:
        return H5Tcopy( H5T_IEEE_F32LE );
    case BLOB_FLOAT32BE:
        return H5Tcopy( H5T_IEEE_F32BE );
    case BLOB_FLOAT64LE:
        return H5Tcopy( H5T_IEEE_F64LE );
    case BLOB_FLOAT64BE:
        return H5Tcopy( H5T_IEEE_F64BE );
    }
}

const char* HDF5Dataset::MatlabClassFromBlobType( BlobType blobType )
{
    switch( blobType )
    {
    default:
    case NOT_BLOB:
        return "char";

    case BLOB_INT8:
        return "int8";
    case BLOB_INT16LE:
    case BLOB_INT16BE:
        return "int16";
    case BLOB_INT32LE:
    case BLOB_INT32BE:
        return "int32";
    case BLOB_INT64LE:
    case BLOB_INT64BE:
        return "int64";

    case BLOB_UINT8:
        return "uint8";
    case BLOB_UINT16LE:
    case BLOB_UINT16BE:
        return "uint16";
    case BLOB_UINT32LE:
    case BLOB_UINT32BE:
        return "uint32";
    case BLOB_UINT64LE:
    case BLOB_UINT64BE:
        return "uint64";

    case BLOB_FLOAT32LE:
    case BLOB_FLOAT32BE:
        return "single";
    case BLOB_FLOAT64LE:
    case BLOB_FLOAT64BE:
        return "double";
    }
}

bool HDF5Dataset::finalize()
{
    H5Dclose( id_ );
    if( -1 != propsId_ )
    {
        H5Pclose( propsId_ );
    }
    return HDF5HasAttributes::finalize();
}
