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

#include "ValueClause.h"

#include "data/BlobValue.h"
#include "data/Slate.h"
#include "data/StrValue.h"
#include "missionScript/MissionItem.h"
#include "missionScript/MissionNode.h"
#include "units/UnitRegistry.h"
#include "units/Units.h"

#include <cstdlib>

const Str ValueClause::OP_NAMES[ OPCOUNT ] =
{
    "--INVALID--",    // corresponds to OP_NONE,
    "Add", // corresponds to OP_ADD,
    "And", // corresponds to OP_AND,
    "Div", // corresponds to OP_DIV,
    "Eq",  // corresponds to OP_EQ,
    "Ge",  // corresponds to OP_GE,
    "Gt",  // corresponds to OP_GT,
    "Le",  // corresponds to OP_LE,
    "Lt",  // corresponds to OP_LT,
    "Max", // corresponds to OP_MAX,
    "Min", // corresponds to OP_MIN,
    "Mod", // corresponds to OP_MOD,
    "Mult", // corresponds to OP_MULT,
    "Ne",  // corresponds to OP_NE,
    "Or",  // corresponds to OP_OR,
    "Pow", // corresponds to OP_POW,
    "Sub", // corresponds to OP_SUB,
};

// Destructor
ValueClause::~ValueClause()
{
    if( NULL != dataValue_ )
    {
        delete dataValue_;
    }
}

// Simple public Constructor -- normally Static Instance Constructor is used
ValueClause::ValueClause( DataValue* dataValue, Logger& logger )
    : logger_( logger ),
      abs_( false ),
      isNaN_( false ),
      negate_( false ),
      dataValue_( NULL == dataValue ? NULL : dataValue->copy() ),
      lhs_(),
      operations_( true )
{
    lhs_.dataValue_ = dataValue_;
    lhs_.deleteDataValue_ = false;
}

// Static Instance Constructor - returns null if invalid
ValueClause* ValueClause::Instance( MissionNode* node, bool nodeIsParent, MissionItem* owner, Logger& logger, bool isStrValue /* = false */ )
{
    ValueClause* theClause = NULL;
    MissionNode* childNode = nodeIsParent ? node->getFirstChild() : node;
    if( NULL != childNode && 0 == strncmp( childNode->getName(), "Description", 12 ) )
    {
        childNode = childNode->getNextSibling( true );
    }
    if( NULL != childNode )
    {
        if( NULL == owner )
        {
            theClause = new ValueClause( logger );
        }
        else
        {
            theClause = new ValueClause( owner->getComponent()->getLogger() );
        }
    }

    // Order of nodes is LHS [operator RHS0 [operator RHS1 [operator RHS2 ...
    // Deal with this as (((LHS operator RHS0) operator RHS1 ) operator RHS2 )
    if( NULL != theClause && ReadSide( childNode, theClause->lhs_, owner, logger, isStrValue ) )
    {
        theClause->dataValue_ = theClause->lhs_.dataValue_->copy();
        childNode = childNode->getNextSibling();
        if( NULL != childNode
                && strncmp( childNode->getName(), "Value", 5 ) == 0 )
        {
            childNode = childNode->getNextSibling();
        }
        while( NULL != childNode )
        {
            ValueClauseOperation* operation = ReadOperation( childNode, owner, logger );
            if( NULL != operation )
            {
                theClause->operations_.push( operation );
                // Unless AND or OR, Need to advance twice, since
                // ReadOperation does not advance
                // our pointer to childNode.
                childNode = childNode->getNextSibling();
                if( NULL != childNode
                        && strncmp( childNode->getName(), "Value", 5 ) == 0 )
                {
                    childNode = childNode->getNextSibling();
                }
            }
            else
            {
                childNode = NULL;
            }
        }
    }
    //logger.syslog( theClause->toString(), Syslog::INFO );
    return theClause;
}

