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

#ifndef VALUECLAUSE_H_
#define VALUECLAUSE_H_

#include "data/DataReader.h"
#include "logger/Logger.h"
#include "utils/FlexArray.h"

class MissionNode;
class MissionItem;

/** \ingroup missionScript
 *
 *  A ValueClause is a set of boolean conditions that may be specified
 *  as conditions for executing (or stopping execution of a Behavior.
 *  The clause is described in the mission script, and is transformed into
 *  a set of ValueClause objects by the static Instance method.
 */
class ValueClause
{

public:

    /// An attempt to define different comparison operators
    typedef enum { OP_NONE,
                   OP_ADD,
                   OP_AND,
                   OP_DIV,
                   OP_EQ,
                   OP_GE,
                   OP_GT,
                   OP_LE,
                   OP_LT,
                   OP_MAX,
                   OP_MIN,
                   OP_MOD,
                   OP_MULT,
                   OP_NE,
                   OP_OR,
                   OP_POW,
                   OP_SUB,
                   OPCOUNT
                 } Operation;

    static const Str OP_NAMES[ OPCOUNT ];

    typedef enum
    {
        READER_NONE,
        READER_ELAPSED
    } ReaderOperation;

    /**
     *  Represents one side of a value clause equation
     */
    struct ValueClauseSide
    {
        DataValue* dataValue_;
        DataReader* dataReader_;
        MissionItem* owner_;
        ReaderOperation operationReader_;
        ValueClause* subClause_;
        bool deleteDataValue_;

        ValueClauseSide()
            : dataValue_( NULL ),
              dataReader_( NULL ),
              owner_( NULL ),
              operationReader_( READER_NONE ),
              subClause_( NULL ),
              deleteDataValue_( true )
        {}

        ~ValueClauseSide()
        {
            if( NULL != dataValue_ && deleteDataValue_ )
            {
                delete dataValue_;
            }
            if( NULL != subClause_ )
            {
                delete subClause_;
            }
        }
    };

    /**
     *  Represents a comparison and the right hand side of a value clause equation
     */
    struct ValueClauseOperation
    {
        Operation comparison_;
        ValueClauseSide rhs_;
    };

    // Simple public Constructor -- normally Static Instance Constructor is used
    ValueClause( DataValue* dataValue, Logger& logger );

    // Static Instance Constructor
    static ValueClause* Instance( MissionNode* node, bool nodeIsParent, MissionItem* owner, Logger& logger, bool isStrValue = false );

    // Used to read a DataReader
    static DataReader* ReadReader( MissionNode* node, MissionItem* owner,
                                   DataValue* defaultValue, Logger& logger, bool deleteDataValue = true );

    // Used to read a static DataValue
    static DataValue* ReadDataValue( MissionNode* unitNode, Logger& logger );

    // Used to read a Unit
    static const Unit* ReadUnit( MissionNode* unitNode, Logger& logger, bool reportErrors = true );

    // Used to read a string DataValue
    static DataValue* ReadStrValue( MissionNode* node, Logger& logger );

    // Used to read a string DataValue
    static DataValue* ReadStrValueUri( MissionNode* node, Logger& logger );

    /// Converts something like Foo:bar.baz into bar.baz
    static Str StripPastColon( Str name );

    /// Converts something like Foo:bar.baz into baz
    static Str StripPastLastDot( Str name );

    // tests the boolclause for truth
    virtual bool test();

    // evaluates the clause
    virtual bool eval();

    // evaluates the clause as a simple string
    virtual bool evalStr();

    // Destructor
    virtual ~ValueClause();

    // Convenience method
    Str toString();

    // Accessor method
    DataValue* getDataValue()
    {
        return dataValue_;
    }

private:
    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    ValueClause( const ValueClause& old ); // disallow copy constructor

    // Convenience method
    Str sideToString( ValueClauseSide& side );

    // Private Constructor
    ValueClause( Logger& logger );

    // Used by instance to read in the Comparison part of the Clause
    static Operation ReadComparison( MissionNode* node, Logger& logger );

    // Used by instance to read in an Entry (lhs or rhs) the Clause
    static bool ReadSide( MissionNode* node, ValueClauseSide& side, MissionItem* owner, Logger& logger, bool isStrValue = false );

    // Used by instance to read in an ValueClauseOperation (comparison and rhs) the Clause
    static ValueClauseOperation* ReadOperation( MissionNode* node, MissionItem* owner, Logger& logger );

    // Evaluates one side of the relationship at hand.
    bool evalSide( const ValueClauseSide& side );

    Logger& logger_;

    bool abs_;

    bool isNaN_;

    bool negate_;

    DataValue* dataValue_;

    ValueClauseSide lhs_;

    FlexArray<ValueClauseOperation*> operations_;

public:

    // If input values have changed since last run of a component
    // Inlined to avoid reference problems in unserialize
    bool wasTouchedSinceLastRun( const Component* component )
    {
        bool touched = false;
        for( int i = -1; !touched && i < ( int )operations_.size(); ++i )
        {
            ValueClauseSide* side;
            if( i < 0 )
            {
                side = &lhs_;
            }
            else
            {
                side = &operations_[i]->rhs_;
            }
            touched = NULL != side->dataReader_
                      && ( side->operationReader_ == READER_ELAPSED
                           || side->dataReader_->wasTouchedSinceLastRun( component ) );
            if( !touched && NULL != side->subClause_ )
            {
                touched =  side->subClause_->wasTouchedSinceLastRun( component );
            }
        }
        return touched;
    }

    // Return most recent timestamp of input values
    // Inlined to avoid reference problems in unserialize
    Timestamp getMostRecentTimestamp()
    {
        Timestamp mostRecentTimestamp( Timestamp::NOT_SET_TIME );
        for( int i = -1; i < ( int )operations_.size(); ++i )
        {
            ValueClauseSide* side;
            if( i < 0 )
            {
                side = &lhs_;
            }
            else
            {
                side = &operations_[i]->rhs_;
            }
            Timestamp timestamp = NULL == side->dataReader_ ? Timestamp::NOT_SET_TIME : side->dataReader_->getTimestamp();
            if( timestamp > mostRecentTimestamp )
            {
                mostRecentTimestamp = timestamp;
            }
            if( side->subClause_ )
            {
                timestamp = side->subClause_->getMostRecentTimestamp();
                if( timestamp > mostRecentTimestamp )
                {
                    mostRecentTimestamp = timestamp;
                }
            }
        }
        return mostRecentTimestamp;
    }

};

#endif /*VALUECLAUSE_H_*/
