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

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

RingBufferVoid* Double::MallocRing_( NULL );

Double::StaticDestructor Double::StaticDestructor_;

Mutex Double::Mutex_;

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

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

/// Constructor from binary
Double::Double( const Unit& unit, const void *binary )
    : DataValue( unit, sizeof( double ), DOUBLE8 ),
      value_( 0 )
{
    fromBinary( binary );
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// Equality test with another DataValue
bool Double::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 Double::equalsForStorage( const double& compareTo ) const
{
    return value_ == compareTo || ( isnan( value_ ) && isnan( compareTo ) );
}

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

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

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

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

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

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

// as a Str
Str Double::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 Double::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 Double::ToBinary( const double& value, unsigned char *buffer, unsigned int maxlen )
{
    if( sizeof( double ) < maxlen )
    {
        memcpy( ( void * ) buffer, ( const void * ) & value, sizeof( double ) );
        return sizeof( double );
    }
    return 0;
}

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

/// Protected Empty constructor for use bya DataEntry class
Double::Double()
    : DataValue( sizeof( double ), DOUBLE8 ),
      value_( NAN )
{}