// Used to read a DataReader
DataReader* ValueClause::ReadReader( MissionNode* node, MissionItem* owner,
                                     DataValue* defaultValue, Logger& logger, bool deleteDataValue /* = true */ )
{
    Str uriStr;
    bool isCustomUri = false;
    if( strncmp( node->getName(), "CustomUri", 9 ) == 0 )
    {
        uriStr = node->getAttribute( "Uri" );
        isCustomUri = true;
    }
    else
    {
        uriStr = StripPastColon( node->getName() );
    }
    unsigned short code = Slate::GetElementURICode( uriStr.cStr() );
    if( isCustomUri && code == ElementURI::NO_CODE && uriStr.length() > 1
            && ( uriStr.cStr()[ 0 ] == '_' ||  uriStr.cStr()[ 0 ] == '#' )
            && uriStr.cStr()[ 1 ] == '.' )
    {
        // This is a new scratchpad variable
        BinaryDataType binaryType = uriStr.cStr()[ 0 ] == '_' ? NO_TYPE : MULTIVALUE;
        BlobType blobType = uriStr.cStr()[ 0 ] == '_' ? NOT_BLOB : BLOB_FLOAT64LE;
        ElementURI uri( "", uriStr, ElementURI::BASE_ELEMENT, ElementURI::UNSPECIFIED_UNIT, binaryType, blobType );
        DataValue* dataValue = uri.getUnit().dataValue( uri.getBinaryType(), uri.getBlobType() );
        DataElement* dataElement = new SimpleDataElement( uri, dataValue, 0 );
        Slate::MapDataElement( &uri, dataElement );
    }

    ElementURI* canonicalURI = canonicalURI = Slate::FindElementURI( uriStr );
    bool deleteCanonicalURI = false;
    if( NULL == canonicalURI )
    {
        canonicalURI = new ElementURI( "", uriStr );
        deleteCanonicalURI = true;
    }
    // Assign the correct units to the dataValue, unless already set.
    if( defaultValue == NULL || defaultValue->getUnit() == Units::NONE )
    {
        //printf( "\n** For uriStr=%s, canonicalURI is%s **\n", uriStr.cStr(), canonicalURI == NULL ? "NULL" : "Not null" );
        if( NULL == defaultValue )
        {
            defaultValue = canonicalURI->getUnit().dataValue( canonicalURI->getBinaryType(), canonicalURI->getBlobType() );
        }
        else
        {
            defaultValue->setUnit( canonicalURI->getUnit() );
        }
    }
    if( NULL == defaultValue )
    {
        defaultValue = Units::NONE( nanf( "" ) );
    }
    DataReader* reader = Slate::NewReader( *canonicalURI, owner->getComponent(), defaultValue, deleteDataValue );
    if( deleteCanonicalURI )
    {
        delete canonicalURI;
    }
    return reader;
}

// Used to read a static DataValue
DataValue* ValueClause::ReadDataValue( MissionNode* unitNode, Logger& logger )
{
    const Unit* unit = ReadUnit( unitNode, logger );
    if( NULL != unit )
    {
        MissionNode* valueNode = unitNode->getNextSibling();
        const char* value = NULL == valueNode ? NULL : valueNode->getChildTextValue();
        if( NULL != value )
        {
            const unsigned int len = strlen( value );
            int rank, m, n, o;
            DataValue* dataValue;
            double* values = Str::ToDoubleArray( value, len, rank, m, n, o );
            if( rank == 0 )
            {
                dataValue = ( *unit )( values[0], DOUBLE8 );
            }
            else
            {
                BlobValue* blobValue = new BlobValue( *unit, BLOB_FLOAT64LE );
                blobValue->setFrom( *unit, values, m, n, o );
                dataValue = blobValue;
            }
            delete[] values;
            return dataValue;
        }
        else
        {
            logger.syslog( "No Value specified for unit ", unit->getName(), Syslog::INFO );
            return ( *unit )( nan( "" ), DOUBLE8 );
        }
    }
    return NULL;
}

const Unit* ValueClause::ReadUnit( MissionNode* unitNode, Logger& logger, bool reportErrors )
{
    Str unitName( StripPastColon( unitNode->getName() ) );
    const Unit* unit = UnitRegistry::FindUnit( unitName );
    if( NULL == unit && reportErrors )
    {
        logger.syslog( "Could not find unit named: " + unitName, Syslog::CRITICAL );
    }
    return unit;
}

// Used to read a string DataValue
DataValue* ValueClause::ReadStrValue( MissionNode* node, Logger& logger )
{
    DataValue* value( NULL );
    if( node->isElement() )
    {
        value = ReadStrValueUri( node, logger );
        if( NULL == value )
        {
            if( node->hasChildNodes() )
            {
                value = new StrValue( node->getChildTextValue() );
            }
            else
            {
                value = new StrValue( node->getName() );
            }
        }
    }
    else if( node->isText() )
    {
        value = new StrValue( node->getTextValue() );
    }
    return value;
}

