// implement basic life rules
#ifndef BASIC_LIFE_H
#define BASIC_LIFE_H

#include <vector>
#include "Life.h"

class BasicLife : public LifeSimulator
	{
public:
	__int64 CountPopulation(void)
		{
		population_ = 0;
		for (int j = 0; j < height_; ++j)
			for (int i = 0; i < width_; ++i)
				if (0 != GetValue(i,j))
					++population_;
		return population_;
		}

	__int64 GetGeneration(void)
		{
		return generation_;
		}
	void Reset(void)
		{
		population_ = 0;
		generation_ = 0;
		for (int pos = 0; pos < width_*height_; ++pos)
			data_[pos] = 0;
		}

	// get value at cell (i,j)
	unsigned char GetValue(int i, int j) const
		{
		return data_[i+j*width_];
		}
	void SetValue(int i, int j, unsigned char value)
		{
		data_[i+j*width_] = value;
		}
	void NextGeneration(void)
		{
		++generation_;
		int w = width_;
		int h = height_;
		std::vector<unsigned char> temp(width_*height_);
		for (int y = 0; y < height_; ++y)
			for (int x = 0; x < width_; ++x)
				{
				int neighbors = 0;
				for (int j = -1; j <= 1; ++j)
					for (int i = -1; i <= 1; ++i)
						{
						if ((i==0) && (j==0))
							continue; // don't count self
						int xp = x + i;
						int yp = y + j;
						bool needsWrap = (xp < 0) || (xp >= w) || (yp < 0) || (yp >= h);
						if ((true == needsWrap) && (false == wraps_))
							continue; // out of bounds
						if (true == needsWrap)
							{ // wrap 
							xp = (xp + w) % w;
							yp = (yp + h) % h;
							}
						if (GetValue(xp,yp) == 1)
							++neighbors;
						}
				/* rules
				< 2 neighbors -> dies
				> 3 -> dies
				=3 -> comes to life
				*/
				bool alive = GetValue(x,y) != 0;
				if (neighbors < 2) 
					{
					if (alive) 
						--population_;
					alive = false;
					}
				else if (3 == neighbors)
					{
					if (false == alive) 
						++population_;
					alive = true;
					}
				else if (3 < neighbors)
					{
					if (alive) 
						--population_;
					alive = false;
					}

				if (true == alive)
					temp[x+y*w] = 1;
				else
					temp[x+y*w] = 0;
				}
		swap(data_,temp);
		} // NextGeneration

	__int64 GetPopulation(void) const
		{
		return population_;
		}
	void SetPopulation(__int64 pop)
		{
		population_ = pop;
		}

	void SetSize(int width, int height)
		{
		width_  = width;
		height_ = height;
		data_.resize(width_*height_);
		}
	void GetSize(int & width, int & height) const
		{
		width = width_;
		height = height_;
		}
	bool GetWraparound(void) const
		{
		return wraps_;
		}
	void SetWraparound(bool wrap)
		{
		wraps_ = wrap;
		}

	// takes RGB buffer, stride, image width, height, and color to draw r,g,b
	// and viewport
	void DrawBuffer(Viewport & vp, unsigned char * buffer, int stride, int width, int height,
		unsigned char r, unsigned char g, unsigned char b)
		{
		int numerator, denominator;
		vp.GetZoom(numerator, denominator);
		int cellWidth = 1<<denominator; // width of a cell in pixels
		int cellSkip  = 1<<numerator;   // cells wide per pixel

		// x,y goes over cell coords, x1,y1 to x2,y2
		int x1, y1, x2, y2;
		vp.GetViewportWorldCoords(width,height,x1,y1,x2,y2);

		// scan over dest pixels
		for (int j = 0; j+cellWidth < height; j += cellWidth)
			{
			unsigned char * ptr = buffer + j*stride;
			for (int i = 0; i+cellWidth < width; i += cellWidth)
				{
				bool alive = false;

				// walk relevant cells to see if any alive
				for (int y = 0; y < cellSkip; ++y)
					for (int x = 0; x < cellSkip; ++x)
						{
						int xpos = x1 + x + i * cellSkip / cellWidth;
						int ypos = y1 + y + j * cellSkip / cellWidth;
						if ((0 <= xpos) && (xpos < width_) && (0 <= ypos) && (ypos < height_))
							alive |= (GetValue(xpos,ypos) != 0);
						}

					// draw cell if alive
					if (false == alive)
						ptr += cellWidth*3;
					else
						{
						for (int y = 0; y < cellWidth; ++y)
							{
							for (int x = 0; x < cellWidth; ++x)
								{
								*ptr++ = r;
								*ptr++ = g;
								*ptr++ = b;
								}
							ptr = ptr + stride - cellWidth*3;
							}
						ptr = ptr - cellWidth*stride + cellWidth*3;
						}
				}
			}
		} // DrawBuffer


	BasicLife(void) : 
			width_(256), height_(256), 
			wraps_(true), population_(0),
			generation_(0)
		{
		data_.resize(width_*height_);
		}

private:
	std::vector<unsigned char> data_; // data 0 = blank, 1 = alive
	int width_, height_;         // size of board
	bool wraps_;                 // do we wrap around?
	__int64 population_;
	__int64 generation_;
	}; // BasicLife

#endif // BASIC_LIFE_H
// end - BasicLife.h