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

#include "MissionItem.h"

#include "component/Component.h"
#include "component/BehaviorRegistry.h"
#include "data/Slate.h"
#include "logger/MissionStateLogger.h"
#include "missionScript/Aggregate.h"
#include "missionScript/Assign.h"
#include "missionScript/DefineBehavior.h"
#include "missionScript/MissionManager.h"
#include "missionScript/MissionNode.h"
#include "missionScript/ReadDataComponent.h"
#include "missionScript/SendDataComponent.h"
#include "missionScript/SyslogComponent.h"
#include "missionScript/ValueClause.h"
#include "units/Units.h"
#include "utils/FastMap.h"

/// Destructor
MissionItem::~MissionItem()
{
    if( NULL != component_ )
    {
        delete component_;
    }

    if( NULL != breakClause_ )
    {
        delete breakClause_;
    }

    if( NULL != whenClause_ )
    {
        delete whenClause_;
    }

    if( NULL != whileClause_ )
    {
        delete whileClause_;
    }

    if( NULL != preemptiveClause_ )
    {
        delete preemptiveClause_;
    }

    if( NULL != timeoutItem_ )
    {
        delete timeoutItem_;
    }

    if( NULL != defineBehaviors_ )
    {
        defineBehaviors_->clear( true );
        delete defineBehaviors_;
    }
}

/// Returns an instance, or NULL if an instance can't be returned
MissionItem* MissionItem::Instance( MissionItem* parent, MissionNode* node, unsigned int tablePos, Logger& logger )
{
    MissionItem * missionItem = NULL;

    missionItem = new MissionItem( parent, tablePos );
    missionItem->initializeFromScript( node, logger );
    if( NULL == missionItem->component_ )
    {
        parent->getComponent()->getLogger().syslog( "Error!!!! no component for child behavior.", Syslog::CRITICAL );
        delete missionItem;
        missionItem = NULL;
    }

    return missionItem;
}

// indicates if the WHEN clause is preemptive
bool MissionItem::isPreemptive()
{
    return ( NULL != preemptiveClause_ && preemptiveClause_->test() )
           || ( NULL != getAggregate() && getAggregate()->containsPreemptive() );
}

/// sets the repeat index
void MissionItem::setRepeatIndex( int repeatIndex )
{
    if( NULL != component_ && repeatIndex != repeatIndex_ )
    {
        MissionStateLogger::SetRepeatIndex( component_->getName(), repeatIndex );
    }
    repeatIndex_ = repeatIndex;
}

// Returns true if the item's missionState is BEHAVIOR_ACTIVE,
// Or is equal to the indicated activeItem,
// Or has a whenClause or whileClause true,
// But not an breakClause true!
bool MissionItem::isActive( MissionItem* activeItem )
{
    bool active = this == activeItem || callCount_ > 0;
    if( !active && whileClause_ == NULL )
    {
        active = component_->getBehaviorState() == Behavior::BEHAVIOR_ACTIVE
                 || !isSequence();
    }
    if( !active && whenClause_ != NULL )
    {
        active =  whenClause_->test();
    }
    if( !active  && whileClause_ != NULL )
    {
        active = whileClause_->test();
    }
    if( active && breakClause_ != NULL )
    {
        active =  !breakClause_->test();
        //printf("breakClause_->test() returned %d\n", !active);
    }
    return active;
}