// Used to read a string DataValue
DataValue* ValueClause::ReadStrValueUri( MissionNode* node, Logger& logger )
{
    Str name( StripPastColon( node->getName() ) );
    unsigned short code = Slate::GetElementURICode( name.cStr() );
    if( code == ElementURI::NO_CODE )
    {
        return NULL;
    }
    StrValue* value = new StrValue( name );
    return value;
}

Str ValueClause::StripPastColon( Str name )
{
    unsigned int findAt = name.find( ':' );
    // delete past first ':'
    if( findAt != Str::NO_POS )
    {
        name = name.substr( findAt + 1 );
    }
    return name;
}

Str ValueClause::StripPastLastDot( Str name )
{
    unsigned int findAt = name.findLastOf( '.' );
    // delete past last '.'
    if( findAt != Str::NO_POS )
    {
        name = name.substr( findAt + 1 );
    }
    return name;
}

Str ValueClause::toString()
{
    Str theStr( Str::EMPTY_STR );
    if( abs_ )
    {
        theStr += "Abs( ";
    }
    if( isNaN_ )
    {
        theStr += "IsNaN( ";
    }
    if( negate_ )
    {
        theStr += "Not( ";
    }
    theStr += sideToString( lhs_ );
    for( unsigned int i = 0; i < operations_.size(); ++i )
    {
        ValueClauseOperation* operation = operations_[i];
        theStr += " " + OP_NAMES[ operation->comparison_ ] + sideToString( operation->rhs_ );
    }
    if( abs_ )
    {
        theStr += " )";
    }
    if( isNaN_ )
    {
        theStr += " )";
    }
    if( negate_ )
    {
        theStr += " )";
    }
    return theStr;
}

Str ValueClause::sideToString( ValueClauseSide& side )
{
    if( NULL != side.dataReader_ )
    {
        switch( side.operationReader_ )
        {
        case READER_NONE:
            return "reader:" + side.dataReader_->getUri();
        case READER_ELAPSED:
            return "Elapsed( " + side.dataReader_->getUri() + " )";
        }
    }
    if( NULL != side.subClause_ )
    {
        return "( " + side.subClause_->toString() + " )";
    }
    if( NULL != side.dataValue_ )
    {
        return "value:" + side.dataValue_->toString();
    }
    return "?";
}

ValueClause::Operation ValueClause::ReadComparison( MissionNode* node, Logger& logger )
{
    const char* comparisonName( node->getName() );
    if( NULL != comparisonName )
    {
        for( int i = 1; i < OPCOUNT; ++i )
        {
            if( OP_NAMES[i] == comparisonName )
            {
                //printf("ReadComparison returning %s\n", OP_NAMES[i].cStr());
                return ( Operation )i;
            }
        }
        logger.syslog( "Invalid comparison operator: ", comparisonName, Syslog::ERROR );
    }
    else
    {
        logger.syslog( "Error reading comparison name.", Syslog::ERROR );
    }
    return OP_NONE;
}

