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

#include "BlobValue.h"

#include "data/DataWriter.h"
#include "data/ElementURI.h"
#include "utils/FlexArray.h"
#include <sys/types.h>
#include <stdint.h>
#include <typeinfo>

const size_t BlobValue::ELEMENT_SIZE[BLOB_TYPE_COUNT] =
{
    0, // NOT_BLOB,       // Not a blob

    1, // BLOB_INT8,      // 8 bit signed integer (char)
    2, // BLOB_INT16LE,   // 2 byte signed integer, little endian (x86 short int)
    2, // BLOB_INT16BE,   // 2 byte signed integer, big endian (68k short int)
    4, // BLOB_INT32LE,   // 4 byte signed integer, little endian (x86 long int)
    4, // BLOB_INT32BE,   // 4 byte signed integer, big endian (68k long int)
    8, // BLOB_INT64LE,   // 8 byte signed integer, little endian (x86 long long int)
    8, // BLOB_INT64BE,   // 8 byte signed integer, big endian (68klong long int)

    1, // BLOB_UINT8,     // 8 bit unsigned integer (unsigned char)
    2, // BLOB_UINT16LE,  // 2 byte unsigned integer, little endian (x86 unsigned short int)
    2, // BLOB_UINT16BE,  // 2 byte unsigned integer, big endian (68k unsigned short int)
    4, // BLOB_UINT32LE,  // 4 byte unsigned integer, little endian (x86 unsigned long int)
    4, // BLOB_UINT32BE,  // 4 byte unsigned integer, big endian (68k unsigned long int)
    8, // BLOB_UINT64LE,  // 8 byte unsigned integer, little endian (x86 unsigned long long int)
    8, // BLOB_UINT64BE,  // 8 byte unsigned integer, big endian (68k unsigned long long int)

    4, // BLOB_FLOAT32LE, // 4 byte single precision float, little endian (x86 single)
    4, // BLOB_FLOAT32BE, // 4 byte single precision float, big endian (68k single)
    8, // BLOB_FLOAT64LE, // 8 byte double precision float, little endian (x86 double)
    8, // BLOB_FLOAT64BE, // 8 byte double precision float, big endian (68k double)

};

