(function($) {
	/**
   * Name: bgMosaic
   * Author: Henrik Almér for AGoodId
   * Version: Alpha 1
   * Size: 5 KB (minified 2 KB)
   *
   * This plugin may be applied to any element and assumes that all
   * first level children to that element are columns which should be
   * filled with images.
   *
   * Using a provided JSON object it loads images into columns and
   * balances them by height.
   *
   * Dependencies: jQuery
   *
   * @param settings: JavaScript object of settings
   *
   * settings.imgJSON should be on the following format:
   *	{"images" : [	{"src" : "/url/to/image1", "srcW" : "321", "srcH" : "654", "destW" : "123", "destH" : "456", "alt" : "Description"},
   *								{"src" : "/url/to/image2", "srcW" : "321", "srcH" : "654", "destW" : "123", "destH" : "456", "alt" : "Description"},
   *								etc.	]}
   */
  
  var config = {
  	'imgJSON'				:	{"images" : []},
  	'interval'			:	100,
  	'fadeSpeed'			:	600,
  	'onComplete'    : false
  };
  
  var $this,
  		data,
  		cols,
  		curCol,
  		shortestCol = {"ix" : 0, "height" : 0},
  		images,
  		dim,
  		jImage,
  		$image,
  		$html,
  		timer,
  		cTimer = 0;
  
  var methods = {
		init	:	function(settings) {		
			// Initiate each matched container
			return this.each(function() {
				$this = $(this);
				data = $this.data('bgMosaic');
			
				// If the plugin hasn't been initialized yet
				if (!data) {
					if (settings) $.extend(config, settings);
				
					$this.data('bgMosaic', {
						target : $this,
						settings : config
					});
					
					data = $this.data('bgMosaic');
				}

				cols = $this.children();
				images = data.settings.imgJSON.images;
				
				// init timer and stores it's identifier so it can be unset later				
				timer = window.setInterval(function() { $this.bgMosaic('addImage') }, data.settings.interval);
			});
		},
		
		destroy	:	function() {
			return this.each(function() {
				$(window).unbind('.bgMosaic');
				data.bgMosaic.remove();
				$this.removeData('bgMosaic');
			});
		},
		
		addImage : function() {
			jImage = images[cTimer];
			dim = $this.bgMosaic('calculateProportions', jImage.srcW, jImage.srcH, jImage.destW, jImage.destH);
				
			$image = $('<img width="' + dim.width + '" height="' + dim.height + '" alt="' + jImage.alt + '" id="image' + cTimer + '" />').attr('src', jImage.src);
			
			// If an URL is set, wrap the image in a link
			if (jImage.url) {
				$html = $('<a href="' + jImage.url + '" class="' + jImage.cssClass + '" title="' + jImage.title + '" rel="bookmark"></a>');
				$html.html($image);
			} else {
				$html = $image;
			}
			
			$image.css({"opacity" : 0});

			if (cTimer < cols.length) {
				curCol = $(cols[cTimer]);
					
				curCol.append($html);
			} else {
				// Find the shortest column
				shortestCol.height = 0;
					
				$.each(cols, function(ix, val) {
					if ($(val).height() < shortestCol.height || shortestCol.height == 0) {
						shortestCol.ix = ix;
						shortestCol.height = $(val).height();
					}
				});
				
				curCol = $(cols[shortestCol.ix]);
				curCol.append($html);
				
				// Call onComplete callback, if defined
				if ($.isFunction(data.settings.onComplete)) {            
          data.settings.onComplete.call(this);
        }
			}
			
			$image.animate({"opacity" : 1}, data.settings.fadeSpeed);

			cTimer++;
			
			// remove timer after interating through all articles
			if (cTimer >= images.length) {
				clearInterval(timer);
			}
		},
		
		calculateProportions	:	function(srcW, srcH, destW, destH) {
			destW = (typeof(destW) == 'undefined' || destW == 0) ? srcW: destW;
			destH = (typeof(destH) == 'undefined' || destH == 0) ? srcH: destH;
			
			if (typeof(srcW) == 'undefined' || typeof(srcH) == 'undefined') {
				return false;
			}
			
			// Calculate ratios
			var srcRatio = srcW/srcH,
					destRatio = destW/destH;

			if (destW != srcW && destH == srcH) {
				/**
				 * Image width has been supplied as an argument,
				 * but height hasn't => Calculate height
				 */

				destH = Math.ceil((1/srcRatio) * destW);
			} else if (destH != srcH && destW == srcW) {
				// Calculate width
				
				destW = Math.ceil(srcRatio * destH);
			}
			
			/**
			 * Max width and ratio are established, check ratio and determine
			 * what proportions to use
			 */

			if ( srcRatio >= destRatio ) {
				// Original is wider => Width rules
				destH = Math.ceil((1/srcRatio) * destW); 
			} else {
				// Original is narrower => Height rules
				destW = Math.ceil(srcRatio * destH);
			}
			
			return {'width' : destW, 'height' : destH};
		}
	};

  $.fn.bgMosaic = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		} else if (typeof method === 'object' || !method) {
			return methods.init.apply( this, arguments );
		} else {
			$.error('Method ' +  method + ' does not exist on jQuery.bgMosaic');
		}
    
    return this;
	}
})(jQuery);
