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

#include "Float.h"
#include "io/OutStream.h"
#include "utils/AuvMath.h"
#include "utils/RingBuffer.h"

RingBufferVoid* Float::MallocRing_( NULL );

Float::StaticDestructor Float::StaticDestructor_;

Mutex Float::Mutex_;

Float::StaticDestructor::~StaticDestructor()
{
    delete MallocRing_;
    MallocRing_ = NULL;
}

/// Constructor for non-array storage
Float::Float( const Unit& unit, float value, bool scaleValue )
    : DataValue( unit, sizeof( float ), FLOAT4 ),
      value_( scaleValue ? unit.getSI( value ) : value )
{}

/// Constructor from binary
Float::Float( const Unit& unit, const void *binary )
    : DataValue( unit, sizeof( float ), FLOAT4 ),
      value_( 0 )
{
    fromBinary( binary );
}

/// Override new
void* Float::operator new( size_t size )
{
    MutexLocker lock( Mutex_ );
    void* newMem = NULL;
    if( size != sizeof( Float ) )
    {
        printf( "Aborting: Requested %zu bytes for a %zu byte Float DataValue\n", size, sizeof( Float ) );
        exit( 1 );
    }
    else
    {
        if( NULL == MallocRing_ )
        {
            MallocRing_ = new RingBufferVoid( true );
        }
        newMem = MallocRing_->pop();
    }
    if( NULL == newMem )
    {
        newMem = malloc( size );
    }
    return newMem;
}

/// Override delete
void Float::operator delete( void* p )
{
    MutexLocker lock( Mutex_ );
    if( NULL != MallocRing_ )
    {
        MallocRing_->push( p );
    }
    else
    {
        free( p );
    }
}

/// Return a pointer to a new copy
Float* Float::copy() const
{
    return new Float( *unit_, value_, false );
}

/// Subtraction of another DataValue to yield another DataValue
void Float::minus( const DataValue* rhs, DataValue* assignTo ) const
{
    float rhsValue;
    if( rhs->copyTo( getBaseUnit(), rhsValue ) )
    {
        assignTo->setFrom( getBaseUnit(), value_ - rhsValue );
    }
}

/// Multiplication by a double to yield another DataValue
void Float::times( const double rhs, DataValue* assignTo ) const
{
    assignTo->setFrom( getBaseUnit(), value_ * rhs );
}

/// Return the absolute value of this difference from another DataValue
double Float::absDiff( const DataValue* rhs ) const
{
    float rhsValue;
    if( rhs->copyTo( getBaseUnit(), rhsValue ) )
    {
        return fabs( value_ - rhsValue );
    }
    return __builtin_nan( "" );
}

/// Copy value to unsigned char
bool Float::copyTo( const Unit& unit, unsigned char& assignTo ) const
{
    assignTo = ( unsigned char ) unit.getScaled( value_ );
    return true;
}

/// Copy value to double
bool Float::copyTo( const Unit& unit, double& assignTo ) const
{
    assignTo = ( double ) unit.getScaled( value_ );
    return true;
}

/// Copy value to float
bool Float::copyTo( const Unit& unit, float& assignTo ) const
{
    assignTo = ( float ) unit.getScaled( value_ );
    return true;
}

/// Copy value to unsigned int
bool Float::copyTo( const Unit& unit, int& assignTo ) const
{
    assignTo = ( int ) unit.getScaled( value_ );
    return true;
}

/// Comparison with unsigned char
int Float::compare( const Unit& unit, const unsigned char& compareTo ) const
{
    return ( int ) AuvMath::Sign( value_ - ( float ) unit.getSI( compareTo ) );
}

/// Comparison with float
int Float::compare( const Unit& unit, const float& compareTo ) const
{
    return ( int ) AuvMath::Sign( value_ - ( float ) unit.getSI( compareTo ) );
}

/// Comparison with double
int Float::compare( const Unit& unit, const double& compareTo ) const
{
    return ( int ) AuvMath::Sign( value_ - ( float ) unit.getSI( compareTo ) );
}

/// Comparison with int
int Float::compare( const Unit& unit, const int& compareTo ) const
{
    return ( int ) AuvMath::Sign( value_ - ( float ) unit.getSI( compareTo ) );
}

