var colWidth = 184;
var margin = 12;
var minLineHeight = 12;
var stretchToFitLine = true;

var newColNum = undefined;
var colNum;
var colSpan;
var currCol;
var currY;

$(function() {
	// DOM is ready
	
	// Set position of div#content to relative, so it is used as reference for the items' absolute positions
	$('#content').css('position', 'relative');
	
	checkLayout();
});

$(window).resize(checkLayout);

function checkLayout() {
	// calculate how many columns fit in
	newColNum = parseInt(($('#content').innerWidth() + margin) / (colWidth + margin));
	if(newColNum != colNum) {
		colNum = newColNum;
		layout();
	}
}

function layout() {	
	// initialize variables
	currCol = 0; // number of the current column
	currY = 0; // Y coordinate of the current line's items
	itemsOfEqualHeight = new Array(); // array with references to all items of the current line
	currLineHeight = 0; // height of the current line
	pushes = new Array();
		
	// reset heights to minimum, so the layout is not affected by previous layouts
	$('.short').height('auto');
	
	// go through all items and position them
	$('.short').each(function(i) {
		// number of columns this short fills
		colSpan = Math.ceil($(this).outerWidth(false) / (colWidth + margin));
		
		// height of this short
		thisHeight = $(this).outerHeight(true);
		
		// do we need to push this short further to leave room for pushes from above?
		findPlace();
		
		// if this item does not fit anymore: start new line
		if(currCol + colSpan > colNum) newLine();
		
		if($(this).hasClass('rowspan')) {
			// create an entry in the pushes array
			pushes[currCol] = new Array();
			pushes[currCol].colSpan = colSpan;
			pushes[currCol].remainingHeight = thisHeight;
			pushes[currCol].height = thisHeight;
			pushes[currCol].ref = $(this);
		} else {
			// update the current line's height, if necessary
			if(thisHeight > currLineHeight) currLineHeight = thisHeight;
			// add this item to the array
			itemsOfEqualHeight.push($(this));
		}
		
		// position item
		$(this).css({'position': 'absolute',
			'left': currCol * (colWidth + margin),
			'top': currY
		});
		
		// update currCol
		currCol += colSpan;
	});
	
	// finish last (possibly incomplete) line
	newLine();
	
	// push currY until the last rowspan bottom edge
	var maxRemainingHeight = 0;
	for(var j in pushes) {
		if(pushes[j]) {
			maxRemainingHeight = Math.max(pushes[j].remainingHeight, maxRemainingHeight);
		}
	}
	currY += maxRemainingHeight;
	
	// 
	$('#content').height(currY);
}

function newLine() {	
	// equalize height of all items in the finished line
	if(stretchToFitLine == true) {
		for(j in itemsOfEqualHeight) {
			thisShort = itemsOfEqualHeight[j];
			thisShortHeight = currLineHeight - (thisShort.outerHeight(true) - thisShort.height());
			itemsOfEqualHeight[j].height(thisShortHeight);
		}
	}
	
	// substract the height of this line from the pushes
	for(var j = 0; j < colNum; j++) {
		if(pushes[j]) {
			pushes[j].remainingHeight -= currLineHeight;
			// if the rowspan fits in this line, it is finished and can be deleted
			if(pushes[j].remainingHeight <= 0) {
				// if stretchToFitLine, pull the bottom border of all the items in this line to the same level
				if(stretchToFitLine == true) {
					pushes[j].ref.height(pushes[j].height - pushes[j].remainingHeight - (pushes[j].ref.outerHeight(true) - pushes[j].ref.height()));
				}
				pushes[j] = undefined;
			}
		}
	}
	
	// update the y coordinate for this line
	currY += currLineHeight;
	
	// reset vars
	currCol = 0;
	itemsOfEqualHeight = new Array();
	currLineHeight = minLineHeight;
	
	findPlace();
}

function findPlace() {
	// do we need to push this item further to leave room for pushes from above?
	// Push currCol until this item fits in
	do {
		pushNeeded = false;
		for(var j = 0; j < colSpan; j++) {
			if(pushes[currCol + j]) {
				pushNeeded = true;
				currCol += j + pushes[currCol + j].colSpan;
			}
			if(currCol + j > colNum - 1) newLine();
		}
	} while (pushNeeded == true);
}