void MissionItem::initialize()
{
    startTime_ = Timespan::Now();
    if( NULL != repeatArgReader_ )
    {
        int repeatLimit;
        if( repeatArgReader_->getDefaultUnit()->getBaseUnit() != Units::COUNT )
        {
            component_->getLogger().syslog( "Repeat must be in counting units, not ",
                                            repeatArgReader_->getDefaultUnit()->getName(), Syslog::CRITICAL );
        }
        else if( repeatArgReader_->read( Units::SECOND, repeatLimit ) )
        {
            repeatLimit_ = repeatLimit;
        }
        else
        {
            component_->getLogger().syslog( "Could not set repeat!", Syslog::CRITICAL );
        }
    }
    if( NULL != timeoutArgReader_ )
    {
        float duration;
        if( timeoutArgReader_->getDefaultUnit()->getBaseUnit() != Units::SECOND )
        {
            component_->getLogger().syslog( "Timeout must be in time units, not ",
                                            timeoutArgReader_->getDefaultUnit()->getName(), Syslog::CRITICAL );
        }
        else if( timeoutArgReader_->read( Units::SECOND, duration ) )
        {
            component_->setTimeout( duration );
        }
        else
        {
            component_->getLogger().syslog( "Could not set timeout!", Syslog::CRITICAL );
        }
    }
    timedOut_ = false;
    if( component_ != NULL )
    {
        if( component_->getBehaviorState() != Behavior::BEHAVIOR_ACTIVE )
        {
            component_->getLogger().logComponentRun( component_, repeatIndex_ + 1 );
            component_->initialize();
            if( !isAggregate() )
            {
                component_->setBehaviorState( Behavior::BEHAVIOR_ACTIVE );
            }
        }
    }
    initialized_ = true;
}

void MissionItem::run()
{
    /// Item running -- therefore, it is no longer preempted.
    if( preempted_ )
    {
        if( component_ != NULL )
        {
            component_->getLogger().logComponentRun( component_, repeatIndex_ + 1 );
        }
        preempted_ = false;
    }
    if( timedOut_ )
    {
        if( timeoutItem_ && timeoutItem_->getComponent()->getBehaviorState() == Behavior::BEHAVIOR_ACTIVE )
        {
            timeoutItem_->run();
        }
        return;
    }
    float duration;
    if( NULL != timeoutArgReader_ && timeoutArgReader_->read( Units::SECOND, duration ) )
    {
        component_->setTimeout( duration );
    }
    if( !timedOut_ && component_->getTimeout() != Timespan::INVALID_TIMESPAN && startTime_.elapsed() >= component_->getTimeout() )
    {
        setTimedOut();
        if( timeoutItem_ )
        {
            timeoutItem_->run();
        }
        return;
    }
    if( component_ != NULL )
    {
        if( component_->getBehaviorState() != Behavior::BEHAVIOR_ACTIVE )
        {
            initialize();
        }
        component_->execute();
    }
}

bool MissionItem::runIfUnsatisfied()
{
    bool satisfied( false );
    if( timedOut_ )
    {
        if( timeoutItem_ )
        {
            return timeoutItem_->runIfUnsatisfied();
        }
        else
        {
            return true;
        }
    }
    float duration;
    if( NULL != timeoutArgReader_ && timeoutArgReader_->read( Units::SECOND, duration ) )
    {
        component_->setTimeout( duration );
    }
    if( component_->getTimeout() != Timespan::INVALID_TIMESPAN && startTime_.elapsed() >= component_->getTimeout() )
    {
        setTimedOut();
        if( timeoutItem_ )
        {
            return timeoutItem_->runIfUnsatisfied();
        }
        else
        {
            return true;
        }
    }
    if( NULL != component_ )
    {
        if( component_->getBehaviorState() != Behavior::BEHAVIOR_ACTIVE )
        {
            initialize();
        }
        if( isAggregate() )
        {
            component_->execute();
            satisfied = component_->isCompleted();
        }
        else
        {
            satisfied = component_->executeIfUnsatisfied();
        }
    }
    return satisfied;
}

