/**
 * 
 */


function LayoutElement(element){
	
	// Ein Jquery Objekt
	this.element = element;
	
	this.isBig = false;
	this.isPlaced = false;
	this.width = 0;
	this.height = 0;
	this.column = 0;
	this.originalHeight = element.outerHeight(true);
	
	this.offset = 	{
						horizontal : 0,
						vertical: 0
					};
	
	this.getHeight = function(){
		return element.outerHeight(true);
	};
	
	this.getWidth = function(){
		return element.outerWidth(true);
	};
}

function Position(){
	
	this.offset = {
			horizontal: 0,
			vertical: 0
	};
}

var layoutManager = {
		
		parentWidth: 0,
		parentId: false,
		columnsPerRow: 4,
		itemClass : false,
		childWidth: 0, // Die normale Breite eines Elements
		item: Array,
		resizableClass : false,		
		verticalOffsets : Array,
		
		create : function(config){
			
			// Beinhaltet die Id des Parents, was als Elternelement dienen soll
			parentId = 		config.parentId || false;
			// Klasse der Child Elemente
			itemClass = 	config.itemClass || false;
			// Klasse des Elements, deren Höhe skaliert werden darf
			// Es muss ein Kindelement, des Parentcontainers sein
			resizableClass = config.resizableClass || false;
			
			resize = 	config.resize || false;
			
			center = 	config.center || false;
			
			if(!itemClass || !resizableClass)
				return false;
			
			// Wenn parent leer, dann nehme das gesamte HTML Dokument als parent
			if(!parentId){
				this.parentWidth = $j(window).width();
			}else{
				this.parentWidth = $j(parentId).width();
			}

			// Berechne die Breite des Child Elements
			// Suche die geringste Breite bei den Elementen
			
			var elementWidths=new Array();
			this.item = new Array();
			
			$j(itemClass).each(function(){
				elementWidths.push($j(this).outerWidth(true));
				if(!($j(this).css('display') == 'none')){				
					layoutManager.item.push(new LayoutElement($j(this)));
				}
				
			});

			this.childWidth = Math.min.apply(Math, elementWidths);
			
			// Lese Windowbreite
			if(center){
				containerWidth = $j(window).width();
			}else{
				containerWidth = $j(parentId).width();
			}
			var newColumnsPerRow = Math.floor(containerWidth / this.childWidth);
			
			// Berechne wieviele Elemente es aufnimmt
			if(newColumnsPerRow > this.columnsPerRow)
			{
				this.columnsPerRow = newColumnsPerRow;
			}
			
			// Init vertical offsets
			this.verticalOffsets = new Array(this.columnsPerRow);
			
			// Wenn die Elemente weniger als die erste Zeile sind, zentrieren
			if(layoutManager.item.length < this.columnsPerRow){
				this.parentWidth = $j(parentId).width(layoutManager.item.length * this.childWidth);
			}else{
				// Setze Breite auf genau die Elemente, die aufgenommen werden können
				this.parentWidth = $j(parentId).width(this.columnsPerRow * this.childWidth);
			}
			
			return true;
			
		},
		
		order:function(config){
			

			if(!this.create(config))
				return false;
			
			this.orderWithPositioning();
			
			return true;
		},
		
		isBigElement: function(element){
			
			if(element.outerWidth(true) > this.childWidth){
				return true;
			}else{
				return false;
			}
				
		},
		
		isBreakingContainer: function(layoutElement){
			
			var columnIndex = layoutElement.column;
			
			if(layoutElement.isBig)
				columnIndex++;

			if(columnIndex > (this.columnsPerRow - 1)){
				return true;
			}else{
				return false;
			}
		},
		
		changePositionOfElement: function(index){
			// Tausche Plätze mit einem Element, dass eine normale Breite hat 						
			var element = this.item[index];
			var indexChangingElement = -1;
			var newIndex = index;
			
			// Durchsuche alle vorherigen Elemente, um ein Element mit normaler Größe zu finden
			for(var i=index-1; i>=0; i--){
				
				if(!this.isBigElement(this.item[i].element))
				{
					// Element gefunden, Schleife abbrechen
					indexChangingElement = i;
					newIndex = i;
					break;
				}
			}
			
			// Es konnte kein Element gefunden werden, durchsuche nächste Elemente
			if(indexChangingElement == -1){
				for(var i=index; i<=this.item.length; i++){
					
					if(!this.isBigElement(this.item[i].element))
					{
						// Element gefunden, Schleife abbrechen
						indexChangingElement = i;
						newIndex = index;
						break;
					}
				}
			}
			
			// Das Element, womit getauscht werden soll
			var changeElement = this.item[indexChangingElement];
			
			// Tausche Elemente
			this.item.splice(index, 1, changeElement);
			this.item.splice(indexChangingElement, 1, element);
			
			// Gebe die neue Position des zu bearbeitenden Elementes zurück
			return newIndex;
		},
		
		positionAt: function(index, h_offset, v_offset){
			
			var parentContainer;
			
			if(parentId)
				parentContainer = $j(parentId);
			else
				parentContainer = $j(window);
			
			this.item[index].element.position({
				
				my: "left top",
				at: "left top",
				of: parentContainer,
				offset: h_offset+" "+v_offset,
				collision: 'none'
			});
		},
		
		findElement: function(index, position){
			
			var elementIndex = 0;
			var maxColumnIndex = 0;
			
			if(position == 'top'){
				maxColumnIndex = this.columnsPerRow;
			}else if(position == 'top-right'){
				maxColumnIndex = this.columnsPerRow - 1;
			}

			// Gehe Elemente durch, solange Elementindex größer als maxColumnIndex ist
			/*while(elementIndex >= maxColumnIndex){
				// Breites Element
				if(this.isBigElement(this.item[i].element)){
					elementIndex += 2;
				}else{
				// Normales Element
					elementIndex++;
				}
			}*/
			
			var i = 0;
			
			for(i=index-1; i>=0 ; i--){

				// Breites Element
				if(this.isBigElement(this.item[i].element)){
					elementIndex=elementIndex+2;
				}else{
				// Normales Element
					elementIndex++;
				}
				
				if(elementIndex >= maxColumnIndex){
					break;
				}
			}
			
			if(i < 0 || this.item[i] == undefined)
				return false;
			else
				return this.item[i];
		},
		
		updateColumn: function(actualColumn, oldIndex, index){
			
			var i = 0;
			for(i=oldIndex; i>=index; i--){
				
				// Breites Element
				if(this.isBigElement(this.item[i].element)){
					
					if(actualColumn >= 2){						
						actualColumn = actualColumn-2;
						
					}else{
						actualColumn = 0;
					}
				}else{
				// Normales Element
					if(actualColumn >= 1){
						actualColumn--;
					}else{
						actualColumn = 0;
					}
				}
			}
			
			return actualColumn;
			
		},
		
		findPositionForElement: function(layoutElement, index, reset){
			
			calculatedPosition = new Position();
			var columnToPlace = 0;
			var offsetToPlace = 0;
			var distance = 0;
			var placed = false;
			
			// Wenn Element gross ist
			if(layoutElement.isBig){
				
				for(var column=0; column < this.verticalOffsets.length - 1; column++){
					
					var actualDistance = 0;
					var columnIsBig = false;
					
					// Offset linke Spalte
					var leftOffset = 0;
					if(this.verticalOffsets[column]){
						 leftOffset = this.verticalOffsets[column].offset.vertical + this.verticalOffsets[column].height;
						 
						 if(this.verticalOffsets[column].isBig)
							 columnIsBig = true;
					}
					// Offset rechte Spalte
					var rightOffset = 0;
					if(this.verticalOffsets[column + 1]){
						rightOffset = this.verticalOffsets[column + 1].offset.vertical + this.verticalOffsets[column + 1].height;

						if(this.verticalOffsets[column + 1].isBig)
							 columnIsBig = true;
					}
					
					if(columnIsBig && !reset){
						//console.log("Skip");	
						continue;
					}
						
					
					var actualOffset = 0;
					
					if(leftOffset > rightOffset){
						actualDistance = leftOffset - rightOffset;
						// Links ansetzen mit Höhe vom linken Element
						actualOffset = leftOffset;
					}else{
						actualDistance = rightOffset - leftOffset;
						// Links ansetzen aber mit Höhe vom rechten Element
						actualOffset = rightOffset;
					}
					
					if(actualDistance < distance || !placed){
						// Platziere in Spalte
						columnToPlace = column;	
						offsetToPlace = actualOffset;
						distance = actualDistance;
						
						placed = true;
						reset = false;
						//console.log("place at: "+columnToPlace);
					}
				}
				
				if(!placed){
					//console.log("reset");
					return this.findPositionForElement(layoutElement, index, true);
				}
				
				// Skalierung der Elemente
				if(this.verticalOffsets[columnToPlace+1]){
					var leftOffset = 
						this.verticalOffsets[columnToPlace].offset.vertical + this.verticalOffsets[columnToPlace].height;
					var rightOffset = 
						this.verticalOffsets[columnToPlace+1].offset.vertical + this.verticalOffsets[columnToPlace+1].height;
					
					if(leftOffset > rightOffset){
						var distance = (this.verticalOffsets[columnToPlace].offset.vertical + this.verticalOffsets[columnToPlace].height) - 
							(this.verticalOffsets[columnToPlace+1].offset.vertical + this.verticalOffsets[columnToPlace+1].height); 
						
						if(distance > 0){
							// Hier sollte die Distanz auf alle Elemente in der gleichen Spalte verteilt werden
							// Gehe alle kleinen Elemente in der gleichen Spalte durch
							var found = 1;
							/*
							for(var i=this.item.length -1; i>0; i--){
								if(this.item[i].isPlaced && this.item[i].column == (columnToPlace+1)){
									// Kleine Items und bereits platzierte
									if(this.item[i].isBig)
										break;
									
									found++;
								}
							}*/

							var distancePerElement = distance / found;
							/*
							for(var i=this.item.length -1; i>0; i--){
								if(this.item[i].isPlaced && this.item[i].column == (columnToPlace+1)){
									if(this.item[i].isBig)
										break;
									
									this.item[i].element.find(resizableClass).css('margin-top', Math.round(distancePerElement / 2));
									this.item[i].element.find(resizableClass).css('margin-bottom', Math.round(distancePerElement / 2));
								}
							}*/
							
							this.verticalOffsets[columnToPlace+1].element.find(resizableClass).css('margin-top', Math.round(distancePerElement / 2));
							this.verticalOffsets[columnToPlace+1].element.find(resizableClass).css('margin-bottom', Math.round(distancePerElement / 2));
						}
					}else{
						var distance = (this.verticalOffsets[columnToPlace+1].offset.vertical + this.verticalOffsets[columnToPlace+1].height) - 
						(this.verticalOffsets[columnToPlace].offset.vertical + this.verticalOffsets[columnToPlace].height); 
						if(distance > 0){
								
							// Hier sollte die Distanz auf alle Elemente in der gleichen Spalte verteilt werden
							var found = 1;
							/*
							for(var i=this.item.length -1; i>0; i--){
								if(this.item[i].isPlaced && this.item[i].column == (columnToPlace)){
									// Kleine Items und bereits platzierte
									if(this.item[i].isBig)
										break;
									
									found++;
								}
							}*/
							
							var distancePerElement = distance / found;
							
							/*
							for(var i=this.item.length -1; i>0; i--){
								if(this.item[i].isPlaced && this.item[i].column == (columnToPlace)){
									if(this.item[i].isBig)
										break;
									
									this.item[i].element.find(resizableClass).css('margin-top', Math.round(distancePerElement / 2));
									this.item[i].element.find(resizableClass).css('margin-bottom', Math.round(distancePerElement / 2));
								}
							}*/
							
							this.verticalOffsets[columnToPlace].element.find(resizableClass).css('margin-top', Math.round(distancePerElement / 2));
							this.verticalOffsets[columnToPlace].element.find(resizableClass).css('margin-bottom', Math.round(distancePerElement / 2));

						}
					}
				}
				
				
				// Set / Update new Elements
				this.verticalOffsets[columnToPlace] = layoutElement;
				this.verticalOffsets[columnToPlace + 1] = layoutElement;
				
				this.item[index].column = columnToPlace;
				
				calculatedPosition.offset.vertical = offsetToPlace;
				
				
				if((columnToPlace - 1) >= 0){
					calculatedPosition.offset.horizontal = 
						this.verticalOffsets[columnToPlace - 1].offset.horizontal + this.verticalOffsets[columnToPlace - 1].width;
				}else{
					calculatedPosition.offset.horizontal = 7;
				}		
				
				//console.log("Column:"+columnToPlace+" Vertical-offset:"+offsetToPlace+" Horizontal-Offset:"+calculatedPosition.offset.horizontal);
				
			}else{
				// Normale Größe	
				for(var column=0; column < this.verticalOffsets.length; column++){
				
					var actualOffset = 0;
					if(this.verticalOffsets[column]){
						 actualOffset = this.verticalOffsets[column].offset.vertical + this.verticalOffsets[column].height;
					}
					
					if(actualOffset < offsetToPlace || column == 0){
						// Platziere in Spalte
						columnToPlace = column;	
						offsetToPlace = actualOffset;
					}
				}	
				
				this.verticalOffsets[columnToPlace] = layoutElement;
				calculatedPosition.offset.vertical = offsetToPlace;
				this.item[index].column = columnToPlace;
				
				if((columnToPlace - 1) >= 0){
					calculatedPosition.offset.horizontal = 
						this.verticalOffsets[columnToPlace - 1].offset.horizontal + this.verticalOffsets[columnToPlace - 1].width;
				}else{
					calculatedPosition.offset.horizontal = 7;
				}				
			
			}
			
			return calculatedPosition.offset;
		},
		
		orderWithPositioning:function(){

			// Hilfsvariable
			var maxColumnIndex = this.columnsPerRow -1;
			// Variable für die unterste Position eines Elements, zum Setzen des Footers
			var bottomOffset = new Array();
			// Iteriere durch alle Elemente, die absolut positioniert werden sollen
			for(var index=0; index< this.item.length; index++){
				
				this.item[index].height = this.item[index].element.outerHeight(true);
				this.item[index].width = this.item[index].element.outerWidth(true);
				
				// Setze Werte für Elemente
				if(this.isBigElement(this.item[index].element)){
					this.item[index].isBig = true;
				}

				this.item[index].offset = this.findPositionForElement(this.item[index], index, false);
				var positionOffset = this.item[index].offset;
				// Positioniere Element mit dem Index				
				this.positionAt(index, positionOffset.horizontal, positionOffset.vertical);
				this.item[index].isPlaced = true;
				
				bottomOffset.push(this.item[index].offset.vertical + this.item[index].element.outerHeight(true));
			};
			
			$j(parentId).css("min-height", Math.max.apply(Math, bottomOffset));
			
		}
}