/// Empty constructor
BlobValue::BlobValue( const Unit& unit, const BlobType blobType )
    : StrValue( NULL, 0, unit ),
      blobType_( blobType ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{}

/// ElementURI constructior
BlobValue::BlobValue( const ElementURI& elementURI )
    : StrValue( NULL, 0, elementURI.getUnit() ),
      blobType_( elementURI.getBlobType() ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{}

/// copy constructor
BlobValue::BlobValue( const BlobValue& blobValue )
    : StrValue( blobValue ),
      blobType_( blobValue.getBlobType() ),
      m_( blobValue.getM() ),
      n_( blobValue.getN() ),
      o_( blobValue.getO() )
{}

/// raw constructor
BlobValue::BlobValue( const void* bytes, size_t elements, const Unit& unit, const BlobType blobType )
    : StrValue( NULL, 0, unit ),
      blobType_( blobType ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{
    setFromRaw( bytes, elements );
}

/// double array constructor
BlobValue::BlobValue( const double* array, size_t elements, const Unit& unit, const BlobType blobType )
    : StrValue( NULL, 0, unit ),
      blobType_( blobType ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{
    setFrom( unit, array, elements );
}

/// float array constructor
BlobValue::BlobValue( const float* array, size_t elements, const Unit& unit, const BlobType blobType )
    : StrValue( NULL, 0, unit ),
      blobType_( blobType ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{
    setFrom( unit, array, elements );
}

/// int array constructor
BlobValue::BlobValue( const int* array, size_t elements, const Unit& unit, const BlobType blobType )
    : StrValue( NULL, 0, unit ),
      blobType_( blobType ),
      m_( 0 ),
      n_( 0 ),
      o_( 0 )
{
    setFrom( unit, array, elements );
}

/// Return a pointer to a new copy
BlobValue* BlobValue::copy() const
{
    return new BlobValue( *this );
}

// set from  raw data
bool BlobValue::setFromRaw( const void* bytes, size_t elements )
{
    size_t length = elements * ElementSize( blobType_ );
    value_.set( ( const char* )bytes, length );
    m_ = elements;
    n_ = o_ = 0;
    return true;
}

// set from a 1-d array (of double, float, or int)
template<typename T>
bool BlobValue::setFrom( const Unit& unit, const T* array, size_t elements )
{
    size_t length = elements * ElementSize( blobType_ );
    m_ = elements;
    n_ = o_ = 0;
    if( conversionNeeded<T>( unit ) )
    {
        value_.set( NULL, length );
        for( unsigned int i = 0; i < elements; ++i )
        {
            set( i, unit, *( array + i ) );
        }
    }
    else
    {
        value_.set( ( const char* )array, length );
    }
    return true;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template bool BlobValue::setFrom( const Unit& unit, const double* array, size_t elements );
template bool BlobValue::setFrom( const Unit& unit, const float* array, size_t elements );
template bool BlobValue::setFrom( const Unit& unit, const int* array, size_t elements );
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

// set from a 2-d array pointer (of double, float, or int)
template<typename T>
bool BlobValue::setFrom( const Unit& unit, const T** array, int m, int n )
{
    size_t length = m * n * ElementSize( blobType_ );
    m_ = m;
    n_ = n;
    o_ = 0;
    value_.set( NULL, length );
    if( conversionNeeded<T>( unit ) )
    {
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                set( i * n + j, unit, array[i][j] );
            }
        }
    }
    else
    {
        length = n * ElementSize( blobType_ );
        for( int i = 0; i < m; ++i )
        {
            size_t offset = i * length;
            memcpy( ( char* )value_.cStr() + offset, ( const char* )( array[i] ), length );
        }
    }
    return true;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template bool BlobValue::setFrom( const Unit& unit, const double** array, int m, int n );
template bool BlobValue::setFrom( const Unit& unit, const float** array, int m, int n );
template bool BlobValue::setFrom( const Unit& unit, const int** array, int m, int n );
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

// set from a 3-d array pointer (of double, float, or int)
template<typename T>
bool BlobValue::setFrom( const Unit& unit, const T*** array, int m, int n, int o )
{
    size_t length = m * n * o * ElementSize( blobType_ );
    m_ = m;
    n_ = n;
    o_ = o;
    value_.set( NULL, length );
    if( conversionNeeded<T>( unit ) )
    {
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                for( int k = 0; k < o; ++k )
                {
                    set( i * n * o + j * o + k, unit, array[i][j][k] );
                }
            }
        }
    }
    else
    {
        length = o * ElementSize( blobType_ );
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                size_t offset = ( i * n + j ) * length;
                memcpy( ( char* )value_.cStr() + offset, ( const char* )( array[i][j] ), length );
            }
        }
    }
    return true;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template bool BlobValue::setFrom( const Unit& unit, const double*** array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const float*** array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const int*** array, int m, int n, int o );
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

// set from a 1-d, 2-d, or 3-d flattened array pointer (of double, float, or int)
template<typename T>
bool BlobValue::setFrom( const Unit& unit, const T* array, int m, int n, int o )
{
    size_t length = m * ( n == 0 ? 1 : n ) * ( o == 0 ? 1 : o ) * ElementSize( blobType_ ), idx = 0;
    m_ = m;
    n_ = n;
    o_ = o;
    value_.set( NULL, length );
    if( conversionNeeded<T>( unit ) )
    {
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n || j == 0; ++j )
            {
                for( int k = 0; k < o || k == 0; ++k )
                {
                    set( idx, unit, array[ idx ] );
                    ++idx;
                }
            }
        }
    }
    else
    {
        memcpy( ( char* )value_.cStr(), &( array[0] ), length );
    }
    return true;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template bool BlobValue::setFrom( const Unit& unit, const bool* array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const unsigned char* array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const double* array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const float* array, int m, int n, int o );
template bool BlobValue::setFrom( const Unit& unit, const int* array, int m, int n, int o );
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

/// set DataValue value
bool BlobValue::setFrom( const DataValue& dataValue )
{
    if( !dataValue.equals( *this ) )
    {
        double tempValue;
        const BlobValue* blobValue;
        switch( dataValue.getBinaryType() )
        {
        case NO_TYPE:
        default:
            return false;
        case MULTIVALUE:
            blobValue = dynamic_cast<const BlobValue*>( &dataValue );
            if( blobValue == NULL )
            {
                return false;
            }
            value_ = blobValue->value_;
            typeSize_ = value_.length();
            blobType_ = blobValue->getBlobType();
            m_ = blobValue->getM();
            n_ = blobValue->getN();
            o_ = blobValue->getO();
            break;
        case UCHAR1:
        case SHORT2:
        case USHORT2:
        case INT4:
        case FLOAT2:
        case FLOAT3:
        case FLOAT4:
        case DOUBLE4:
        case DOUBLE6:
        case DOUBLE8:
            if( !dataValue.copyTo( dataValue.getUnit(), tempValue ) )
            {
                return false;
            }
            setFrom( dataValue.getUnit(), &tempValue, 1, 0, 0 );
            break;
        }
        return true;
    }
    return true;
};

/// Set an individual value
bool BlobValue::set( size_t index, const Unit& unit, double value )
{
    if( index >= getElements() )
    {
        return false;
    }
    if( &unit != unit_ )
    {
        if( unit != unit.getBaseUnit() )
        {
            value = unit.getSI( value );
        }
        if( unit_ != &unit_->getBaseUnit() )
        {
            value = unit_->getScaled( value );
        }
    }

    switch( blobType_ )
    {
    case  NOT_BLOB:       // Not a blob
        break;
    case BLOB_INT8:      // 8 bit signed integer (char)
        setElement( index, ( int8_t )value, false );
        break;
    case BLOB_INT16LE:   // 2 byte signed integer, little endian (x86 short int)
        setElement( index, ( int16_t )value, false );
        break;
    case BLOB_INT16BE:   // 2 byte signed integer, big endian (68k short int)
        setElement( index, ( int16_t )value, true );
        break;
    case BLOB_INT32LE:   // 4 byte signed integer, little endian (x86 long int)
        setElement( index, ( int16_t )value, false );
        break;
    case BLOB_INT32BE:   // 4 byte signed integer, big endian (68k long int)
        setElement( index, ( int16_t )value, true );
        break;
    case BLOB_INT64LE:   // 8 byte signed integer, little endian (x86 long long int)
        setElement( index, ( int64_t )value, false );
        break;
    case BLOB_INT64BE:   // 8 byte signed integer, big endian (68klong long int)
        setElement( index, ( int64_t )value, true );
        break;

    case BLOB_UINT8:     // 8 bit unsigned integer (unsigned char)
        setElement( index, ( uint8_t )value, false );
        break;
    case BLOB_UINT16LE:  // 2 byte unsigned integer, little endian (x86 unsigned short int)
        setElement( index, ( uint16_t )value, false );
        break;
    case BLOB_UINT16BE:  // 2 byte unsigned integer, big endian (68k unsigned short int)
        setElement( index, ( uint16_t )value, true );
        break;
    case BLOB_UINT32LE:  // 4 byte unsigned integer, little endian (x86 unsigned long int)
        setElement( index, ( uint16_t )value, false );
        break;
    case BLOB_UINT32BE:  // 4 byte unsigned integer, big endian (68k unsigned long int)
        setElement( index, ( uint16_t )value, true );
        break;
    case BLOB_UINT64LE:  // 8 byte unsigned integer, little endian (x86 unsigned long long int)
        setElement( index, ( uint64_t )value, false );
        break;
    case BLOB_UINT64BE:  // 8 byte unsigned integer, big endian (68k unsigned long long int)
        setElement( index, ( uint64_t )value, true );
        break;

    case BLOB_FLOAT32LE: // 4 byte single precision float, little endian (x86 single)
        setElement( index, ( float )value, false );
        break;
    case BLOB_FLOAT32BE: // 4 byte single precision float, big endian (68k single)
        setElement( index, ( float )value, true );
        break;
    case BLOB_FLOAT64LE: // 8 byte double precision float, little endian (x86 double)
        setElement( index, value, false );
        break;
    case BLOB_FLOAT64BE: // 8 byte double precision float, big endian (68k double)
        setElement( index, value, true );
        break;
    case BLOB_TYPE_COUNT:
        break;
    }
    return true;
}

// copy to a 1-d array (of doubles, float, or int)
template<typename T>
int BlobValue::copyTo( const Unit& unit, T* array, size_t elements ) const
{
    if( elements > getElements() )
    {
        elements = getElements();
    }
    if( conversionNeeded<T>( unit ) )
    {
        double value;
        for( unsigned int i = 0; i < elements; ++i )
        {
            get( i, unit, value );
            *( array + i ) = value;
        }
    }
    else
    {
        memcpy( ( void* )array, getBytes(), elements * sizeof( T ) );
    }
    return elements;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template int BlobValue::copyTo( const Unit& unit, double* array, size_t elements ) const;
template int BlobValue::copyTo( const Unit& unit, float* array, size_t elements ) const;
template int BlobValue::copyTo( const Unit& unit, int* array, size_t elements ) const;
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

// copy to a 2-d pointer array (of doubles, float, or int)
template<typename T>
int BlobValue::copyTo( const Unit& unit, T** array, int m, int n ) const
{
    size_t elements = m * n;
    if( elements > getElements() )
    {
        m = getElements() / n;
        elements = m * n;
    }
    if( conversionNeeded<T>( unit ) )
    {
        double value;
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                get( i * n + j, unit, value );
                array[i][j] = value;
            }
        }
    }
    else
    {
        size_t length = n * sizeof( T );
        for( int i = 0; i < m; ++i )
        {
            size_t offset = i * length;
            memcpy( ( char* )( array[i] ), value_.cStr() + offset, length );
        }
    }
    return elements;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template int BlobValue::copyTo( const Unit& unit, double** array, int m, int n ) const;
template int BlobValue::copyTo( const Unit& unit, float** array, int m, int n ) const;
template int BlobValue::copyTo( const Unit& unit, int** array, int m, int n ) const;
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

// copy to a 3-d pointer array (of doubles, float, or int)
template<typename T>
int BlobValue::copyTo( const Unit& unit, T*** array, int m, int n, int o ) const
{
    size_t elements = m * n * o;
    if( elements > getElements() )
    {
        m = getElements() / n / o;
        elements = m * n;
    }
    if( conversionNeeded<T>( unit ) )
    {
        double value;
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                for( int k = 0; k < o; ++k )
                {
                    get( i * n * o + j * o + k, unit, value );
                    array[i][j][k] = value;
                }
            }
        }
    }
    else
    {
        size_t length = o * sizeof( T );
        for( int i = 0; i < m; ++i )
        {
            for( int j = 0; j < n; ++j )
            {
                size_t offset = ( i * n + j ) * length;
                memcpy( ( char* )( array[i][j] ), value_.cStr() + offset, length );
            }
        }
    }
    return elements;
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template int BlobValue::copyTo( const Unit& unit, double*** array, int m, int n, int o ) const;
template int BlobValue::copyTo( const Unit& unit, float*** array, int m, int n, int o ) const;
template int BlobValue::copyTo( const Unit& unit, int*** array, int m, int n, int o ) const;
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

