/*******************************************************************
*
*  DESCRIPTION: class CoupledCell
*
*  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: 06/03/1999 (v2)
*
*******************************************************************/

/** include files **/
#include <algorithm>
#include <coupcell.h>         // CoupledCell
#include <cellstate.h>        // class CellState
#include <strutil.h>          // class int2Str
#include <atomcell.h>         // class AtomicCell
#include <procadm.h>          // SingleProcessorAdmin
#include <modeladm.h>         // SingleModelAdm

using namespace std;
/** methods **/

CoupledCell::CoupledCell( const std::string &name )
: Coupled( name )
, state( NULL )
, localFn( LocalTransAdmin::InvalidFn )
{}
 
/*******************************************************************
* Function Name: ~CoupledCell
********************************************************************/
CoupledCell::~CoupledCell()
{
	if( state )
		delete state; 
}

/*******************************************************************
* Function Name: cellState
********************************************************************/
CoupledCell &CoupledCell::cellState( CellState *ptr )
{
	MASSERTMSG( ptr != NULL, "Attemp to set a CellState with a NULL value\n");

	if( state )
		delete state ;

	state = ptr ;
	return *this ;
}

/*******************************************************************
* Function Name: createCells
********************************************************************/
CoupledCell &CoupledCell::createCells( const CellPositionList &neighbors, const CellPositionList &selectList )
{
	cellState( new CellState( *((nTupla *) &(dimension())), wrapped ) );

	AtomicCell **matrix = new AtomicCell *[ dimension().totalElements() ] ;

	CellPositionList::const_iterator cursor;
	/////////////////////////////////////////////////////////////
	// let's create first the cells sepecified in the select std::list
	/////////////////////////////////////////////////////////////
	for (cursor = selectList.begin(); cursor != selectList.end(); cursor++)
		matrix[ cursor->calculateIndex( dimension() ) ] = &( SingleModelAdm::Instance().newAtomicCell( inertial, cellName(*cursor) ) );

	//////////////////////
	// cell creation
	//////////////////////
	nTupla	dim( dimension() );
	AtomicCell *atomicCell;
	CellPositionList::const_iterator toFind;

	MASSERTMSG( !dim.contains(0), "Attemp to create a Coupled Cell Model with a dimension containing the value 0.");

	CellPosition	counter( dim.dimension(), 0);
	register bool	overflow = false;

	while ( !overflow ){
		// Busco la posicion pos
		toFind = selectList.begin();
		while (toFind != selectList.end() && !(*toFind == counter ))
			toFind++;

		if (toFind == selectList.end())		// no encontre
			matrix[ counter.calculateIndex( dim, false ) ] = &( SingleModelAdm::Instance().newAtomicCell( inertial, cellName(counter) ) );

		atomicCell = matrix[ counter.calculateIndex( dim, false ) ];

		atomicCell->neighborhood( new NeighborhoodValue( *cellState(), neighbors, counter));

		// Neighborhoood must be set 
		atomicCell->value( initialValue );

		atomicCell->localFunction( localTransition() );

		// inserts ordered by id
		addModel( *atomicCell );

		overflow = counter.next( dim );
	}

	/////////////////////
	// links creation
	/////////////////////
	CellPosition	auxi;
	counter.fill( dim.dimension(), 0 );
	overflow = false;

	while ( !overflow )
	{
		MASSERTMSG( matrix[ counter.calculateIndex( dim, false ) ] != NULL, "Incorrect value for matrix in CoupledCell::CreateCells");

		for (cursor = neighbors.begin(); cursor != neighbors.end(); cursor++)
		{
			// get the real position
			auxi = *cursor;
			auxi += counter;

			if (wrapped)
			{
				auxi.canonizar( dim );

				// ADD FOR THE REVERSE NEIGHBOR LIST
				addInfluence( matrix[ auxi.calculateIndex( dim, false ) ]->description(), AtomicCell::outPort,
						    matrix[ counter.calculateIndex( dim, false ) ]->description(), AtomicCell::neighborChangePort );
			}
			else if (cellState()->includes( auxi ))
				// ADD FOR THE REVERSE NEIGHBOR LIST
				// make the link between neighbors
				addInfluence( matrix[ auxi.calculateIndex( dim, false ) ]->description(), AtomicCell::outPort,
						    matrix[ counter.calculateIndex( dim, false ) ]->description(), AtomicCell::neighborChangePort );
		}
		overflow = counter.next( dim );
	}

	cout<<"end creating cells\n";
	delete [] matrix;
	return *this;
}

/*******************************************************************
* Method: localTransition
********************************************************************/
CoupledCell &CoupledCell::setLocalTransition( const CellPosition &pos, const LocalTransAdmin::Function &fName )
{
	AtomicCell &cell( static_cast< AtomicCell & >( SingleProcessorAdmin::Instance().processor( cellName( pos ) ).model() ) );
	cell.localFunction( fName );
	return *this ;
}

/*******************************************************************
* Function Name: setCellValue
********************************************************************/
CoupledCell &CoupledCell::setCellValue( const ModelId &model, Value v )
{
	AtomicCell &cell( static_cast< AtomicCell & >( SingleProcessorAdmin::Instance().processor( model ).model() ) );

	cell.value( Real(v) );
	return *this ;
}

/*******************************************************************
* Function Name: setCellValue
********************************************************************/
CoupledCell &CoupledCell::setCellValue( const CellPosition &pos, const Real &v )
{
//	AtomicCell &cell( static_cast< AtomicCell & >( SingleProcessorAdmin::Instance().processor( cellName( pos ) ).model() ) );

//	cell.value( v );
//	return *this ;

	(*cellState())[ *(CellPosition *) &pos ] = v;
	return *this;
}

/*******************************************************************
* Function Name: setCellValue
********************************************************************/
CoupledCell &CoupledCell::setCellValue( const std::string &sCellPos, const Real &v )
{
	AtomicCell &cell( static_cast< AtomicCell & >( SingleProcessorAdmin::Instance().processor( cellName(sCellPos) ).model() ) ) ;
	cell.value( v );

	return *this ;
}
