/*
 * jQuery Image Preloader Plugin
 * http://inlight/com.au
 * Copyright (c) 2010 Inlight Media
 * Version: 1.0 (09/04/2010)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * Requires: jQuery v1.2.3 or later
 */

/**
 * TODO
 * - Add support to handle failed images (ie, 404)
 * - Add non persistent next and prev methods for pool (ie, examine what the next will be and what the prev was)
 */

;(function($) {

var ver = '1.0';
var loaded = [];
var loading_index = -1;
var concurrently_loading = 0;

$.fn.imagepreloader = function(options) {
	var urls = this;

	// NOT COOL - NEEDS TO MAKE A DIFFERENT INSTANCE OF THIS PLUGIN IN FUTURE
	loaded = [];
	loading_index = -1;
	concurrently_loading = 0;

	// init options
	options = options || {};
	var opts = $.extend({}, $.fn.imagepreloader.defaults, options);

	opts.before_all = opts.before_all ? [opts.before_all] : [];
	opts.before_each = opts.before_each ? [opts.before_each] : [];
	opts.after_all = opts.after_all ? [opts.after_all] : [];
	opts.after_each = opts.after_each ? [opts.after_each] : [];
	opts.after_first = opts.after_first ? [opts.after_first] : [];

	// run before all hook
	if (opts.before_all.length)
		opts.before_all[0].apply(urls, [urls, urls, opts, true]);

	// init progress bar
	if (opts.progressbar)
		$.fn.imagepreloader.progress.init(urls.length);

	// init container
	$cont = constructContainer(opts.container);

	// start loading images
	preloadImages(urls, $cont, opts);

	return this;
};

// creates the container to hold all the loaded images
function constructContainer(container) {
	if($('#'+container).length == 0) {
		debug('contructing container with id: #'+container);
		$cont = $('<div id="'+container+'"></div>');
		$cont.hide();
		$('body').append($cont);
		return $cont;
	} else {
		debug('using existing container with id: #'+container);
		return $('#'+container);
	}
}

// creates the container to hold all the loaded images
function preloadImages(urls, $cont, opts) {
	// Check if we still have more designs to load.
	if (loading_index + 1 < urls.length) {
		loading_index++;

		// construct image
		constructImage(urls[loading_index], urls, $cont, opts);

		// update counter
		concurrently_loading++;

		// rinse and repeat
		if (concurrently_loading < opts.max_concurrent) {
			preloadImages(urls, $cont, opts);
		}
	}
}

function constructImage(url, urls, $cont, opts) {
	debug('preloading image for url: '+url);

	// fire hooks
	if (opts.before_each.length)
		opts.before_each[0].apply(url, [urls, urls, opts, true]);

	// init image
	var img = new Image();

	// attach an event handler to fire when the image has finished loading.
	img.onload = function(event) {
		var $loaded = $(this);
		var loaded_url = $loaded.attr('src');

		debug(loaded_url + " image finished loading.");

		// update counts
		concurrently_loading--;
		loaded.push($loaded);

		// update progess bar
		if (opts.progressbar)
			$.fn.imagepreloader.progress.update(loaded.length, urls.length);

		// fire hooks
		if (opts.after_each.length)
			opts.after_each[0].apply(loaded_url, [urls, urls, opts, true]);

		// if its the first url to load fire hook
		if (opts.after_first.length && loaded.length == 1)
			opts.after_first[0].apply(loaded_url, [urls, urls, opts, true]);

		// if its the last url to load fire hook
		if (opts.after_all.length && loaded.length == urls.length)
			opts.after_all[0].apply(urls, [urls, urls, opts, true]);

		// rinse and repeat
		if (concurrently_loading < opts.max_concurrent) {
			preloadImages(urls, $cont, opts);
		}
	};

	// trigger the image loading proccess
	img.src = url;

	// append to preloader container
	$cont.append(img);

	return img;
};

// PROGRESS BAR -----------------------------------------------------------------------
// this can be overwritten to customise the look and feel :)
$.fn.imagepreloader.progress = {
	$element: $('<div id="imagepreloader-progress" style="position:absolute; top: 0; left: 0; z-index:1000; color: white;">0</div>'),

	init: function(total) {
		if($('#imagepreloader-progress').length == 0)
			$('body').prepend($.fn.imagepreloader.progress.$element);
		$.fn.imagepreloader.progress.update(0, total);
	},
	update: function(count, total) {
		$.fn.imagepreloader.progress.$element.html('loaded ' + count + ' of ' + total);
	}
};

// LOADED IMAGE POOL ------------------------------------------------------------------
// allows you to access the pool of loaded images
$.fn.imagepreloader.pool = {
	// private
	index: -1,

	// retrieve the next full loaded image
	next: function() {
		var next = $.fn.imagepreloader.pool.index + 1;
		if(loaded.length >= next) { // look for next loaded
			$.fn.imagepreloader.pool.index++;
			return loaded[next];
		} else {
			// none have loaded yet
			return null;
		}
	},

	current: function() {
		var current = $.fn.imagepreloader.pool.index;
		if(current >= 0) {
			return loaded[current];
		} else {
			return null;
		}
	},

	// retrieve the previously shown full loaded image
	prev: function() {
		var prev = $.fn.imagepreloader.pool.index - 1;
		if(prev >= 0) {
			$.fn.imagepreloader.pool.index--;
			return loaded[prev];
		} else {
			return null;
		}
	},

	// returns to the first image in the pool.
	first: function() {
		$.fn.imagepreloader.pool.index = 0;
		return loaded[0];
	}
};

// UTILS ------------------------------------------------------------------
function debug() {
	try {
		if (window.console && window.console.log) {
			console.log.apply(window.console, arguments)
		}
	}
	catch (err) {}
}

// DEFAULTS ------------------------------------------------------------------
$.fn.imagepreloader.defaults = {
	before_all: null, // run before attempting to load any images
	before_each: null, // run before loading each image
	after_all: null, // run after all images are loaded
	after_each: null, // run after loading each image
	after_first: null, // run after loading each image
	max_concurrent: 5, // false = infinite
	container: 'imagepreloader',
	progressbar: false
};

})(jQuery);