/// Get an individual value
bool BlobValue::get( size_t index, const Unit& unit, double& value ) const
{
    if( index >= getElements() )
    {
        return false;
    }
    value = nanf( "" );
    union
    {
        int8_t int8_;
        int16_t int16_;
        int32_t int32_;
        int64_t int64_;
        uint8_t uint8_;
        uint16_t uint16_;
        uint32_t uint32_;
        uint64_t uint64_;
        float float_;
    } u = {};
    switch( blobType_ )
    {
    case  NOT_BLOB:       // Not a blob
        break;
    case BLOB_INT8:      // 8 bit signed integer (char)
        getElement( index, u.int8_, false );
        value = u.int8_;
        break;
    case BLOB_INT16LE:   // 2 byte signed integer, little endian (x86 short int)
        getElement( index, u.int16_, false );
        value = u.int16_;
        break;
    case BLOB_INT16BE:   // 2 byte signed integer, big endian (68k short int)
        getElement( index, u.int16_, true );
        value = u.int16_;
        break;
    case BLOB_INT32LE:   // 4 byte signed integer, little endian (x86 long int)
        getElement( index, u.int16_, false );
        value = u.int32_;
        break;
    case BLOB_INT32BE:   // 4 byte signed integer, big endian (68k long int)
        getElement( index, u.int16_, true );
        value = u.int32_;
        break;
    case BLOB_INT64LE:   // 8 byte signed integer, little endian (x86 long long int)
        getElement( index, u.int64_, false );
        value = u.int64_;
        break;
    case BLOB_INT64BE:   // 8 byte signed integer, big endian (68klong long int)
        getElement( index, u.int64_, true );
        value = u.int64_;
        break;

    case BLOB_UINT8:     // 8 bit unsigned integer (unsigned char)
        getElement( index, u.uint8_, false );
        value = u.uint8_;
        break;
    case BLOB_UINT16LE:  // 2 byte unsigned integer, little endian (x86 unsigned short int)
        getElement( index, u.uint16_, false );
        value = u.uint16_;
        break;
    case BLOB_UINT16BE:  // 2 byte unsigned integer, big endian (68k unsigned short int)
        getElement( index, u.uint16_, true );
        value = u.uint16_;
        break;
    case BLOB_UINT32LE:  // 4 byte unsigned integer, little endian (x86 unsigned long int)
        getElement( index, u.uint16_, false );
        value = u.uint32_;
        break;
    case BLOB_UINT32BE:  // 4 byte unsigned integer, big endian (68k unsigned long int)
        getElement( index, u.uint16_, true );
        value = u.uint32_;
        break;
    case BLOB_UINT64LE:  // 8 byte unsigned integer, little endian (x86 unsigned long long int)
        getElement( index, u.uint64_, false );
        value = u.uint64_;
        break;
    case BLOB_UINT64BE:  // 8 byte unsigned integer, big endian (68k unsigned long long int)
        getElement( index, u.uint64_, true );
        value = u.uint64_;
        break;

    case BLOB_FLOAT32LE: // 4 byte single precision float, little endian (x86 single)
        getElement( index, u.float_, false );
        value = u.float_;
        break;
    case BLOB_FLOAT32BE: // 4 byte single precision float, big endian (68k single)
        getElement( index, u.float_, true );
        value = u.float_;
        break;
    case BLOB_FLOAT64LE: // 8 byte double precision float, little endian (x86 double)
        getElement( index, value, false );
        break;
    case BLOB_FLOAT64BE: // 8 byte double precision float, big endian (68k double)
        getElement( index, value, true );
        break;
    case BLOB_TYPE_COUNT:
        break;
    }

    if( &unit != unit_ )
    {
        if( unit_ != &unit_->getBaseUnit() )
        {
            value = unit_->getSI( value );
        }
        if( unit != unit.getBaseUnit() )
        {
            value = unit.getScaled( value );
        }
    }
    return true;
}

