$.fn.pairs = (function() {
	
	var total, moves, timer, items;
	
	var getActive = function() {
		return root.find('li.active');
	};
	
	var getRevealed = function() {
		return root.find('li.revealed');
	};
	
	var reset = function() {
		items.removeClass('revealed');
		shuffle();
		moves = 0;

		if (total % 2 == 1) {
			throw('odd number of elements');
		}
		
		root.trigger('pairs:start', moves);
		timer = null;
	};
	
	var shuffle = function() {
		items.remove();
		indices = getShuffeled(items.length);
		$.each(indices,function(j,k) { root.append(items.eq(k)); });
	}
	
	var index = function(item) {
		return item.index() % (total / 2);
	};
	
	var getShuffeled = function(numof) {
		var shuffled = [];
		for(var i = 0; i < numof; ++i) {
			var ix = parseInt(Math.random() * (i + 1));
			shuffled.splice(ix, 0, i)
		}
		return shuffled;
	}
	
	var pairs = function(options) {
		options = options || {};
		var clone = ('clone' in options) ? options.clone : true;
		
		var $this = $(this);
		if ($this.length > 1) {
			throw('this version of pairs only works with one set');
		} else if ($this.length == 0) {
			return $this;
		}

		root = $this;
		
		if (clone) {
			var rootContent = root.html();
			root.html(rootContent + rootContent);
		}
		
		items = root.find('li');
		total = items.length;
		
		root.trigger('pairs:index');
		items.each(function() {
			$(this).attr('item', index($(this)));
		});

		reset();
		
		items.live('click', function() {
		
			if(!timer) {
				timer = new Date().getTime();
			}
		
			var active   = getActive();
			var revealed = getRevealed();
		
			if (revealed.length == total) {
				reset();
				return;
			}

			if (active.length >= 2) {
				items.removeClass('active');
			}
		
			if ($(this).is('.active') || $(this).is('revealed')) {
				return;
			}
		
			items.removeClass('current');
			$(this).addClass('active').addClass('current');
			root.trigger('pairs:active', {item: $(this)});
			
			if (active.length == 1) {
				moves++;
				active = getActive();
				if (active.eq(0).attr('item') == active.eq(1).attr('item')) {
					root.trigger('pairs:equal');
					active.removeClass('active').addClass('revealed').removeClass('current');
					if (getRevealed().length == total) {
						timediff = new Date().getTime() - timer;
						root.trigger('pairs:end', {moves: moves, time: timediff})
					}
				} else {
					root.trigger('pairs:odd');
				}
			}
	
		});
		
		root.addClass('running-pairs');
		
		return $this;
	};
	
	return pairs;
	
})();