/// Comparison with another DataValue
int Float::compare( const DataValue& compareTo ) const
{
    return - compareTo.compare( getBaseUnit(), value_ );
}

/// Equality test with with unsigned char
bool Float::equals( const Unit& unit, const unsigned char& compareTo ) const
{
    return value_ == ( float ) unit.getSI( compareTo );
}

/// Equality test with double
bool Float::equals( const Unit& unit, const double& compareTo ) const
{
    float scaled = ( float ) unit.getSI( compareTo );
    return value_ == scaled || ( isnan( value_ ) && isnan( scaled ) );
}

/// Equality test with float
bool Float::equals( const Unit& unit, const float& compareTo ) const
{
    float scaled = ( float ) unit.getSI( compareTo );
    return value_ == scaled || ( isnan( value_ ) && isnan( scaled ) );
}

/// Equality test with int
bool Float::equals( const Unit& unit, const int& compareTo ) const
{
    return value_ == ( float ) unit.getSI( compareTo );
}

/// Equality test with another DataValue
bool Float::equals( const DataValue& compareTo ) const
{
    return compareTo.equals( getBaseUnit(), value_ );
}

/// A quick equality check used to see if value has changed enough
/// to matter in logging (useful for reduced resolution data storage
bool Float::equalsForStorage( const float& compareTo ) const
{
    return value_ == compareTo || ( isnan( value_ ) && isnan( compareTo ) );
}

/// Returns true if the number is not a number
bool Float::isNaN()
{
    return isnan( value_ );
}

/// set from unsigned char value
bool Float::setFrom( const Unit& unit, unsigned char value )
{
    if( ! equals( unit, value ) )
    {
        float newValue = ( float ) unit.getSI( value );
        bool changedForStorage = !equalsForStorage( newValue );
        value_ = newValue;
        return changedForStorage;
    }
    return false;
};

/// set from double value
bool Float::setFrom( const Unit& unit, double value )
{
    if( ! equals( unit, value ) )
    {
        float newValue = ( float ) unit.getSI( value );
        bool changedForStorage = !equalsForStorage( newValue );
        value_ = newValue;
        return changedForStorage;
    }
    return false;
};

/// set from float value
bool Float::setFrom( const Unit& unit, float value )
{
    if( ! equals( unit, value ) )
    {
        float newValue = ( float ) unit.getSI( value );
        bool changedForStorage = !equalsForStorage( newValue );
        value_ = newValue;
        return changedForStorage;
    }
    return false;
};

/// set int value
bool Float::setFrom( const Unit& unit, int value )
{
    if( ! equals( unit, value ) )
    {
        float newValue = ( float ) unit.getSI( value );
        bool changedForStorage = !equalsForStorage( newValue );
        value_ = newValue;
        return changedForStorage;
    }
    return false;
};

/// set DataValue value
bool Float::setFrom( const DataValue& value )
{
    if( ! value.equals( getBaseUnit(), value_ ) )
    {
        float newValue;
        value.copyTo( getBaseUnit(), newValue );
        bool changedForStorage = !equalsForStorage( newValue );
        value_ = newValue;
        return changedForStorage;
    }
    return false;
};

// as a Str
Str Float::toString( const Unit& unit ) const
{
    return Str( unit.getScaled( value_ ) );
}

/// Send the data value to the indicated stream.
/// Return the number of bytes written.
/// For now, assume little-endian.
unsigned int Float::toStream( OutStream& outStream ) const
{
    return outStream.write( ( const char* )&value_, typeSize_ ).bytesWritten();
}

/// Convert the specified data value to a binary representation in the buffer
/// Typically just a memcopy of the variable, though endianness
/// may come into it.
unsigned int Float::ToBinary( const float& value, unsigned char *buffer, unsigned int maxlen )
{
    if( sizeof( float ) < maxlen )
    {
        memcpy( ( void * ) buffer, ( const void * ) & value, sizeof( float ) );
        return sizeof( float );
    }
    return 0;
}

void Float::fromBinary( const void *buffer )
{
    memcpy( ( void * ) & value_, ( const void * ) buffer, sizeof( float ) );
}

/// Protected Empty constructor for use bya DataEntry class
Float::Float()
    : DataValue( sizeof( float ), FLOAT4 ),
      value_( NANF )
{}


