//----------------------------------------------------------------------------
//	Copyright (C) 2002, 2004, 2007 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 Cell.cpp

 \author Sascha Qualitz

 \date created at 2010/01/05

 \brief Implementation of class Cell

 \sa Cell.h

 \since 3.0
 */

#include <odemx/base/cellular_automaton/Cell.h>
#include <odemx/base/cellular_automaton/CellVariablesContainer.h>
#include <odemx/base/cellular_automaton/CellMonitor.h>
#include <math.h>

using namespace odemx::base;

//------------------------------------------------------------------------------construction/destruction
Cell::Cell(unsigned dimension) : dimension(dimension) {

}

Cell::~Cell() {

}

void Cell::transitionFunction(SimTime time) {
	// reimplement this

	/*if(container->getInputValue(cellIndex-1) == 1) {
		setValue(cellIndex, 1);
	}*/

	//std::list<Continuous*>::iterator iter = find(this->continuousList.begin(), this->continuousList.end(), continuous);

	std::vector<int> vector = pullValue(0);
	unsigned barrier = 0;

	//cout << "Cell::transitionFunction(SimTime time) cellIndex: " << cellIndex << endl;

	for(int i = 0; i < vector.size(); i++){
		//cout << "Cell::transitionFunction(SimTime time): VectorValue: " << vector[i] << endl;
		if(vector[i] == 1) {
			//cout << "Cell::transitionFunction(SimTime time): cellIndex set to 1: " << cellIndex << endl;
			barrier++;

		}
	}
	if(barrier > 1)
		setValue(cellIndex, 1);
}

std::list<int> Cell::calculateNeighborhood(unsigned cellIndex,unsigned neighborhoodType, unsigned radius = 1) {

	switch (neighborhoodType) {
		case MOORE:
			return calculateMooreNeighborhood(cellIndex, radius);
			break;
		case NEUMANN:
			return calculateVonNeumannNeighborhood(cellIndex, radius);
			break;
		default:
			break;
	}
	return std::list<int>();
}

std::list<int> Cell::calculateMooreNeighborhood(unsigned cellIndex, unsigned radius = 1) {
	std::list<int> cellIndexList;

	unsigned rowCount = container->getNumberOfRows();
	unsigned columnCount = container->getNumberOfColumns();
	unsigned rowNumberIndex = cellIndex / columnCount;
	unsigned columnNumberIndex = cellIndex % columnCount;

	for (int i = 0; i < rowCount; ++i) {
		for (int k = 0; k < columnCount; ++k) {
			if(std::abs(i-(int)rowNumberIndex) <= radius && std::abs(k-(int)columnNumberIndex) <= radius) {
				if(!(i == rowNumberIndex && k == columnNumberIndex))
					cellIndexList.push_back(i*columnCount+k);
			}
		}
	}

	return cellIndexList;
}

std::list<int> Cell::calculateVonNeumannNeighborhood(unsigned cellIndex, unsigned radius = 1) {
	std::list<int> cellIndexList;

	unsigned rowCount = container->getNumberOfRows();
	unsigned columnCount = container->getNumberOfColumns();

	unsigned rowNumberIndex = cellIndex / columnCount;
	unsigned columnNumberIndex = cellIndex % columnCount;

	for (int i = 0; i < rowCount; ++i) {
		for (int k = 0; k < columnCount; ++k) {
			if(std::abs(i-(int)rowNumberIndex) + std::abs(k-(int)columnNumberIndex) <= radius) {
				if(!(i == rowNumberIndex && k == columnNumberIndex))
					cellIndexList.push_back(i*columnCount+k);
			}
		}
	}

	return cellIndexList;
}

void Cell::pushValue(unsigned variableIndex, int value) {
	// reimplement this
}

std::vector<int> Cell::pullValue(unsigned variableIndex) {

	std::vector<int> neigborhoodCellsValuesVector;

	//cout << "Cell::pullValue(...) value of neighborhoodCellIndexList: (";
	for (std::list<int>::iterator iter = neighborhoodCellIndexList.begin(); iter != neighborhoodCellIndexList.end(); iter++) {
		//cout << *iter << ", ";
		//cout << "Cell::pullValue(...) value of container->getInputValue(*iter, variableIndex): " << container->getInputValue(*iter, variableIndex) << endl;
		neigborhoodCellsValuesVector.push_back(container->getInputValue(*iter, variableIndex));
	}
	//cout << ")" << endl;

	return neigborhoodCellsValuesVector;
}

void Cell::setCellIndex(unsigned cellIndex) {
	neighborhoodCellIndexList = calculateNeighborhood(cellIndex, monitor->typeOfNeighborhood.neighborhoodType, monitor->typeOfNeighborhood.radius);
	this->cellIndex = cellIndex;
	this->baseIndex = cellIndex*dimension;
}

//void Cell::setVariableContainer(CellVariablesContainer<int>* container) {
void Cell::setVariableContainer(CellVariablesContainer* container) {
	this->container = container;
}

//CellVariablesContainer<int>* Cell::getVariableContainer() {
CellVariablesContainer* Cell::getVariableContainer() {
	return this->container;
}

Cell& Cell::operator [](const unsigned i) {
	if(i < 0 || i > container->getNumberOfColumns()-1 || i > container->getNumberOfRows()-1)
	{//throw std::out_of_range("The index is out of bounds");

	}
	setCellIndex(i);

	return *this;
}

void Cell::setValue(unsigned cellIndex, unsigned variableIndex, int value) {
	this->container->setStateValue(cellIndex, variableIndex, value);
}

/*void Cell::setValue(unsigned cellRow, unsigned cellColumn, unsigned variableIndex, int value) {
	this->container->setStateValue(cellRow*container->getNumberOfColumns() + cellColumn, variableIndex, value);
}*/

void Cell::setValue(unsigned cellIndex, int value) {
	this->container->setStateValue(cellIndex, value);
}

void Cell::setMonitor(CellMonitor* monitor) {
	this->monitor = monitor;
}
