/*******************************************************************
*
*  DESCRIPTION: class AtomicCell
*
*  AUTHOR:    Amir Barylko & Jorge Beyoglonian
*  Version 2: Daniel Rodriguez.
*
*  EMAIL: mailto://amir@dc.uba.ar
*         mailto://jbeyoglo@dc.uba.ar
*         mailto://drodrigu@dc.uba.ar
*
*  DATE: 27/06/1998
*  DATE: 25/04/1999 (v2)
*
*******************************************************************/

// ** include files **
#include "atomcell.h"      // base header
#include "message.h"       // InternalMessage
#include "except.h"        // MASSERTMSG
#include "strutil.h"       // function lowerCase

// ** public data **
const string AtomicCell::cellClassName( "AtomicCell" ) ;

const string AtomicCell::outPort( "out" ) ;
const string AtomicCell::neighborChangePort( "neighborChange" ) ;


void AtomicCell::setPortInFunction( const string portName, const string functionName )
{
	inputPortFunction()[ portName ] = functionName;
}

Model &AtomicCell::outputFunction( const InternalMessage &msg )
{
	this->sendOutput( msg.time(), outputPort(), this->value().value(), NULL );
	return *this;
}

/*******************************************************************
* Function Name: ~AtomicCell
********************************************************************/
AtomicCell::~AtomicCell()
{
	if( neighbors )
		delete neighbors ;
}

/*******************************************************************
* Method: AtomicCell
* Description: Constructor
********************************************************************/
AtomicCell::AtomicCell( const string &name, const LocalTransAdmin::Function &lf ) 
: Atomic( name )
, neighbors( NULL )
, localFn( lf )
, out( addOutputPort( outPort ) )
, neighborChange( addInputPort( neighborChangePort ) )
{}

/*******************************************************************
* Method: initFunction
* Description:
********************************************************************/
Model &AtomicCell::initFunction()
{
	ExternalMessage msg( Time::Zero, this->id(), this->neighborPort(), 0, NULL );
	this->externalFunction( msg );
	return *this;
}

/*******************************************************************
* Function Name: neighborhood
********************************************************************/
AtomicCell &AtomicCell::neighborhood( NeighborhoodValue *nval )
{
	if( neighbors )
		delete neighbors ;

	neighbors = nval ;
	return *this ;
}

/*******************************************************************
* Function Name: addInPort
* Description: Adds an input port if not exists.
********************************************************************/
bool AtomicCell::addInPort( string portName )
{
	// See if the port exists
	string	name( lowerCase( portName ) );

	PortList::iterator	cursor;

	// search the port
	for (cursor = inputPort().begin() ;
		cursor != inputPort().end() && cursor->second->name() != name ;
		cursor++ )
		;
			
	// If not exists
	if (cursor == inputPort().end() )
	{
		Port	*port = &( addInputPort( name ) ); // Call to the Model's method

		// Adds the port in the IN list
		inputPort()[ port->id() ] = port;

		// Now adds the default function in the port function list
		inputPortFunction()[ name ] = DEFAULT_FUNCTION_InPort;

		// Now set the last value arrived to the port with a undefined value
		inputPortValues()[ name ] = Real();

		return true;
	}
	return false;
}

/*******************************************************************
* Function Name: addOutPort
* Description: Adds an output port if not exists.
********************************************************************/
bool AtomicCell::addOutPort( string portName )
{
	// See if the port exists
	string	name( lowerCase( portName ) );

	if (name == outPort)		// Si es el puerto "OUT"
		return false;

	PortList::iterator	cursor;

	// search the port
	for (cursor = outPortList().begin() ;
		cursor != outPortList().end() && cursor->second->name() != name ;
		cursor++ )
		;
			
	// If not exists
	if (cursor == outPortList().end() )
	{
		Port	*port = &( addOutputPort( name ) ); // Call to the Model's method

		// Adds the port in the IN list
		outPortList()[ port->id() ] = port;

		return true;
	}
	return false;
}

/*******************************************************************
* Function Name: setPortValue
* Description: set the last value arrived to a port
********************************************************************/
void AtomicCell::setPortValue( const string portName, const Real portValue )
{
	// See if the port exists
	string	name( lowerCase( portName ) );

	PortList::iterator	cursor;

	// search the port
	for (cursor = inputPort().begin() ;
		cursor != inputPort().end() && cursor->second->name() != name ;
		cursor++ )
		;
			
	// If not exists, then STOP !!
	MASSERTMSG( cursor != inputPort().end(), "The port not exist, in method AtomicCell::setPortValue(port, value)" );

	// Now set the last value arrived to the port with a undefined value
	inputPortValues()[ name ] = portValue;
}

/*******************************************************************
* Function Name: getOutPort
* Description: gets an output port
********************************************************************/
void AtomicCell::getOutPorts(VirtualPortList *vpl)
{
	PortList::iterator	cursor = outPortList().begin();

	while (cursor != outPortList().end() )
	{
		vpl->insert( VirtualPortList::value_type( cursor->second->name(), cursor->second ) );
		cursor++;
	}
}