bool MissionItem::satisfied()
{
    bool satisfied( false );
    if( timedOut_ )
    {
        if( timeoutItem_ )
        {
            return timeoutItem_->satisfied();
        }
        else
        {
            return true;
        }
    }
    if( component_->getTimeout() != Timespan::INVALID_TIMESPAN && startTime_.elapsed() >= component_->getTimeout() )
    {
        setTimedOut();
        if( timeoutItem_ )
        {
            return timeoutItem_->satisfied();
        }
        else
        {
            return true;
        }
    }
    if( NULL != component_ )
    {
        switch( component_->getBehaviorState() )
        {
        case Behavior::BEHAVIOR_ACTIVE:
            return isAggregate() ? false : component_->satisfied();
        case Behavior::BEHAVIOR_CALLED:
            return false;
        case Behavior::BEHAVIOR_COMPLETED:
        case Behavior::BEHAVIOR_INACTIVE:
        case Behavior::BEHAVIOR_TIMEOUT:
            return isAggregate();
        }
    }
    return satisfied;
}

/// Called when the mission component becomes preempted.
void MissionItem::preempted()
{
    if( !preempted_ )
    {
        if( component_ != NULL )
        {
            component_->getLogger().logComponentRun( component_, -1 );
            component_->preempted();
        }
        preempted_ = true;
    }
}

void MissionItem::uninitialize()
{
    callCount_ = 0;
    if( component_ != NULL )
    {
        if( component_->getBehaviorState() != Behavior::BEHAVIOR_INACTIVE )
        {
            component_->getLogger().logComponentRun( component_, 0 );
            component_->uninitialize();
            component_->setBehaviorState( Behavior::BEHAVIOR_INACTIVE );
        }
    }
    initialized_ = false;
}

void MissionItem::addCall()
{
    if( callCount_ == 0 )
    {
        timedOut_ = false;
        startTime_ = Timestamp::Now();
    }
    ++callCount_;
    if( component_ != NULL )
    {
        if( component_->getBehaviorState() != Behavior::BEHAVIOR_ACTIVE )
        {
            component_->setBehaviorState( Behavior::BEHAVIOR_CALLED );
        }
    }
}

void MissionItem::removeCall()
{
    callCount_ -= callCount_ > 0 ? 1 : 0;
    if( component_ != NULL )
    {
        if( component_->getBehaviorState() == Behavior::BEHAVIOR_CALLED )
        {
            component_->setBehaviorState( Behavior::BEHAVIOR_INACTIVE );
        }
    }
}

/// Protected Constructor
MissionItem::MissionItem( MissionItem* parent, unsigned int tablePos )
    : parentItem_( parent ),
      tablePos_( tablePos ),
      component_( NULL ),
      sequence_( false ),
      parallel_( false ),
      aggregate_( false ),
      breakClause_( NULL ),
      whenClause_( NULL ),
      whileClause_( NULL ),
      preemptiveClause_( NULL ),
      callCount_( 0 ),
      repeatIndex_( 0 ),
      repeatLimit_( 1 ),
      repeatArgReader_( NULL ),
      startTime_( Timespan::INVALID_TIMESPAN ),
      timedOut_( false ),
      timeoutArgReader_( NULL ),
      timeoutItem_( NULL ),
      defineBehaviors_( NULL ),
      preempted_( true ),
      initialized_( false ),
      node_( NULL ),
      ignoreWhenWhile_( false )
{
}