Str BlobValue::toString( const Unit& unit ) const
{
    FlexArray<const Str*> values( true );
    double value( 0 );
    int idx, i, j, k;
    values.push( new Str( "[" ) );
    for( i = 0; i < m_; ++i )
    {
        if( n_ > 0 )
        {
            if( i > 0 )
            {
                values.push( new Str( ",[" ) );
            }
            else
            {
                values.push( new Str( "[" ) );
            }
        }
        else if( i > 0 )
        {
            values.push( new Str( "," ) );
        }
        for( j = 0; j < n_ || j == 0; ++j )
        {
            if( o_ > 0 )
            {
                if( j > 0 )
                {
                    values.push( new Str( ",[" ) );
                }
                else
                {
                    values.push( new Str( "[" ) );
                }
            }
            else if( j > 0 )
            {
                values.push( new Str( "," ) );
            }
            for( k = 0; k < o_ || k == 0; ++k )
            {
                if( k > 0 )
                {
                    values.push( new Str( "," ) );
                }
                idx = i;
                if( n_ > 0 )
                {
                    idx = idx * n_ + j;
                }
                if( o_ > 0 )
                {
                    idx = idx * o_ + k;
                }
                get( idx, unit, value );
                values.push( new Str( value ) );
            }
            if( o_ > 0 )
            {
                values.push( new Str( "]" ) );
            }
        }
        if( n_ > 0 )
        {
            values.push( new Str( "]" ) );
        }
    }
    values.push( new Str( "]" ) );
    return Str( values.getArray(), ( int )values.size() );
}