bool ValueClause::ReadSide( MissionNode* node, ValueClauseSide& side, MissionItem* owner, Logger& logger, bool isStrValue /* = false */ )
{
    bool setSomething = false;
    Str name( node->getName() );

    if( isStrValue )
    {
        side.dataValue_ = ReadStrValue( node, logger );
        setSomething = true;
    }
    else if( name == "True" )
    {
        side.dataValue_ = Units::BOOL( true );
        setSomething = true;
    }
    else if( name == "False" )
    {
        side.dataValue_ = Units::BOOL( false );
        setSomething = true;
    }
    else if( name == "Called" )
    {
        side.dataValue_ = Units::BOOL( false );
        setSomething = true;
    }
    else if( name == "Abs" )
    {
        side.subClause_ = ValueClause::Instance( node, true, owner, logger );
        if( NULL != side.subClause_ )
        {
            side.dataValue_ = side.subClause_->dataValue_->copy();
            side.subClause_->abs_ = true;
            setSomething = true;
        }
        else
        {
            side.dataValue_ = Units::NONE( nanf( "" ) );
        }
    }
    else if( name == "IsNaN" )
    {
        side.subClause_ = ValueClause::Instance( node, true, owner, logger );
        if( NULL != side.subClause_ )
        {
            side.dataValue_ = side.subClause_->dataValue_->copy();
            side.subClause_->isNaN_ = true;
            setSomething = true;
        }
        else
        {
            side.dataValue_ = Units::NONE( nanf( "" ) );
        }
    }
    else if( name == "Not" )
    {
        side.subClause_ = ValueClause::Instance( node, true, owner, logger );
        side.dataValue_ = Units::BOOL( false );
        if( NULL != side.subClause_ )
        {
            side.subClause_->negate_ = true;
            setSomething = true;
        }
    }
    else if( name == "Elapsed" )
    {
        // We expect to find a URI contained here
        MissionNode* childNode = node->getFirstChild();
        if( NULL != childNode )
        {
            side.dataValue_ = Units::SECOND();
            if( strncmp( childNode->getName(), "Arg", 4 ) == 0 )
            {
                side.dataReader_ = owner->findArgReader( childNode->getAttribute( "Name" ) );
            }
            else if( NULL != owner )
            {
                side.dataReader_ = ReadReader( childNode, owner, side.dataValue_, logger );
                side.deleteDataValue_ = false;
            }
            side.operationReader_ = READER_ELAPSED;
            setSomething = true;
        }
        else
        {
            side.dataValue_ = Units::SECOND();
            side.owner_ = owner;
            side.operationReader_ = READER_ELAPSED;
            setSomething = true;
        }
    }
    else if( name == "Arg" )
    {
        DataReader* argReader( NULL );
        Str argName( node->getAttribute( "Name" ) );
        if( Str::EMPTY_STR != argName )
        {
            argReader = owner->findArgReader( argName );
        }
        if( NULL != argReader )
        {
            side.dataReader_ = argReader;
            side.dataValue_ = argReader->getDefaultValueCopy();
            side.operationReader_ = READER_NONE;
            setSomething = true;
        }
        else
        {
            logger.syslog( "Could not find Arg named \"" + argName + "\" in value clause.", Syslog::ERROR );
        }
    }
    else if( name.find( "Units" ) == 0 )
    {
        side.dataValue_ = ReadDataValue( node, logger );
        setSomething = NULL != side.dataValue_;
    }
    else if( name.find( "Sensor" ) == 0 )
    {
        side.dataValue_ = new StrValue( StripPastColon( name ) );
        setSomething = true;
    }
    else if( name.find( "String" ) == 0 )
    {
        side.dataValue_ = new StrValue( node->getChildTextValue() );
        setSomething = true;
    }
    else if( NULL != owner )
    {
        side.dataReader_ = ReadReader( node, owner, NULL, logger );
        side.dataValue_ = side.dataReader_->getDefaultValueCopy();
        side.deleteDataValue_ = true;
        setSomething = true;
    }

    /// Return true if we set something.
    return setSomething;
}

ValueClause::ValueClauseOperation* ValueClause::ReadOperation( MissionNode* node, MissionItem* owner, Logger& logger )
{
    ValueClauseOperation* operation = new ValueClauseOperation();
    operation->comparison_ = ReadComparison( node, logger );
    if( OP_NONE == operation->comparison_ )
    {
        logger.syslog( "Unable to read Comparison of ", node->getName(), Syslog::CRITICAL );
        delete operation;
        return NULL;
    }
    else
    {
        operation->rhs_.subClause_ = Instance( node, true, owner, logger );
        if( NULL == operation->rhs_.subClause_ )
        {
            logger.syslog( "Unable to read RHS of " + OP_NAMES[operation->comparison_], Syslog::CRITICAL );
            delete operation;
            return NULL;
        }
        operation->rhs_.dataValue_ = operation->rhs_.subClause_->dataValue_;
        operation->rhs_.deleteDataValue_ = false;
    }
    return operation;
}