void MissionItem::initializeFromScript( MissionNode* node, Logger& logger )
{
    Str nodeName( ValueClause::StripPastColon( node->getName() ) );
    MissionNode* insertNode( NULL );
    Str name;
    node_ = node;
    const char* id = node->getAttribute( "Id" );
    if( NULL != id )
    {
        if( NULL != parentItem_ )
        {
            name = parentItem_->getComponent()->getName() + Str( ":" ) + id;
        }
        else
        {
            name = id;
        }
    }
    else
    {
        if( NULL != parentItem_ )
        {
            name = parentItem_->getComponent()->getName() + Str( ":" ) + Str( tablePos_, 26, 10 );
        }
        else
        {
            name = Str( tablePos_, 26, 10 );
        }
    }

    FastMap< const Str, DataValue* > dataValueMap;

    component_ = NULL;
    aggregate_ = false;
    MissionNode* childNode = NULL;


    if( nodeName == "Mission" )
    {
        component_ = new Aggregate( name );
        ( ( Aggregate* )component_ )->setIndex( tablePos_ );
        aggregate_ = true;
    }
    else if( nodeName == "Aggregate" )
    {
        component_ = new Aggregate( name );
        ( ( Aggregate* )component_ )->setIndex( tablePos_ );
        aggregate_ = true;
    }
    else if( nodeName == "Call" )
    {
        const char* refId = node->getAttribute( "RefId" );
        if( NULL == refId )
        {
            logger.syslog( "No RefId specified for Call at " + name, Syslog::CRITICAL );
            return;
        }
        MissionItem* ref = findItemByRefId( refId );
        if( NULL == ref )
        {
            logger.syslog( "No Aggregate corresponding to RefId " + Str( refId ) + " at " + name, Syslog::CRITICAL );
            return;
        }

        parallel_ = ref->isParallel();
        sequence_ = ref->isSequence();

        component_ = new Aggregate( name );
        ( ( Aggregate* )component_ )->setIndex( tablePos_ );
        aggregate_ = true;

        const char* priorityHere = node->getAttribute( "PriorityHere" );
        if( NULL != priorityHere && strncasecmp( priorityHere, "true", 4 ) == 0 )
        {
            node = ref->node_;
            ignoreWhenWhile_ = true;
        }
        else
        {
            ( ( Aggregate* )component_ )->setRef( ref );
        }
    }
    else if( nodeName == "Timeout" )
    {
        if( NULL == id )
        {
            name += "_Timeout";
        }
        component_ = new Aggregate( name );
        ( ( Aggregate* )component_ )->setIndex( tablePos_ );
        aggregate_ = true;
    }
    else
    {
        if( nodeName == "Assign" )
        {
            component_ = Assign::Instance( node, name, this, logger );
            interpretRunIn( node, name, logger );
            if( !sequence_ && !parallel_ )
            {
                sequence_ = true;
            }
            return;
        }
        else if( nodeName == "SendData" )
        {
            component_ = SendDataComponent::Instance( node, name, this, NULL, logger );
            sequence_ = true;
            return;
        }
        else if( nodeName == "Syslog" || nodeName == "SendStr" )
        {
            component_ = SyslogComponent::Instance( node, name, this, NULL, logger );
            sequence_ = true;
            return;
        }
        else if( nodeName == "ReadDatum" )
        {
            component_ = ReadDataComponent::Instance( node, name, this, NULL, logger );
            sequence_ = true;
            parallel_ = false;
            interpretRepeat( node, name, logger );
        }
        else if( nodeName == "ReadData" )
        {
            component_ = ReadDataComponent::Instance( node, name, this, NULL, logger );
            sequence_ = false;
            parallel_ = true;
        }
        else if( nodeName == "CustomBehavior" )
        {
            const char* nameAttr = node->getAttribute( "Name" );
            if( NULL == nameAttr )
            {
                logger.syslog( "No Name specified for CustomBehavior at " + name, Syslog::CRITICAL );
                return;
            }
            nodeName = nameAttr;
        }


        DefineBehavior* defineBehavior = findDefineBehavior( nodeName );

        BehaviorRegistryEntry* entry =
            NULL == component_ ? BehaviorRegistry::Find( nodeName ) : NULL;

        if( NULL == defineBehavior && NULL == entry && NULL == component_ )
        {
            logger.syslog( "Unable to find MissionComponent with name " + nodeName + " at " + name, Syslog::CRITICAL );
            return;
        }
        else if( NULL != defineBehavior )
        {
            component_ = new DefinedBehavior( name + ".", defineBehavior );
        }
        else if( NULL != entry )
        {
            BehaviorCreator behaviorCreator( entry->behaviorCreator_ );
            const Module* module( entry->module_ );
            component_ = ( *behaviorCreator )( name + ".", module );
        }
    }
    if( sequence_ == false && NULL != component_ )
    {
        interpretRunIn( node, name, logger );
        if( !sequence_ && !parallel_ )
        {
            parallel_ = NULL != component_ && !isAggregate();
            sequence_ = aggregate_;
        }
        if( aggregate_ )
        {
            MissionItem* ref = ( ( Aggregate* )component_ )->getRef();
            if( NULL !=  ref )
            {
                parallel_ = ref->parallel_;
                sequence_ = ref->sequence_;
            }
            ( ( Aggregate* )component_ )->setParallel( parallel_ );
        }
    }

    MissionItem* childItem = NULL;
    unsigned int childTablePos( 0 );
    childNode = NULL == childNode ? node->getFirstChild() : childNode;
    while( NULL != childNode )
    {
        Str childName( childNode->getName() );

        if( childName == "Insert" )
        {
            const char* filename = childNode->getAttribute( "Filename" );

            MissionNode* rootNode = filename == NULL ? NULL : MissionManager::LoadRootNode( filename, true, logger );
            MissionNode* topNode( NULL );
            if( NULL != rootNode )
            {
                topNode = MissionManager::FindTopNode( rootNode, logger );
            }

            if( NULL != topNode )
            {
                childNode->holdNode( rootNode );
                insertNode = childNode;
                childNode = topNode;
                childName = childNode->getName();

                const char* insertId = insertNode->getAttribute( "Id" );
                if( NULL != insertId )
                {
                    childNode->setAttribute( "Id", insertId );
                }

                // Print the mission out to Syslog::DEBUG
                MissionManager::LogMission( filename, logger );
            }
            else
            {
                logger.syslog( "No top node in file ", filename, Syslog::CRITICAL );

                if( NULL != rootNode )
                {
                    delete rootNode;
                }
            }
        }

        if( childName == "Description"
                || childName == "Parallel"
                || childName == "Progression"
                || childName == "Sequence" )
        {
            // do nothing for now, move on
        }
        else if( childName == "Preemptive" )
        {
            preemptiveClause_ = ValueClause::Instance( childNode, true, this, logger );
        }
        else if( childName == "Break" )
        {
            breakClause_ = ValueClause::Instance( childNode, true, this, logger );
        }
        else if( childName == "When" )
        {
            if( !ignoreWhenWhile_ )
            {
                whenClause_ = ValueClause::Instance( childNode, true, this, logger );
            }
        }
        else if( childName == "While" )
        {
            if( !ignoreWhenWhile_ )
            {
                whileClause_ = ValueClause::Instance( childNode, true, this, logger );
            }
        }
        else if( childName == "DefineArg" || childName == "DefineOutput" )
        {
            const char* argName = childNode->getAttribute( "Name" );
            if( !aggregate_ )
            {
                logger.syslog( "Only Aggregates can contain elements called " + childName, Syslog::CRITICAL );
            }
            else if( NULL == argName || *argName == 0 )
            {
                logger.syslog( "Unable to find Name for " + childName + " in " + name, Syslog::CRITICAL );
            }
            else
            {
                ValueClause* valueClause = ValueClause::Instance( childNode, true, this, logger );
                DataValue* dataValue( NULL );
                if( NULL == valueClause )
                {
                    logger.syslog( "Unable to find value for " + childName + " " + argName, Syslog::CRITICAL );
                }
                else
                {
                    dataValue = valueClause->getDataValue();
                }

                if( dataValue == NULL )
                {
                    logger.syslog( "Invalid value for " + childName + " " + argName + " in " + name, Syslog::CRITICAL );
                }
                else
                {
                    if( !valueClause->eval() )
                    {
                        logger.syslog( "Could not evaluate value for " + childName + " " + argName + " in " + name, Syslog::CRITICAL );
                    }
                    else
                    {
                        bool isOutput( childName == "DefineOutput" );
                        ( ( Aggregate* )component_ )->defineArg( argName, dataValue, isOutput );
                        logger.syslog( childName + " " + component_->getName() + "." + argName + " = " + dataValue->toString(), Syslog::INFO );
                    }
                }

                if( NULL != valueClause )
                {
                    delete valueClause;
                }
            }
        }
        else if( childName == "DefineBehavior" )
        {
            const char* argName = childNode->getAttribute( "Name" );
            if( !aggregate_ )
            {
                logger.syslog( "Only Aggregates can contain elements called " + childName, Syslog::CRITICAL );
            }
            else if( NULL == argName || *argName == 0 )
            {
                logger.syslog( "Unable to find Name for " + childName + " in " + name, Syslog::CRITICAL );
            }
            else
            {
                DefineBehavior* defineBehavior = DefineBehavior::Instance( childNode, argName, logger );
                if( NULL == defineBehavior )
                {
                    logger.syslog( "Could not create DefineBehavior at " + name, Syslog::CRITICAL );
                    return;
                }
                putDefineBehavior( argName, defineBehavior );
            }

        }
        else if( ( childName == "Setting" || childName == "Set" || childName == "SetString" || childName == "SetUri" )
                 &&  NULL != component_ && !isAggregate() )
        {
            MissionNode* uriNode = childNode->getFirstChild();
            if( NULL == uriNode )
            {
                logger.syslog( "Unable to find URI node for " + name + " setting ", Syslog::CRITICAL );
            }
            else
            {
                Str uriPart;
                if( 0 == strncmp( uriNode->getName(), "CustomUri", 10 ) )
                {
                    const char* uri = uriNode->getAttribute( "Uri" );
                    if( NULL != uri )
                    {
                        uriPart = ( ValueClause::StripPastLastDot( uri ) );
                    }
                }
                else
                {
                    uriPart = ( ValueClause::StripPastLastDot( uriNode->getName() ) );
                }
                MissionNode* dataValueNode = uriNode->getNextSibling( true );
                ValueClause* valueClause( NULL );
                if( NULL == dataValueNode )
                {
                    logger.syslog( "Unable to find value for " + name + " setting: " + uriPart, Syslog::CRITICAL );
                }
                else
                {
                    SettingReader* settingReader = component_->findSettingReader( uriPart );
                    const SettingURI* uri = settingReader == NULL ? NULL : &( settingReader->getSettingURI() );
                    bool isStringSetting = uri != NULL && uri->getBinaryType() == MULTIVALUE && uri->getBlobType() == NOT_BLOB;
                    valueClause = ValueClause::Instance( dataValueNode, false, this, logger, isStringSetting );
                }

                if( NULL == valueClause || NULL == valueClause->getDataValue() )
                {
                    logger.syslog( "Unable to parse value for " + name + " setting: " + uriPart, Syslog::CRITICAL );
                }
                else
                {
                    component_->addValueClause( uriPart, valueClause );
                }
            }
        }
        else if( ( childName == "OutputArg" )
                 &&  NULL != component_ && !isAggregate() )
        {
            const char* argName = childNode->getAttribute( "Name" );
            DataReader* dataReader = NULL;
            if( NULL == argName || *argName == 0 )
            {
                logger.syslog( "Unable to find Name for DefineArg in " + name, Syslog::CRITICAL );
            }
            else
            {
                dataReader = findArgReader( argName );
            }
            MissionNode* uriNode = NULL;
            if( NULL != dataReader )
            {
                uriNode = childNode->getFirstChild();
                if( NULL == uriNode )
                {
                    logger.syslog( "Unable to find URI node for " + name + " OutputArg " + argName, Syslog::CRITICAL );
                }
            }
            if( NULL != uriNode )
            {
                Str uriPart;
                if( 0 == strncmp( uriNode->getName(), "CustomUri", 10 ) )
                {
                    const char* uri = uriNode->getAttribute( "Uri" );
                    if( NULL != uri )
                    {
                        uriPart = ( ValueClause::StripPastLastDot( uri ) );
                    }
                }
                else
                {
                    uriPart = ( ValueClause::StripPastLastDot( uriNode->getName() ) );
                }
                DataReader* argReader = findArgReader( argName );
                DataWriter* argWriter = Slate::NewWriter( argReader->getOwner()->getName(), argName, argReader->getOwner() );
                component_->addArgWriter( uriPart, argWriter );
            }
        }
        else if( childName == "Timeout" )
        {
            const char* duration = childNode->getAttribute( "Duration" );
            bool timeoutOk( false );
            if( NULL == duration )
            {
                logger.syslog( "Missing timeout value for " + name, Syslog::CRITICAL );
            }
            else
            {
                Timespan timeoutDuration;
                if( timeoutDuration.setDuration( duration ) )
                {
                    timeoutOk = true;
                    component_->setTimeout( timeoutDuration );
                }
                else
                {
                    timeoutArgReader_ = findArgReader( duration );
                    if( NULL != timeoutArgReader_ )
                    {
                        timeoutOk = true;
                    }
                    else
                    {
                        logger.syslog( "Invalid timeout value for " + name, Syslog::CRITICAL );
                    }
                }
            }
            if( timeoutOk )
            {
                if( NULL != childNode->getFirstChild( true ) )
                {
                    timeoutItem_ = Instance( this, childNode, childTablePos, logger );
                }
            }
        }
        else if( aggregate_ )
        {
            childItem = Instance( this, childNode, childTablePos, logger );
            if( NULL != childItem )
            {
                ( ( Aggregate* )component_ )->push( childItem );
                ++childTablePos;
            }
        }
        else if( childName != "Initialize"
                 && childName != "Run"
                 && childName != "Uninitialize"
                 && childName != "IsSatisfied"
                 && nodeName != "ReadData"
                 && nodeName != "ReadDatum" )
        {
            logger.syslog( "Within " + name + ", unknown MissionItem element: " + childName, Syslog::ERROR );
        }

        if( NULL != insertNode )
        {
            if( NULL != childItem )
            {
                MissionNode* insertChildNode = insertNode->getFirstChild( true );
                while( NULL != insertChildNode )
                {
                    Str insertChildName = insertChildNode->getName();
                    if( insertChildName == "RedefineArg" )
                    {
                        const char* argName = insertChildNode->getAttribute( "Name" );
                        if( !childItem->isAggregate() )
                        {
                            logger.syslog( "Only Aggregates can contain elements called RedefineArg", Syslog::CRITICAL );
                        }
                        else if( NULL == argName || *argName == 0 )
                        {
                            logger.syslog( "Unable to find Name for RedefineArg in " + childItem->getComponent()->getName(), Syslog::CRITICAL );
                        }
                        else
                        {
                            ValueClause* valueClause = ValueClause::Instance( insertChildNode, true, childItem, logger );
                            if( NULL == valueClause )
                            {
                                logger.syslog( "Unable to find value for RedefineArg ", argName, Syslog::CRITICAL );
                            }
                            else
                            {
                                ( ( Aggregate* )childItem->getComponent() )->redefineArg( argName, valueClause );
                                logger.syslog( "RedefineArg " + childItem->getComponent()->getName() + "." + argName + " = " + valueClause->toString(), Syslog::INFO );
                            }
                        }
                    }
                    insertChildNode = insertChildNode->getNextSibling( true );
                }
            }
            childNode = insertNode->getNextSibling();
            insertNode = NULL;
        }
        else
        {
            childNode = childNode->getNextSibling();
        }
    }
}