template<>
bool BlobValue::conversionNeeded<bool>( const Unit& unit ) const
{
    if( sizeof( bool ) == 1 )
    {
        return ( blobType_ != BLOB_UINT8 && blobType_ != BLOB_INT8 ) || &unit != unit_;
    }
    else if( sizeof( bool ) == 2 )
    {
        return ( blobType_ != BLOB_UINT16LE && blobType_ != BLOB_INT16LE ) || &unit != unit_;
    }
    else if( sizeof( bool ) == 4 )
    {
        return ( blobType_ != BLOB_UINT32LE && blobType_ != BLOB_INT32LE ) || &unit != unit_;
    }
    return true;
}

template<>
bool BlobValue::conversionNeeded<unsigned char>( const Unit& unit ) const
{
    return blobType_ != BLOB_UINT8 || &unit != unit_;
}

template<>
bool BlobValue::conversionNeeded<double>( const Unit& unit ) const
{
    return blobType_ != BLOB_FLOAT64LE || &unit != unit_;
}

template<>
bool BlobValue::conversionNeeded<float>( const Unit& unit ) const
{
    return blobType_ != BLOB_FLOAT32LE || &unit != unit_;
}

template<>
bool BlobValue::conversionNeeded<int>( const Unit& unit ) const
{
    return ( blobType_ != BLOB_INT32LE && sizeof( int ) == 4 )
           || ( blobType_ != BLOB_INT64LE && sizeof( int ) == 8 ) || &unit != unit_;
}