bool ValueClause::evalSide( const ValueClauseSide& side )
{
    if( NULL != side.dataReader_ || NULL != side.owner_ )
    {
        switch( side.operationReader_ )
        {
        case READER_NONE:
            if( NULL != side.dataReader_ )
            {
                side.dataReader_->read( *side.dataValue_ );
            }
            break;
        case READER_ELAPSED:
            if( NULL != side.dataReader_ )
            {
                side.dataValue_->setFrom( side.dataValue_->getUnit(), side.dataReader_->getTimestamp().elapsed().asDouble() );
                //printf("Elapsed dataValue set to %s\n", side.dataValue_->toString().cStr());
            }
            else if( NULL != side.owner_ )
            {
                side.dataValue_->setFrom( side.dataValue_->getUnit(), side.owner_->getStartTime().elapsed().asDouble() );
            }
            break;
        }
    }
    else if( NULL != side.subClause_ )
    {
        if( side.subClause_->eval() )
        {
            if( NULL != side.subClause_->dataValue_ )
            {
                side.dataValue_->setFrom( *( side.subClause_->dataValue_ ) );
            }
        }
    }
    else if( NULL == side.dataValue_ )
    {
        return false;
    }
    return true;
}

// tests the boolclause for truth
bool ValueClause::test()
{
    bool result( false );
    if( eval() && dataValue_ != NULL )
    {
        dataValue_->copyTo( dataValue_->getUnit(), result );
        //printf("test() %s, copied %s to %d\n", toString().cStr(),dataValue_->toString().cStr(),result);
    }
    return result;
}

// evaluates the clause as a simple string
bool ValueClause::evalStr()
{
    bool retVal( false );
    if( NULL != dataValue_ && dataValue_->getBinaryType() == MULTIVALUE )
    {
        retVal = true;
    }
    else if( NULL != lhs_.dataReader_ && NULL != dataValue_ && dataValue_->getUnit() == Units::NONE )
    {
        delete dataValue_;
        dataValue_ = new StrValue( lhs_.dataReader_->getUri() );
        retVal = evalStr();
    }
    return retVal;
}

