(function($) {
	$.fn.tilePuzzle = function(settings) {
	var config = {
		'autoShuffle': true,
		'autoPlay': false,
		'autoPlayDelay': 1000,
		'cellsWide': 4,
		'cellsHigh': 4,
		'backgroundColor': '#333',
		'onWin': null
	};
	var isInitialised = this.data('initialised');

	this.shuffle = function() {
		console.log('shuffling');
	};


	this.makeRandomMove = function() {
		this.each(function(i, element) {
			if ($(element).data('initialised')) {
				makeRandomMove(element);
			}
		});
	};


	this.initialise = function() {
		if (settings) {
			$.extend(config, settings);
		}

		this.each(function(i, element) {
//			console.log(element.id + ':' + $(element).data('initialised'));
			if ($(element).data('initialised') !== true) {
				var background, pieces = new Array();

				$(element).data('initialised', true);
				$(element).data('config', config);
				if (!element.id) {
					element.id = uniqueElementId();
				}
				
//				console.log('initialising ' + element.id);

				background = createBackground(element, config.backgroundColor);
				createPieces(element, background, config.cellsWide, config.cellsHigh);
				$(element).data('spaceLocation', (config.cellsWide * config.cellsHigh));
				activatePieces(element);
			}
		}
	);};


	this.initialise();

/* PRIVATE FUNCTIONS */

	function activatePieces(element) {
		$('#' + element.id + '_background .puzzlePiece').bind('mousedown', function(e) {
			e.preventDefault();
			e.stopPropagation();
		});
		$('#' + element.id + '_background  .puzzlePiece').bind('click', function(e) {
			processPieceClick(e, element);
		});
	}

	function processPieceClick(e, element) {
		var puzzleWidth = $(element).data('config').cellsWide;
		var piece = $(e.currentTarget);
		var pieceNum = piece.data('pieceNum');
		var pieceLocation = piece.data('currentLocation');
		var spaceLocation = $(element).data('spaceLocation');
	

		if (spaceLocation == pieceLocation + puzzleWidth) {
			movePieceTo(element, pieceNum, spaceLocation);
		} else if (spaceLocation == pieceLocation - puzzleWidth) {
			movePieceTo(element, pieceNum, spaceLocation);
		} else if (spaceLocation == pieceLocation + 1) {
			movePieceTo(element, pieceNum, spaceLocation);
		} else if (spaceLocation == pieceLocation - 1) {
			movePieceTo(element, pieceNum, spaceLocation);
		} else {
			return;
		}
		checkForWin(element);
	}

	function checkForWin(element) {
		var puzzleWidth = $(element).data('config').cellsWide;
		var puzzleHeight = $(element).data('config').cellsHigh;

		if ($(element).data('spaceLocation') != (puzzleWidth * puzzleHeight)) {
			return false;
		}
		for(var i = 1; i <= (puzzleWidth * puzzleHeight) - 1; i++) {
			var pieceId = '#'+element.id + '_puzzlePiece_' + i;
			if ($(pieceId).data('currentLocation') != i) {
				return false;
			}
		}
			if (($(element).data('config')).onWin) {
				($(element).data('config')).onWin(element);
			}
	}

	function alertWin(element) {
		alert('winner! ' + element.id);
	}

	function movePieceTo(element, pieceNum, newLocation) {
//		console.log('movePieceTo(%o, %d, %d)', element, pieceNum, newLocation);
		var thePuzzle = $(element);
		var puzzleCellsWide = thePuzzle.data('config').cellsWide;
		var puzzleCellsHigh = thePuzzle.data('config').cellsHigh;

		var pieceId = '#'+element.id + '_puzzlePiece_' + pieceNum;
		var thePiece = $(pieceId);

		/* FIXME */
		// shouldn't have to calculate this at every move
		var cellWidth = thePuzzle.width()/puzzleCellsWide;
		var cellHeight = (thePuzzle.height())/puzzleCellsHigh;
		
		var newTop = (Math.floor( (newLocation-1) / puzzleCellsWide)) * cellHeight;
		var newLeft = ((newLocation - 1)% puzzleCellsWide) * cellWidth;
			
		thePiece.animate({top: newTop});
		thePiece.animate({left: newLeft});
		thePuzzle.data('spaceLocation', thePiece.data('currentLocation'));
		thePiece.data('currentLocation', newLocation);
	}

	function makeRandomMove(element) {
		var puzzleWidth = $(element).data('config').cellsWide;
		var puzzleHeight = $(element).data('config').cellsHigh;

		var availablePositions = new Array();
		var puzzlePieces = 	$('#' + element.id + '_background  .puzzlePiece');
		var spaceLocation = $(element).data('spaceLocation');
//		console.log(spaceLocation);

		if ((spaceLocation % puzzleWidth) > 0) {
			availablePositions.push(spaceLocation+1);
		}
		
		if (spaceLocation % puzzleWidth != 1) {
			availablePositions.push(spaceLocation-1);
		}
		
		if (spaceLocation <= (puzzleWidth*(puzzleHeight-1))) {
			availablePositions.push(spaceLocation+puzzleWidth);
		}
		
		if (spaceLocation > puzzleWidth) {
			availablePositions.push(spaceLocation-puzzleWidth);
		}
		
//		console.log(availablePositions);
		var randomNewPosition = availablePositions[Math.floor(Math.random()*availablePositions.length)];
//		console.log('randomNewPosition:' + randomNewPosition);
	

		// find piece in position randomNewPosition
		for(var j = 0; j < puzzlePieces.length ; j++) {
			if ($(puzzlePieces[j]).data('currentLocation') == randomNewPosition) {
				var pieceToMove = $(puzzlePieces[j]).data('pieceNum');
				movePieceTo(element, pieceToMove, spaceLocation);
				break;
			}
		}
	}


	function createBackground(element, bgColor) {
//		console.log('createBackground %s (%s)', element, bgColor);
		var background = $("<div></div>");
		$(background).css('background-color', bgColor);
		$(background).css('position', 'absolute');
		$(background).css('width', $(element).width());
		$(background).css('height', $(element).height());
//		$(background).css('top', $(element).offset().top);
//		$(background).css('left', $(element).offset().left);
		$(background).css('top', 0);
		$(background).css('left', 0);
		$(background).attr('id', element.id + '_background')
		
		$('#container').append(background);
		
		return background;
	}


	function createPieces(element, background, cellsWide, cellsHigh) {
		var width = $(element).width();
		var height = $(element).height();
		var pieceWidth = width/cellsWide;
		var pieceHeight = height/cellsHigh;
		
		for (var i = 1; i <= ((cellsWide * cellsHigh) - 1); i++) {
			createPiece(element, background, i, pieceWidth, pieceHeight, cellsWide, cellsHigh);
		}
	}

	function createPiece(element, background, i, width, height, cellsWide, cellsHigh) {
		var piece = $("<div></div>");
		$(piece).attr('alt', i);
		$(piece).attr('id', element.id + '_puzzlePiece_' + i);
		$(piece).attr('class', 'puzzlePiece');
		$(piece).css('width', width-1);
		$(piece).css('height', height-1);
		$(piece).css('position', 'absolute');
		$(piece).css('border', '1px #333 solid');
		$(piece).data('puzzleId', element.id);
		$(piece).data('pieceNum', i);
		$(piece).data('currentLocation', i);
		
		var offsetHeight = (Math.floor((i-1)/cellsWide) * height);
		var offsetWidth = (Math.floor((i-1)%cellsWide) * width);
		
		$(piece).css('top', offsetHeight);
		$(piece).css('left', offsetWidth);

		$(piece).css('background-image', 'url(' + $(element).attr('src') + ')');
		$(piece).css('background-position', '-' + (offsetWidth+1) + 'px  -' + (offsetHeight+1) + 'px');
		

		$(background).append(piece);	
	}
	
	function uniqueElementId() {
		var timePart = (new Date()).getTime();
		var numberPart = (((1+Math.random())*0x10000)|0).toString(16).substring(1);
		return('puzzle_' + timePart + '_' + numberPart);
	}

	return this;
};

})(jQuery);