/// Set an individual value, without bounds checking or scaling
template<typename T>
void BlobValue::setElement( size_t index, T value, bool flip )
{
    if( flip )
    {
        value = AuvMath::FlipEndian( value );
    }
    memcpy( ( char* )value_.cStr() + index * sizeof( T ), &value, sizeof( T ) );
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template void BlobValue::setElement( size_t index, int8_t value, bool flip );
template void BlobValue::setElement( size_t index, int16_t value, bool flip );
template void BlobValue::setElement( size_t index, int32_t value, bool flip );
template void BlobValue::setElement( size_t index, int64_t value, bool flip );
template void BlobValue::setElement( size_t index, uint8_t value, bool flip );
template void BlobValue::setElement( size_t index, uint16_t value, bool flip );
template void BlobValue::setElement( size_t index, uint32_t value, bool flip );
template void BlobValue::setElement( size_t index, uint64_t value, bool flip );
template void BlobValue::setElement( size_t index, float value, bool flip );
template void BlobValue::setElement( size_t index, double value, bool flip );
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

/// Get an individual value, without bounds checking or scaling
template<typename T>
void BlobValue::getElement( size_t index, T& value, bool flip ) const
{
    memcpy( &value, ( char* )value_.cStr() + index * sizeof( T ), sizeof( T ) );
    if( flip )
    {
        value = AuvMath::FlipEndian( value );
    }
}
/// \cond DOXYGEN_CANT_HANDLE_THIS
template void BlobValue::getElement( size_t index, int8_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, int16_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, int32_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, int64_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, uint8_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, uint16_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, uint32_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, uint64_t& value, bool flip ) const;
template void BlobValue::getElement( size_t index, float& value, bool flip ) const;
template void BlobValue::getElement( size_t index, double& value, bool flip ) const;
/// \endcond // DOXYGEN_CANT_HANDLE_THIS

/// Dimensions are not currenntly serialized, so assume 1D.
void BlobValue::fromBinary( const void *buffer, unsigned short typeSize )
{
    StrValue::fromBinary( buffer, typeSize );
    m_ = value_.length() / ElementSize( blobType_ );
    n_ = o_ = 0;
}