void MissionItem::interpretRepeat( MissionNode* node, const Str& name, Logger& logger )
{
    int repeat;
    const char* repeatName = node->getAttribute( "Repeat", repeat );
    if( NULL != repeatName )
    {
        if( repeat > 0 || ( strlen( repeatName ) == 1 && repeatName[0] == '0' ) )
        {
            repeatLimit_ = repeat;
        }
        else
        {
            repeatArgReader_ = findArgReader( repeatName );
            if( NULL == repeatArgReader_ )
            {
                logger.syslog( "Invalid repeat value for " + name, Syslog::CRITICAL );
            }
        }
    }
}

void MissionItem::interpretRunIn( MissionNode* node, const Str& name, Logger& logger )
{
    MissionNode* childNode = node->getFirstChild();
    while( NULL != childNode )
    {
        const char* runIn = childNode->getName();
        if( NULL != runIn )
        {
            if( 0 == strncasecmp( runIn, "Sequence", 9 ) ||
                    0 == strncasecmp( runIn, "When", 5 ) )
            {
                sequence_ = true;
                interpretRepeat( childNode, name, logger );
            }
            else if( 0 == strncasecmp( runIn, "Parallel", 9 ) ||
                     0 == strncasecmp( runIn, "While", 5 ) )
            {
                parallel_ = true;
            }
            else if( 0 == strncasecmp( runIn, "Progression", 12 ) )
            {
                sequence_ = true;
                parallel_ = true;
                interpretRepeat( childNode, name, logger );
            }
        }
        childNode = childNode->getNextSibling( true );
    }
}

