/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */


static const char* const StateInterface_cxx_Version = 
    "$Id: StateInterface.cxx,v 1.2 2001/08/10 04:02:04 icahoon Exp $";


#include "global.h"
#include <cassert>
#include "StateInterface.hxx"
#include "Stimulus.hxx"
#include "Action.hxx"
#include "VLog.hxx"


using Vocal::Behavioral::StateInterface;
using Vocal::Behavioral::Stimulus;
using Vocal::Behavioral::Action;
using Vocal::Logging::VLog;
using Vocal::ReturnCode;


StateInterface::StateInterface(
    const char      *	name,
    Action  	    *	onEntry,
    Action  	    *	onExit
)
    :	myName(name ? name : "NullState"),
    	myEntry(onEntry),
    	myExit(onExit)
{
    const string    fn("StateInterface::StateInterface");
    VLog    	    log(fn);
    
    VDEBUG(log) << fn << ": " << *this << VDEBUG_END(log);
}


StateInterface::~StateInterface()
{
    const string    fn("StateInterface::~StateInterface");
    VLog    	    log(fn);
    
    VDEBUG(log) << fn << ": " << myName << VDEBUG_END(log);
}


void		    
StateInterface::addAction(
    const Stimulus 	    &	stimulus,
    Action  	    	    &	action,
    StateInterface          &	nextState
)
{
    const string    fn("StateInterface::addAction");
    VLog    	    log(fn);

    bool rc =	stimulusTable_.insert(
    	    	    StimulusPair(stimulus, StimulusData(action, nextState, 0))
	      	).second;

    assert( rc );

    VDEBUG(log) << fn 
    	    	<< "\n\tState          :\t" << myName
    	    	<< "\n\tStimulus       :\t" << stimulus
	    	<< "\n\tAction         :\t" << action
	    	<< "\n\tNextState      :\t" << nextState.name()
		<< VDEBUG_END(log);
}


void		    
StateInterface::addAction(
    const Stimulus 	    &	stimulus,
    Action  	    	    &	action,
    StateInterface          &	nextState,
    StateInterface          &	nextStateOnFail
)
{
    const string    fn("StateInterface::addAction");
    VLog    	    log(fn);

    bool rc =	stimulusTable_.insert(
    	    	    StimulusPair(stimulus, StimulusData(action, nextState, &nextStateOnFail))
	      	).second;

    assert( rc );

    VDEBUG(log) << fn 
    	    	<< "\n\tState          :\t" << myName
    	    	<< "\n\tStimulus       :\t" << stimulus
	    	<< "\n\tAction         :\t" << action
	    	<< "\n\tNextState      :\t" << nextState.name()
	    	<< "\n\tNextStateOnFail:\t" << nextStateOnFail.name()
		<< VDEBUG_END(log);
}


void        	    
StateInterface::entryAction(Action & onEntry)
{
    myEntry = &onEntry;
}


void        	    
StateInterface::exitAction(Action & onExit)
{
    myExit = &onExit;
}


void        	    
StateInterface::enter() throw ()
{
    const string    fn("StateInterface::enter");
    VLog    	    log(fn);

    if ( myEntry )
    {
    	VDEBUG(log) << fn << ": State: " << myName
	    	    << ": Action: " << *myEntry << VDEBUG_END(log);
	
    	myEntry->action();
    }
}


StateInterface &    
StateInterface::stimulate(
    const Stimulus  &   stimulus
) throw ()
{

    const string    fn("StateInterface::stimuluate");
    VLog    	    log(fn);

    VDEBUG(log) << fn 
    	    	<< "\n\tState: " << myName
    	    	<< ",\t\tStimulus: " << stimulus 
		<< VDEBUG_END(log);
		
    // Lookup the stimulus in the stimulus table. 
    //
    StimulusTable::iterator it = stimulusTable_.find(stimulus);
    
    // If it doesn't exist, do nothing.
    //
    if ( it == stimulusTable_.end() )
    {
    	VDEBUG(log) << fn 
    	    	<< "\n\tState: " << myName
    	    	<< ",\t\tAction: No Action" 
		<< VDEBUG_END(log);

    	return ( *this );
    }
    
    // If the stimulus exists first perform the exit action on this state, 
    // then perform the action associated with the stimulus, and finally
    // perform the entry action on the next state.
    //
    // Note that the exit action, the action associated with this stimulus, 
    // and the entry action of the next state may throw an exception.
    //
    exit();

    Action  &	action = (*it).second.action_;
    
    VDEBUG(log) << fn 
    	    	<< "\n\tState: " << myName
    	    	<< ",\t\tAction: " << action 
		<< VDEBUG_END(log);

    ReturnCode	rc = action.action(stimulus);
    
    StateInterface  	&   nextState = 
    	( rc != SUCCESS && (*it).second.nextStateOnFail_
	    ? *(*it).second.nextStateOnFail_
	    : (*it).second.nextState_ 
    	);

    VDEBUG(log) << fn 
    	    	<< "\n\tState: " << myName
    	    	<< ",\t\tNext State: " << nextState.name() 
		<< VDEBUG_END(log);
    
    nextState.enter();
    
    return ( nextState );
}


void        	    
StateInterface::exit() throw ()
{
    const string    fn("StateInterface::enter");
    VLog    	    log(fn);

    if ( myExit )
    {
    	VDEBUG(log) << fn 
	    	    << "\n\tState: " << myName
	    	    << ",\t\tAction: " << *myExit 
		    << VDEBUG_END(log);
		    
    	myExit->action();
    }
}


ostream &           
StateInterface::writeTo(ostream & out) const
{
    out << name() 
    	<< "\n\tEntry:\t" << ( myEntry ? *myEntry : Action() )
	<< "\n\tExit:\t" << ( myExit  ? *myExit  : Action() )
	<< "\n\tStimuli:\n\t{";
    
    for (   StimulusTable::const_iterator it = stimulusTable_.begin();
    	    it != stimulusTable_.end();
	    it++
	)
    {
    	out << "\n\t\tStimulus       :\t" << (*it).first
	    << "\n\t\tAction         :\t" << (*it).second.action_
	    << "\n\t\tNextState      :\t" << (*it).second.nextState_.name();

    	if ( (*it).second.nextStateOnFail_ )
	{
	    out << "\n\t\tNextStateOnFail:\t"  << (*it).second.nextStateOnFail_->name();
	}

	out << '\n';
    }
    
    return ( out << "\n\t}" );
}


const string &
StateInterface::name() const
{
    return ( myName );
}


StateInterface::StimulusData::StimulusData(
    Action  	    	&   action,
    StateInterface      &   nextState,
    StateInterface      *   nextStateOnFail
)   
    :   action_(action),
    	nextState_(nextState),
	nextStateOnFail_(nextStateOnFail)
{
}




syntax highlighted by Code2HTML, v. 0.9.1