(function($){
    $.gol = function(el, options, oldarguments){
       	var base = this;

       	// Access to jQuery and DOM versions of element
       	base.$el = $(el);
       	base.el = el;
		base.canvas = el;

       	// Add a reverse reference to the DOM object
       	base.$el.data("gol", base);

		base.$el.click(function(e) {
			var offsetX = $(this).offset().left;
			var offsetY = $(this).offset().top;
			var x = Math.round(e.pageX - offsetX);
			var y = Math.round(e.pageY - offsetY);
			base.click(x, y);
		});

		map = {};

        base.init = function(){
            base.options = $.extend({},$.gol.defaultOptions, options);
	
			map.width = Math.floor(base.canvas.width / (base.options.cellWidth + base.options.cellPadding) + base.options.cellPadding);
			map.height = Math.floor(base.canvas.height / (base.options.cellWidth + base.options.cellPadding) + base.options.cellPadding);

			if (!base.canvas.getContext) return;
			ctx = base.canvas.getContext("2d");

			gameoflife.reset(map);
			
			var sweetShape = [[-3,0],[-2,0],[-1,0],[0,0],[0,1],[0,-2],[3,0],[2,0],[1,0],[-3,-1],[3,-1],[-2,-2],[2,-2],[0,0]];

			var centerX = Math.floor(map.width / 2);
			var centerY = Math.floor(map.height / 2);

			for (i = 0; i < sweetShape.length; i++) {
				try {
					map.map[centerX + sweetShape[i][0]][centerY + sweetShape[i][1]] = 1
				} catch(err){/* don't care */}
			}

			base.draw(base.canvas, map);
			base.pause(base.options.paused);
        };

		base.draw = function(canvas, map){
			canvas.width = canvas.width;//clear canvas

			for (x = 0; x < map.width; x++) {
				for (y = 0; y < map.height; y++) {
					if (map.map[x][y] == 1) {
						ctx.fillStyle = base.options.colors.cell;
					} else {
						ctx.fillStyle = base.options.colors.background;
					}
					base.drawCell(x, y);
				}
			}
		};

		base.drawCell = function(x, y) {
			ctx.beginPath();
			ctx.arc(x * base.options.cellWidth + x * base.options.cellPadding + (base.options.cellWidth / 2), y * base.options.cellWidth + y * base.options.cellPadding + (base.options.cellWidth / 2),  base.options.cellWidth / 2, 0, Math.PI*2, true);
			ctx.closePath();
			ctx.fill();
		}

		base.click = function(screenX, screenY) {
			var x = Math.floor(screenX / (base.options.cellWidth + base.options.cellPadding));
			var y = Math.floor(screenY / (base.options.cellWidth + base.options.cellPadding));
			if (map.map[x][y] == 0) {
				map.map[x][y] = 1;
				ctx.fillStyle = base.options.colors.cell;
			} else {
				map.map[x][y] = 0;
				ctx.fillStyle = base.options.colors.background;
			}
			base.drawCell(x, y);
		}

		base.step = function() {
			gameoflife.step(map);
			base.draw(base.canvas, map);
		}

		base.clear = function() {
			gameoflife.reset(map);
			base.draw(base.canvas, map);
		}
		
		base.pause = function(newPause) {
			if (newPause === false) {
				base.interval = setInterval (function(){base.step();}, base.options.loopInterval);
			} else {
				clearInterval(base.interval);
			}
			base.options.paused = newPause;
		}

        base.init();
    };

    $.gol.defaultOptions = {
		loopInterval: 1000,
		paused: false,
		wrap: true,
		colors: {
			background: '#fff',
			cell: '#000'
		},
		cellWidth: 8,
		cellPadding: 1
    };

    $.fn.gol = function(options){
		base = $(this).data('gol');
		if (base != undefined) {
			if (typeof(options) == "string") {
				if (options === 'pause') {
					if (arguments[1] != undefined) {
						base.pause(arguments[1]);
					}
					return base.options.paused;
				} else if (options === 'clear') {
					base.clear();
				}
				return;
			}
		} else {
			return this.each(function(){
				(new $.gol(this, options));
			});
		}
    };
})(jQuery);

var gameoflife = {
	neighbourLocations: [[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]],
	step: function(map) {
		gameoflife.forEachCell(map, function(){
			n = gameoflife.countNeighbours(map, x, y);
			if (map.map[x][y] == 0) {
				if (n == 3) {
					map.nextmap[x][y] = 1;
				} else {
					map.nextmap[x][y] = 0;
				}
			} else {
				if (n < 2) {
					map.nextmap[x][y] = 0;
				} else if (n > 3) {
					map.nextmap[x][y] = 0;
				} else {
					map.nextmap[x][y] = 1;
				}
			}
		});
		gameoflife.forEachCell(map, function(){
			map.map[x][y] = map.nextmap[x][y];
		});
		return map;
	},
	countNeighbours: function (map, x, y, wrap) {
		var n = 0;
		for (i = 0; i < gameoflife.neighbourLocations.length; i++) {
			try {
				var curX = x + gameoflife.neighbourLocations[i][0];
				var curY = y + gameoflife.neighbourLocations[i][1];
				if (wrap) {
					if (curX < 0) {
						curX = map.width;
					} else if (curX > map.width) {
						curX = 0;
					}
					if (curY < 0) {
						curY = map.height;
					} else if (curY > map.height) {
						curY = 0;
					}
				}
				if (map.map[curX][curY] == 1) {
					n++;
				}
			} catch(err){/* don't care */}
		}
		return n;
	},
	reset: function (map) {
		map.map = [];
		map.nextmap = [];
		for (x = 0; x < map.width; x++) {
			map.map[x] = [];
			map.nextmap[x] = [];
			for (y = 0; y < map.height; y++) {
				map.map[x][y] = 0;
				map.nextmap[x][y] = 0;
			}
		}
		return map;
	},
	forEachCell: function(map, action) {
		for (x = 0; x < map.width; x++) {
			for (y = 0; y < map.height; y++) {
				action(map, x, y)
			}
		}
	}
}