// evaluates the clause
bool ValueClause::eval()
{
    if( !evalSide( lhs_ ) )
    {
        logger_.syslog( "Error evaluating LHS of expression: " + this->toString(), Syslog::CRITICAL );
        return false;
    }

    if( NULL == dataValue_ )
    {
        logger_.syslog( "Error evaluating expression, no datavalue!", Syslog::CRITICAL );
        return false;
    }

    lhs_.dataValue_->copyTo( *dataValue_ );

    for( unsigned int i = 0; i < operations_.size(); ++i )
    {
        ValueClauseOperation* operation = operations_[i];

        // Shorthand evaluation of and/or
        if( operation->comparison_ == OP_AND || operation->comparison_ == OP_OR )
        {
            bool boolValue( false );
            if( dataValue_->copyTo( dataValue_->getUnit(), boolValue ) )
            {
                if( operation->comparison_ == OP_AND && boolValue == false )
                {
                    continue;
                }
                if( operation->comparison_ == OP_OR && boolValue == true )
                {
                    continue;
                }
            }
        }

        if( !evalSide( operation->rhs_ ) )
        {
            logger_.syslog( "Error evaluating expression RHS#", ( int )i, Syslog::CRITICAL );
            return false;
        }
        double lhsValue( nan( "" ) );
        double rhsValue( nan( "" ) );
        double doubleValue( nan( "" ) );
        bool boolValue( false );
        dataValue_->copyTo( dataValue_->getBaseUnit(), lhsValue );
        //printf( "operation->rhs_.dataValue_->getUnit()=%s\n", operation->rhs_.dataValue_->getUnit().getName() );
        operation->rhs_.dataValue_->copyTo( operation->rhs_.dataValue_->getBaseUnit(), rhsValue );
        switch( operation->comparison_ )
        {
        case OP_NONE:
        case OPCOUNT:
            logger_.syslog( "Invalid comparison", Syslog::CRITICAL );
            return false;
        case OP_ADD:
            doubleValue = lhsValue + rhsValue;
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, ADD doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        case OP_AND:
            boolValue = lhsValue != 0.0 && rhsValue != 0.0;
            break;
        case OP_DIV:
            doubleValue = rhsValue == 0.0 ? nan( "" ) : lhsValue / rhsValue;
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, DIV doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        case OP_EQ:
            boolValue = dataValue_->equals( *operation->rhs_.dataValue_ );
            //printf( "\nWith dataValue_=%s, operation->rhs_.dataValue_=%s, EQ boolValue=%d\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), boolValue );
            break;
        case OP_GE:
            if( !dataValue_->isNaN()
                    && !operation->rhs_.dataValue_->isNaN() )
            {
                boolValue = dataValue_->compare( *operation->rhs_.dataValue_ ) >= 0;
            }
            else
            {
                boolValue = false;
            }
            break;
        case OP_GT:
            if( !dataValue_->isNaN()
                    && !operation->rhs_.dataValue_->isNaN() )
            {
                boolValue = dataValue_->compare( *operation->rhs_.dataValue_ ) > 0;
            }
            else
            {
                boolValue = false;
            }
            break;
        case OP_LE:
            if( !dataValue_->isNaN()
                    && !operation->rhs_.dataValue_->isNaN() )
            {
                boolValue = dataValue_->compare( *operation->rhs_.dataValue_ ) <= 0;
            }
            else
            {
                boolValue = false;
            }
            break;
        case OP_LT:
            if( !dataValue_->isNaN()
                    && !operation->rhs_.dataValue_->isNaN() )
            {
                boolValue = dataValue_->compare( *operation->rhs_.dataValue_ ) < 0;
            }
            else
            {
                boolValue = false;
            }
            break;
        case OP_MAX:
            doubleValue = AuvMath::Max( lhsValue, rhsValue );
            break;
        case OP_MIN:
            doubleValue = AuvMath::Min( lhsValue, rhsValue );
            break;
        case OP_MOD:
            doubleValue = rhsValue == 0.0 ? nan( "" ) : fmod( lhsValue, rhsValue );
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, DIV doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        case OP_MULT:
            doubleValue = lhsValue * rhsValue;
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, MULT doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        case OP_NE:
            boolValue = !dataValue_->equals( *operation->rhs_.dataValue_ );
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, NE boolValue=%d\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), boolValue );
            break;
        case OP_OR:
            boolValue = lhsValue != 0.0 || rhsValue != 0.0;
            break;
        case OP_POW:
            doubleValue = pow( lhsValue, rhsValue );
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, MULT doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        case OP_SUB:
            doubleValue = lhsValue - rhsValue;
            //printf( "With dataValue_=%s, operation->rhs_.dataValue_=%s, SUB doubleValue=%g\n", dataValue_->toString().cStr(), operation->rhs_.dataValue_->toString().cStr(), doubleValue );
            break;
        }

        switch( operation->comparison_ )
        {
        case OP_AND:
        case OP_EQ:
        case OP_GE:
        case OP_GT:
        case OP_LE:
        case OP_LT:
        case OP_NE:
        case OP_OR:
            dataValue_->setFrom( dataValue_->getBaseUnit(), boolValue );
            //printf("\nIn eval result =%d, set dataValue to %s\n", boolValue, dataValue_->toString().cStr());
            break;
        case OP_ADD:
        case OP_DIV:
        case OP_MAX:
        case OP_MIN:
        case OP_MOD:
        case OP_MULT:
        case OP_POW:
        case OP_SUB:
            dataValue_->setFrom( dataValue_->getBaseUnit(), doubleValue );
            break;
        case OP_NONE:
        case OPCOUNT:
            break;
        }
        //printf("post-op dataValue set to %s\n", dataValue_->toString().cStr());
    }
    if( abs_ )
    {
        double doubleValue( nan( "" ) );
        if( dataValue_->copyTo( dataValue_->getUnit(), doubleValue ) )
        {
            dataValue_->setFrom( dataValue_->getUnit(), fabs( doubleValue ) );
        }
    }
    if( isNaN_ )
    {
        double doubleValue( nan( "" ) );
        if( dataValue_->copyTo( dataValue_->getUnit(), doubleValue ) )
        {
            dataValue_->setFrom( dataValue_->getBaseUnit(), isnan( doubleValue ) );
        }
    }
    if( negate_ )
    {
        bool boolValue( false );
        if( dataValue_->copyTo( dataValue_->getUnit(), boolValue ) )
        {
            dataValue_->setFrom( dataValue_->getBaseUnit(), !boolValue );
        }
    }
    //printf("eval() %s, return dataValue is %s\n", toString().cStr(), dataValue_->toString().cStr());
    return true;
}

// Private Constructor
ValueClause::ValueClause( Logger& logger )
    : logger_( logger ),
      abs_( false ),
      isNaN_( false ),
      negate_( false ),
      dataValue_( NULL ),
      lhs_(),
      operations_( true )
{}