void MissionItem::setTimedOut()
{
    timedOut_ = true;
    if( component_ != NULL && component_->getBehaviorState() != Behavior::BEHAVIOR_TIMEOUT )
    {
        component_->getLogger().syslog( Str( "Timed out from " ) + startTime_.toString( 1 ), Syslog::INFO );
        component_->setBehaviorState( Behavior::BEHAVIOR_TIMEOUT );
    }
}

MissionItem* MissionItem::findItemByRefId( const Str& refId ) const
{
    MissionItem* ref = NULL;
    if( aggregate_ )
    {
        ref  = ( ( Aggregate* )component_ )->getItemByRefId( refId );
    }
    if( NULL == ref && NULL != parentItem_ )
    {
        ref = parentItem_->findItemByRefId( refId );
    }
    return ref;
}

void MissionItem::putDefineBehavior( const Str& name, DefineBehavior* defineBehavior )
{
    if( NULL == defineBehaviors_ )
    {
        defineBehaviors_ = new FastMap<const Str, DefineBehavior*>();
    }
    defineBehaviors_->put( name, defineBehavior );
}

DefineBehavior* MissionItem::findDefineBehavior( const Str& name ) const
{
    DefineBehavior* defineBehavior = NULL;
    if( NULL != defineBehaviors_ )
    {
        defineBehavior = defineBehaviors_->get( name );
    }
    if( NULL == defineBehavior && NULL != parentItem_ )
    {
        defineBehavior = parentItem_->findDefineBehavior( name );
    }
    return defineBehavior;
}

DataReader* MissionItem::findArgReader( const Str& argName ) const
{
    DataReader* argReader = NULL;
    const MissionItem* item = this;
    while( NULL != item )
    {
        if( item->isAggregate() )
        {
            Aggregate* aggregate( item->getAggregate() );
            unsigned int colonAt = argName.find( ':' );
            if( colonAt != Str::NO_POS && colonAt > 0 )
            {
                MissionItem* child( aggregate->getChild( aggregate->getName() + ":" + argName.substr( 0, colonAt ) ) );
                if( NULL != child )
                {
                    argReader = child->findArgReader( argName.substr( colonAt + 1 ) );
                }
            }
            else
            {
                argReader = aggregate->findArgReader( argName );
            }
            if( NULL != argReader )
            {
                break;
            }
        }
        item = item->getParent();
    }
    return argReader;
}
