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

#include "Int.h"
#include "io/OutStream.h"
#include "utils/RingBuffer.h"

RingBufferVoid* Int::MallocRing_( NULL );

Int::StaticDestructor Int::StaticDestructor_;

Mutex Int::Mutex_;

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

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

/// Constructor from binary
Int::Int( const Unit& unit, const void *binary )
    : DataValue( unit, sizeof( int ), INT4 ),
      value_( 0 )
{
    fromBinary( binary );
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// set from unsigned char value
bool Int::setFrom( const Unit& unit, unsigned char value )
{
    if( ! equals( getBaseUnit(), value ) )
    {
        value_ = unit.getSI( ( int )value );
        return true;
    }
    return false;
};

/// set from double value
bool Int::setFrom( const Unit& unit, double value )
{
    if( ! equals( unit, value ) )
    {
        if( value != value )
        {
            value_ = 1 << ( typeSize_ * 8 );
        }
        else
        {
            value_ = unit.getSI( ( int )value );
        }
        return true;
    }
    return false;
};

/// set from float value
bool Int::setFrom( const Unit& unit, float value )
{
    if( ! equals( unit, value ) )
    {
        value_ = unit.getSI( ( int )value );
        return true;
    }
    return false;
};

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

/// set DataValue value
bool Int::setFrom( const DataValue& value )
{
    if( ! value.equals( getBaseUnit(), value_ ) )
    {
        value.copyTo( getBaseUnit(), value_ );
        return true;
    }
    return false;
};

// as a Str
Str Int::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 Int::toStream( OutStream& outStream ) const
{
    return outStream.write( ( const char* )&value_, typeSize_ ).bytesWritten();
}

void Int::fromBinary( const void *buffer )
{
    memcpy( ( void * ) & value_, ( const void * ) buffer, typeSize_ );
    if( typeSize_ == 2 )
    {
        value_ = ( short )value_;
    }
}

/// Protected Empty constructor for use bya DataEntry class
Int::Int()
    : DataValue( sizeof( int ), INT4 ),
      value_( 0 )
{}


