/*****************************************************************************
  Copyright (C) 1993, 2001, 2011 Humboldt-Universitaet zu Berlin
  
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*****************************************************************************/

/**
 * @file   Example_CA_GameOfLife.cpp
 * @author Oliver Keil
 * @date   created at 2011/06/09
 * @brief  Simple Cellular Automaton example, Game-Of-Life. 
 * @since  3.0
 **/

/**
 * @example Example_CA_GameOfLife.cpp
 * Shows basic usage of Cellular Automaton implementation.
 *
 * This demonstrates how to use Cellular Automata in ODEMx.  For this a simple
 * Game-Of-Life automaton is implemented.
 *
 * We use a 'Cell'-derivation named 'GameOfLifeCell' to define cells of our
 * automaton.  To describe a cell's behavior we need to override
 * 'transitionFunction'.  So, our 'transitionFunction' implements the well
 * known Game-Of-Life rules.
 *
 * Functions 'getMooreNeighbors', 'getValue', and 'setValue' are defined to
 * ease the implementation of 'transitionFunction'.
 *
 * In 'main' we create a new 'CellMonitor' using our new defined
 * 'GameOfLifeCell'.  Before starting this new automaton it gets initialized
 * with a simple "glider".  Finally, the automaton runs for some time and
 * program will be closed explicitly.
 **/
#include <odemx/odemx.h>

#include <string>
#include <iostream> 

class GameOfLifeCell : public odemx::base::Cell { 
public:
	// A cell ctor needs a dimension---number of state variables per cell
	GameOfLifeCell (unsigned int dim) : Cell (dim) {} 

private:
	// Get a list of neighbors' indexes
	std::list<int> getMooreNeighbors (void) {
		return calculateMooreNeighborhood (cellIndex, 1);
	} 

	// Get and set cell's value of *the* state variable
	int  getValue () { return container->getStateValue (cellIndex, 0); }
	void setValue (int val) { Cell::setValue (cellIndex, 0u, val); } 

	// Get value of state variable by 'cellIndex'
	int getValue (unsigned int cellIndex) {
		return this->container->getStateValue (cellIndex, 0);
	} 

	// Straight forward definition of cell's transition function, implementing
	// well known rules of Game-Of-Life cell
	virtual void transitionFunction (odemx::base::SimTime time) {
		using std::cout;
		using std::endl;
		
		unsigned int livingNeighbors = 0;

		// estimate living neighbor cells
		std::vector<int> neighborValues = pullValue(0); 
		for (unsigned int i = 0; i < neighborValues.size(); ++i)
			if (neighborValues[i] == 1) ++livingNeighbors; 

		// change cell's state according to well known Game-Of-Life rules
		if (getValue() == 0 && livingNeighbors == 3)
			setValue (1);
		if (getValue() == 1 && (livingNeighbors < 2 || livingNeighbors > 3))
			setValue (0);
	}
};


//
// Helper function to print a 2-dimensional cellular automaton. 
// Living cells are marked by '*' and dead ones by '.'.
//
void printCells (odemx::base::CellMonitor& cm) { 
	unsigned int rows = cm.getRowCount();
	unsigned int cols = cm.getColumnCount(); 
	int* valuesVector = cm.getValues();
	using std::cout;
	using std::endl;

	for (unsigned int index = 0; index < (rows * cols); index++) {
		cout << (valuesVector[index] ? "*" : ".") << " "; 
		if (! ((index + 1) % cols) )
			cout << endl;
	}
	cout << endl << endl;
}

//
// Helper function to set a glider on position (x,y).  Position marks
// upper-left corner of glider. The upper-left corner of whole 2-dimensional
// cellular automaton equals position (0,0).
//
// Fails if glider is positioned wrongly.
//
int setGlider (odemx::base::CellMonitor& cm, unsigned int posx
		, unsigned int posy)
{
	unsigned int rows = cm.getRowCount();
	unsigned int cols = cm.getColumnCount(); 
	int* valuesVector = cm.getValues();
	
	// glider needs space of 3x3 cells to be set correctly
	if ((posx + 2) > cols)
		return -1; 
	if ((posy + 2) > rows)
		return -1;

	//   *
	//     *
	// * * *
	valuesVector[(posx + (cols *  posy)     ) + 1] = 1;
	valuesVector[(posx + (cols * (posy + 1))) + 2] = 1;
	valuesVector[(posx + (cols * (posy + 2)))    ] = 1;
	valuesVector[(posx + (cols * (posy + 2))) + 1] = 1;
	valuesVector[(posx + (cols * (posy + 2))) + 2] = 1;
	
	return 0;
}


int main () {
	odemx::base::Simulation& sim = odemx::getDefaultSimulation(); 

	unsigned int cellDimension = 1;
	GameOfLifeCell cell (cellDimension); 

	odemx::data::Label monitorName = "Game of Life Monitor";
	unsigned int inCount = 1;        // number of inputs per cell
	unsigned int outCount = 1;       // number of outputs per cell
	unsigned int cols = 10;          // columns and rows of automaton
	unsigned int rows = 10;          //
	double       timestep = 1; 
	odemx::base::CellMonitor cm (sim, monitorName, inCount, outCount, rows
		, cols, timestep, &cell);

	setGlider (cm, 0, 0);

	cm.setTimeLimit (3.0);
	cm.activate();
	
	printCells (cm);
	for (int i = 0; i < 45; i++) {
		sim.step();
		printCells (cm);
	} 

	return 0;
}
