// Copyright 2008 ProjectWizards GmbH. All rights reserved.

//console.log("GanttView");

var GanttView = Base.extend({
	constructor: function(environment, ganttRuler, scrollView, drawShadows) {
		this.base();
		this.drawShadows = drawShadows;
		this.environment = environment;
		this.verticalLabelOffset = Prototype.Browser.Gecko ? 0 : 1;
		this.ruler = ganttRuler;
		this.scrollView = scrollView;
		this.controller = this.scrollView.delegate;
		this.displayGraphs = (this.controller && this.controller.displayGraphs && this.controller.displayGraphs()) ? true: false;
		this.controllerRespondsToGraphMethods = (this.controller && this.controller.showGraphForObject) ? true : false;
		this.graphContainer = null;
		this.recreateGraph = 0;
		this.connectShapeRects = {};
	},
	
	
	drawGraphBlock: function(graphObject, block, startDate) {
		var left = this.ruler.dateToPos(this.ruler.startOfUnitContainingDateClipAtDateExtendIfTooThin(this.ruler.primaryUnit, startDate, null, true));
		var right = this.ruler.dateToPos(this.ruler.endOfUnitWithStartDateClipAtDateExtendIfTooThin(this.ruler.primaryUnit, startDate, null, true));
		var x = left+1;
		var width = (right-left-1);
		var text = this.controller.graphTextForBlock(graphObject, block, width);
		block.div = DIV({'class':'graphBlock', style:'top:'+(graphObject.top+2)+'px; height:'+(graphObject.height-4)+'px; width:'+width+'px; left:'+x+'px; background-color:gray;'}
						, block.textDiv = DIV({'class':'graphText'}, text));	
		this.controller.willDrawGraphBlock(graphObject, block);
		this.graphContainer.appendChild(block.div);	
	},
	
	adjustGraph: function(){		
		var invisibleObjects = [];
		for(var objectID in this.graphObjects) {
			var graphObject = this.graphObjects[objectID];
			if(graphObject.positionRecorded){
				for(var startDateStr in graphObject.blocks){
					var block = graphObject.blocks[startDateStr];
					var startDate = block.startDate;
					var left = this.ruler.dateToPos(this.ruler.startOfUnitContainingDateClipAtDateExtendIfTooThin(this.ruler.primaryUnit, startDate, null, true));
					var right = this.ruler.dateToPos(this.ruler.endOfUnitWithStartDateClipAtDateExtendIfTooThin(this.ruler.primaryUnit, startDate, null, true));
					var top = (graphObject.top+2) + "px";
					var height = (graphObject.height-4) + "px";
					var width = right-left-2;
					block.div.style.left = (left+1) + "px";
					block.div.style.width =  Math.max(width, 0) + "px";
					if(block.div.style.top != top)
						block.div.style.top = top;
					if(block.div.style.height != height)
						block.div.style.height = height;
					block.textDiv.innerText = this.controller.graphTextForBlock(graphObject, block, width);
				}
			}else
				invisibleObjects.push(objectID);
		}
		for(var i=0; i<invisibleObjects.length; i++){
			var objectID = invisibleObjects[i];
			var graphObject = this.graphObjects[objectID];
			for(var startDateStr in graphObject.blocks){
				var block = graphObject.blocks[startDateStr];
				block.div.parentNode.removeChild(block.div);
			}
			delete this.graphObjects[objectID];
		}
	},
		
	graphValuesReceived: function(response){
		if(this.oldGraphContainer){
			this.container.removeChild(this.oldGraphContainer);
			this.oldGraphContainer = null;
		}			
		for(var objectID in response) {
			var graphObject = this.graphObjects[objectID];
			var responseObject = response[objectID];
			if(responseObject && graphObject) {
				graphObject.min = responseObject.min;
				graphObject.max = responseObject.max;
				graphObject.overThreshold = responseObject.overThreshold;
				graphObject.underThreshold = responseObject.underThreshold;
				if(!graphObject.blocks)
					graphObject.blocks = {};
				var newBlocks = responseObject.blocks;
				for(var startDateStr in newBlocks) {
					startDate = new Date(startDateStr * 1000);
					if(!graphObject.blocks[startDateStr]){
						var block = newBlocks[startDateStr];
						block.startDate = startDate;
						graphObject.blocks[startDateStr] = block;
						this.drawGraphBlock(graphObject, block, startDate);
					}
				}
			}
		}
	},
	
	visibleSections: function(){
		var result = {};
		var vStartDate = this.scrollView.visibleStartDate;
		var vEndDate = this.scrollView.visibleEndDate;
		var vStart = vStartDate.getTime();
		var vEnd = vEndDate.getTime();
		for(var i in this.horizontalSections){
			var section = this.horizontalSections[i];
			if((section.end >= vStart && section.end <= vEnd) || (section.start >= vStart && section.start <= vEnd) || (section.start < vStart && section.end > vEnd)){
				result[i] = section;
			}
		}
		return result;
	},
	
	requestGraphValues: function(){
		var request = null;
		var visibleSections = this.visibleSections();
		var visibleSectionsArray = [];
		var start = null;
		var end = null;
		for(var objectURI in this.graphObjects){
			var graphObject = this.graphObjects[objectURI];
			if(!this.controller.requestGraphValuesForObject(graphObject))
				continue;
			if(!graphObject.sections){
				graphObject.sections = {};
				for(var key in this.horizontalSections){
					var section = this.horizontalSections[key];
					graphObject.sections[key] = {start:section.start, end:section.end};
				}
			}
			var doRequest = false;
			for(var i in visibleSections){
				if(!graphObject.sections[i].requested){
					graphObject.sections[i].requested = true;
					doRequest = true;
				}
				if(!start)
					visibleSectionsArray.push(visibleSections[i]);
			}			
			if(!start){
				start = new Date(visibleSectionsArray[0].start);
				var visibleEnd = visibleSectionsArray[visibleSectionsArray.length-1].end;
				var rulerEnd = this.ruler.endDate.getTime();
				end = new Date(visibleEnd > rulerEnd ? rulerEnd : visibleEnd);
			}
			if(doRequest){
				if(!request){
					request = {};
					request.objectURIs = [];
					request.start = start;
					request.end = end;
					request.rulerStart = this.ruler.startDate;
					request.rulerEnd = this.ruler.endDate;
					request.ruler = this.ruler.rulerDescription();
				}
				request.objectURIs.push(objectURI);
			}
		}	
		if(request){
			this.controller.requestGraphValues(request, this.graphValuesReceived, this);
		}else if(this.oldGraphContainer){
			this.container.removeChild(this.oldGraphContainer);
			this.oldGraphContainer = null;
		}	
	},	
				
	initGraph: function() {
		if(this.displayGraphs){
			var numUnits = this.ruler.numberOfRulerUnits();
			var recreate = (!this.controller.isInLayoutChangingAction() || this.cachedNumberOfRulerUnits != numUnits || !this.graphObjects);
			this.recreateGraph += recreate ? 1 : 0;
			if(this.recreateGraph){
				this.cachedNumberOfRulerUnits = numUnits;	
				var right = this.ruler.posToDate(500);
				var left = this.ruler.posToDate(0);
				var diff = this.ruler.posToDate(500).getTime()-this.ruler.posToDate(0).getTime();
				var start = this.ruler.startDate.getTime();
				var rEnd = this.ruler.endDate.getTime();
				var end;
				this.horizontalSections = {};
				var i = 0;
				while(start < rEnd){
					end = start+diff;
					this.horizontalSections[i]={start:start, end:end};
					start = end;
					i++;
				}
				this.graphObjects = {}; 
			}
			for(var uri in this.graphObjects)
				this.graphObjects[uri].positionRecorded = false;
		}	
	},
																																																
	refreshGantt: function() {
		if(this.blockRefresh)
			return;
		this.minX = null;
		this.maxX = null;	
		this.minDate = null;							
		var offset = 0;
		if(!this.refreshWithoutOffset){
			this.refreshWithoutOffset = true;
			offset = -1000000;
		}else
			this.initGraph();
		var startDate = this.ruler.posToDate(offset);
		var endDate = this.ruler.posToDate(50);							
		this.ruler.adjustFromDateToDateOnlyByExpanding(startDate, endDate, true);		
		this._refreshGanttWithRows(this.initialRows, true);
	},
	
	drawGraphs: function(){
		this.graphTimer = null;
		if(this.recreateGraph){
			this.recreateGraph = 0;
			this.oldGraphContainer = this.graphContainer;
			this.graphContainer = DIV({'class':'graphContainer', style:'bottom:'+PWUtils.scrollbarWidth()+'px;'});
			this.container.appendChild(this.graphContainer);		
		}
		else
			this.adjustGraph();	// adjust the positions of the divs to match the ruler row.
		this.requestGraphValues();	// fetch missing graph values and creates new divs accordingly
	},

	refreshDidFinish: function(){
		if(this.displayGraphs)
			this.drawGraphs();
	},

	refreshWillStart: function(){
		if(this.controller && this.controller.refreshWillStart)
			this.controller.refreshWillStart();
	},

	recreateLabelContainer: function() {
		var old = this.labelContainer;
		this.labelContainer = DIV({'class':'labelContainer', style:'position:absolute; z-index:2; top:0px; right:0px; left:0px; bottom:'+PWUtils.scrollbarWidth()+'px;'});
		if(old)
			this.container.replaceChild(this.labelContainer , old);
		else
			this.container.appendChild(this.labelContainer);
	},

	setViewOptions: function(viewOptions) {
		this.viewOptions = viewOptions;
	},

	setStyleList: function(list) {
		this.styleList = list;
	},
	
	refreshRuler: function() {		
		this.blockRefresh = true;
		this.scrollView.ganttRulerView.refreshRuler();	
		this.blockRefresh = false;
	},

	projectEndDatePlusPixel: function(pixelNum)	{	
		return this.ruler.posToDate(this.ruler.dateToPos(this.endDate) + pixelNum);		
	},

	setWidth: function(width) {
		if(width != 0){
			this.rulerWidthChanged(width);
			this.refreshGantt();
		}
	},

	width: function() {
		return this.rulerWidth;
	},

	handleClickInGantt: function(event)	{
		var y = event.offsetY;
		if(!y)
			y = event.layerY;
		this.scrollView.handleClickInRowAtPosition(event, y);
	},
	
	calculateMinMax: function()	{
		var labels = this.newLabels;
		var label;
		var left;
		var right;
		for(var i=0; i<labels.length; i++){	
			label = labels[i];
			if(label.type == "L"){
				left = label.offsetLeft;
				if(left < this.minX || this.minX == null)
					this.minX = left;				
			}
			else if(label.type == "R"){
				right = parseInt(label.style.left) + label.offsetWidth;
				if(right > this.maxX || this.maxX == null)
					this.maxX = right;		
			}
		} 
	},
	
	minCanvasWidthWithPreferedWidth: function(w) {
		var diff = this.scrollView.hasVerticalScrollbar() ? PWUtils.scrollbarWidth() : 0;
		var containerWidth = this.container.cachedOffsetWidth-diff;
		if(w <= containerWidth)
			w = containerWidth;
		w -= 1;
		return w;		
	},
	
	canScrollVertically: function()	{	
		return (this.height() > this.container.offsetHeight);
	},
	
	refreshSelection: function(objects)	{
		if(!this.whiteSelections)
			this.whiteSelections = [];

		var uris = [];
		for(var i=0; i<objects.length; i++){
			uris.push(objects[i].valueForKey(WRLSnapshotURIKey));
		}
		
		var rows = this.scrollView.outlineRows();	// adjust background color of even rows
		var row;
		var uri;
		for(var i=0; i<rows.length; i++){
			row = rows[i];
			if(row /*&& row.isOdd*/){			
				uri = row.object.valueForKey(WRLSnapshotURIKey);
				var bgObject = this.backgrounds[uri];
				if(bgObject){
					bgObject.style.backgroundColor = row.className == "selected" ? "#D7E3F7": row.style.backgroundColor;
				}
				uris = uris.without(uri);
			}
		}

		for(var i=0; i<uris.length; i++){	// Create selection divs for white rows
			uri = uris[i];
			if(!this.whiteSelections[uri] && this.whiteBackgrounds[uri])
				this.whiteSelections[uri] = this.whiteBackgrounds[uri].call();
		}
	},
	
	outlineDidCreateRows: function(outline, rows)
	{
		if(rows.length)
			this._refreshGanttWithRows(rows, false);
	},


	outlineRowsBecameVisible: function(outline, rows)
	{
		if(rows.length && !this.drawnRows[rows[0].object.valueForKey(WRLSnapshotURIKey)])
			this._refreshGanttWithRows(rows, false);
	},
	
	// ************************************************************************************************************
	// *****
	// *****										High level drawing methods
	// *****	
	
	drawSelection: function(top, height) {
		var bg = $(document.createElement("div"));
		bg.style.position = "absolute";
		bg.className = "selected";
		bg.style.top = top+"px";
		bg.style.height = height + "px";
		this.backgroundContainer.appendChild(bg);	
		this.backgroundContainer.whiteSelectionDivs.push(bg);
		return bg;
	},
	
	drawBackground: function(top, width, height, color, row, group)	{
		var uri = row.object.valueForKey(WRLSnapshotURIKey);
		var isSelected = this.scrollView.outlineView.controller.selectedObjects.indexOf(row.object) != -1;
		var bg = $(document.createElement("div"));
		bg.style.position = "absolute";
		bg.style.top = top+"px";
		bg.style.left = "0px";
		bg.style.right = "0px";
		bg.style.height = height + "px";
		bg.style.backgroundColor = color;	
		bg.top = top;
		bg.bottom = top+height;
		bg.uri = uri;
		this.backgrounds[uri] = bg;
		this.backgroundContainer.appendChild(bg);
	},
	
	drawLabel: function(left, top, text, fontSize, fontWeight, fontStyle, fontStretch, fontFamily, color, type, width, group) {	
		left = Math.floor(left);
		top = Prototype.Browser.IE ? Math.floor(top-2) : Math.floor(top);
		width = Math.floor(width);
		var label = $(document.createElement("div"));
		label.type = type;
		label.style.position = "absolute";
		label.style.padding = "0px";
		label.style.margin = "0px";
		label.style.whiteSpace = "nowrap";
		label.style.overflow = "visible";
		if(fontSize)
			label.style.fontSize = fontSize+"px";
		if(fontWeight)
			label.style.fontWeight = fontWeight;
		if(fontStyle)
			label.style.fontStyle = fontStyle;
		if(fontStretch)
			label.style.fontStretch = fontStretch;
		if(fontFamily)
			label.style.fontFamily = fontFamily;
		if(color)
			label.style.color = color;
		label.style.lineHeight = "160%";
		label.style.top = top+"px";
		label.appendChild(document.createTextNode(text));
		if(type == "M" || type == "R") {
			if(width){	// middle label
				label.style.width = Math.max(width, 0) + "px";
				label.style.overflow = "hidden";
			}
			else	// right label
				this.newLabels.push(label);
			label.style.textAlign = "left";
			label.style.left = left+"px";
			this.labelContainer.appendChild(label);
		}else{	// left label
			label.style.top = "0px";
			label.style.right = "0px";
			label.style.textAlign = "right";
			var labelWrapper = $(document.createElement("div"));
			labelWrapper.style.position = "absolute";
			labelWrapper.style.top = top+"px";
			labelWrapper.style.left = "0px";
			labelWrapper.style.width = Math.max(left, 0)+"px";
			labelWrapper.appendChild(label);			
			this.labelContainer.appendChild(labelWrapper);			
			this.newLabels.push(label);
		}
	},
	
	// **************************************************************************************************
	// *****
	// *****							Calculate positions of activities
	// *****							and invoke appropriate drawing methods
	// *****
	
	barHeightForBarStyleMiddleLableStyle: function(barStyle, middleLabelStyle) {
		var height = middleLabelStyle.lineHeight;
		if(!height)
			height = middleLabelStyle.fontSize+4;
		if(!height || height<12)
			height = 12;
		if(barStyle.height && height<barStyle.height)
			height = barStyle.height;
		return height;
	},

	recordVerticalGraphPosition: function(object, row, ruler, top, height, canvasWidth, group) {
		var color = row.className == "selected" ? "#D7E3F7": row.style.backgroundColor;
		this.drawBackground(top, canvasWidth, height-2, color, row, group);
		if(this.graphObjects){
			var objectURI = object.uri();
			var graphObject = this.graphObjects[objectURI];
			if(!graphObject){
				graphObject = this.graphObjects[objectURI] = {};
				graphObject.object = object;
			}
			graphObject.top = top;
			graphObject.height = height;
			graphObject.positionRecorded = true;
		}
	},
	
	createActivityInRow: function(activity, row, ruler, top, height, canvasWidth, group) {
		var isEmptyProject = activity.className() == "MEProject" && activity.isLeaf();
		if(isEmptyProject) {
			activity = new WRLObject();	
			var startDate = new Date();
			var endDate = ruler.posToDate(this.container.offsetWidth);
			activity.setValueForKey(startDate, "expectedStartDate");
			activity.setValueForKey(startDate, "plannedStartDate");
			activity.setValueForKey(endDate, "expectedEndDate");
			activity.setValueForKey(endDate, "plannedEndDate");
		}

		var x = ruler.dateToPos(activity.valueForKey("expectedStartDate"))-this.leftMargin;
		var width = Math.max(ruler.dateToPos(activity.valueForKey("expectedEndDate"))-x-this.leftMargin, 4);	
		var maxX = x+width+50;	// plus ein bisschen extra Rand
		if(this.maxX < maxX)
			this.maxX = maxX;			
			
		if(this.displayGraphs && this.controllerRespondsToGraphMethods && this.controller.showGraphForObject(activity)){
			this.recordVerticalGraphPosition(activity, row, ruler, top, height, canvasWidth, group);
			var minDate = activity.valueForKey("expectedStartDate");	
			if(!this.minDate || (minDate && this.minDate.getTime() > minDate.getTime()))	{
				this.minDate = minDate;
				this.minX = x-10; // add a left margin
			}
			return;
		}

		var completeWidth = ruler.dateToPos(activity.valueForKey("actualCompleteThroughDate"))-x-this.leftMargin;
		var overlap = false;
		var	plannedX = null;
		var	plannedWidth = null;
		var minDate = activity.valueForKey("expectedStartDate");
		var color = row.className == "selected" ? "#D7E3F7": row.style.backgroundColor;

		if(!minDate)
		{
			this.drawBackground(top, canvasWidth, height-2, color, row, group);
			return;
		}
			
		if(this.viewOptions.displayMode != 3  && (!minDate.isEqual(activity.valueForKey("plannedStartDate")) 
			|| !activity.valueForKey("expectedEndDate").isEqual(activity.valueForKey("plannedEndDate")))) {
			plannedX = ruler.dateToPos(activity.valueForKey("plannedStartDate"))-this.leftMargin;
			plannedWidth = ruler.dateToPos(activity.valueForKey("plannedEndDate"))-plannedX-this.leftMargin;
			var end1 = x+width+2; // +2 wegen dem Rand
			var end2 = plannedX+plannedWidth+2;
			overlap = (end1>=plannedX && end1<=end2) || (x>=plannedX && x<=end2);
			if(minDate.getTime()>activity.valueForKey("plannedStartDate").getTime())
				minDate = activity.valueForKey("plannedStartDate");
		}
		if(!this.minDate || this.minDate.getTime() > minDate.getTime())	{
			this.minDate = minDate;
			this.minX = x-10; // add a left margin
		}
		var children = activity.valueForKey("linkedSubActivities");
		var isGroup = children && children.length;
		var isMilestone = activity.valueForKey("isMilestone");
		var barStyle = this.styleList.flattenedStyleForTarget(StyleList.activityViewStyleTargets.MEStyleTargetBar, activity.valueForKey("styleConditionMask"));
		
		var barColor = null;
		if(this.displayGraphs && this.controllerRespondsToGraphMethods)
			barColor = this.controller.barColorForObject(activity, isGroup); 

		var middleLabelStyle = this.styleList.flattenedStyleForTarget(StyleList.activityViewStyleTargets.MEStyleTargetLabelMiddle, activity.valueForKey("styleConditionMask"));
		var barHeight = this.barHeightForBarStyleMiddleLableStyle(barStyle, middleLabelStyle);
		var y = top+(height-barHeight)/2;
		
		this.drawBackground(top, canvasWidth, height-2, color, row, group);

		if(isGroup)
			barHeight += 2;
		else if(!isMilestone)
			barHeight -= 1;

		if(!isEmptyProject) {
			if(isGroup)
				this.createGroupInRow(activity, row, x, y, plannedX, plannedWidth, width, completeWidth, barColor ? barColor : barStyle, barHeight, height, top, overlap, group);
			else if(isMilestone)
				this.createMilestoneInRow(activity, row, x, y, plannedX, width, barStyle, barHeight, height, overlap, group);
			else
				this.createBarInRow(activity, row, x, y, plannedX, plannedWidth, width, completeWidth, barStyle, barHeight, height, top, overlap, group);
			if(this.viewOptions.showLabels)
				this.createLabelInRow(activity, row, x, y, plannedX, plannedWidth, width, height, barHeight, overlap, group);				
		}

	},
	
	createGroupInRow: function(activity, row, x, y, plannedX, plannedWidth, width, completeWidth, barStyle, barHeight, height, top, overlap, group) {
		var me = this;
		var isMilestone = activity.valueForKey('isMilestone');
		if(activity.valueForKey("isCollapsed") && !isMilestone){
			this.createBarInRow(activity, row, x, y, plannedX, plannedWidth, width, completeWidth, barStyle, barHeight-3, height, top, overlap, group);
			return;
		}else if(isMilestone){
			this.createMilestoneInRow(activity, row, x, y, plannedX, width, barStyle, barHeight, height, overlap, group);
			return;
		}
		var borderWidth = 0.5;
		x = Math.floor(x)+0.5;
		y = Math.floor(y)+0.5;
		var h = Math.floor(barHeight/3.0);
		var y1 = y;
		var y2 = y;
		if(plannedX){
			// Schatten
			var border = Math.max((height-(2*barHeight+5))*0.5, 1);
			y1 = overlap ? Math.floor(top+height-border-barHeight)+0.5 : y;
			this.drawGroup(Math.floor(plannedX)+0.5, y1, plannedWidth, 0, barHeight, "#AAAAAA", "#555555", borderWidth, false, group);
			y2 = overlap ? Math.floor(top+border)+0.5 : y;
			this.drawGroup(x, y2, width, completeWidth, barHeight, barStyle.backgroundColor, barStyle.color, borderWidth, true, group);
		}else	
			this.drawGroup(x, y, width, completeWidth, barHeight, barStyle.backgroundColor, barStyle.color, borderWidth, true, group);
		width += 2*h;
		x -= h;		
		var yDiff = Math.floor((barHeight-2*h)/2.0);
		y1 += yDiff;
		y2 += yDiff;
		this.connectShapeRects[activity.uri()] = {x:x, y:y2, w:width, h:h};
		if(!plannedX){
			activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}else{
			plannedX -= h;
			plannedWidth += 2*h;
			if(plannedX < x)
				activity.leftLabelRect = {x:plannedX, y:y1, w:plannedWidth, h:h};
			else
				activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			if(plannedX+plannedWidth > x+width)	
				activity.rightLabelRect = {x:plannedX, y:y1, w:plannedWidth, h:h};
			else
				activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}	
	},

	createMilestoneInRow: function(activity, row, x, y, plannedX, width, barStyle, barHeight, height, overlap, group) {
		var me = this;	
		var h = barHeight*0.5;
		var borderWidth = barStyle.borderStyle && this.viewOptions.showCriticalPath ? 1.5 : 0.5;
		var offset = barStyle.borderStyle ? 0 : 0.5;
		x = Math.floor(x)+offset;
		y = Math.floor(y)+offset;	
		if(plannedX)
			this.drawMilestone(Math.floor(plannedX)+offset, y, h, "#AAAAAA", "#555555", borderWidth, group);
		this.drawMilestone(x, y, h, barStyle.backgroundColor, barStyle.color, borderWidth, group);
		this.connectShapeRects[activity.uri()] = {x:x-h, y:y, w:barHeight, h:barHeight};																		
		if(!plannedX){
			activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}else{
			if(plannedX < x)
				activity.leftLabelRect = {x:plannedX-h, y:y, w:barHeight, h:barHeight};
			else
				activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			if(plannedX > x+width)
				activity.rightLabelRect = {x:plannedX-h, y:y, w:barHeight, h:barHeight};
			else
				activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}	
	},

	createBarInRow: function(activity, row, x, y, plannedX, plannedWidth, width, completeWidth, barStyle, barHeight, height, top, overlap, group) {
		var dashed = activity.className() == 'MEAssignment';
		var me = this;
		var radius = barHeight*0.15;
		var borderWidth = barStyle.borderStyle && this.viewOptions.showCriticalPath ? 1.5 : 0.5;
		var offset = barStyle.borderStyle ? 0 : 0.5;
		x = Math.floor(x)+offset;
		y = Math.floor(y)+offset-1;
		var y1 = y;
		var y2 = y;
		if(plannedX){
			var border = Math.max((height-(2*barHeight+5))*0.5, 1);
			y1 = overlap ? Math.floor(top+height-border-barHeight)+offset-2 : y;
			this.drawBar(Math.floor(plannedX)+offset, y1, plannedWidth, 0, barHeight, radius, "#AAAAAA", "#555555", borderWidth, false, group, dashed);
			y2 = overlap ? Math.floor(top+border)+offset : y;
			this.drawBar(x, y2, width, completeWidth, barHeight, radius, barStyle.backgroundColor, barStyle.color, borderWidth, true, group, dashed);
		}else
			this.drawBar(x, y, width, completeWidth, barHeight, radius, barStyle.backgroundColor, barStyle.color, borderWidth, true, group, dashed);
		this.connectShapeRects[activity.uri()] = {x:x, y:y2, w:width, h:barHeight};	
		if(!plannedX){
			activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}else{
			if(plannedX < x)
				activity.leftLabelRect = {x:plannedX, y:y1, w:plannedWidth, h:barHeight};
			else
				activity.leftLabelRect = this.connectShapeRects[activity.uri()];
			if(plannedX+plannedWidth > x+width)	
				activity.rightLabelRect = {x:plannedX, y:y1, w:plannedWidth, h:barHeight};
			else
				activity.rightLabelRect = this.connectShapeRects[activity.uri()];
		}
	},
	
	createLabelInRow: function(activity, row, x, y, plannedX, plannedWidth, width, height, barHeight, overlap, group) {
		var me = this;
		var style = this.styleList.flattenedStyleForTarget(StyleList.activityViewStyleTargets.MEStyleTargetLabelLeft, activity.valueForKey("styleConditionMask"))
		var isMilestone = activity.valueForKey('isMilestone');
		if(style.contentKey){		
			style.contentFormatter.environment = this.environment;  
			var leftLabel = style.contentFormatter.stringForObjectValue(activity.valueForKey(style.contentKey));
			if(leftLabel.length){
				if(plannedX && plannedX < x){
					x = plannedX;
					width = plannedWidth;
				}
				var rect = activity.leftLabelRect;
				var div = $(document.createElement("div"));
				var fontSize = style.fontSize || 10;
				var top = rect.y+this.verticalLabelOffset+(rect.h-(fontSize*1.5))*0.5 - 1.0;
				var left = x-barHeight-(isMilestone ? 7 : 0);
				this.drawLabel(left, top, leftLabel, fontSize, style.fontWeight, style.fontStyle, style.fontStretch, style.fontFamily, style.color, "L", group);
 			}
		}
		style = this.styleList.flattenedStyleForTarget(StyleList.activityViewStyleTargets.MEStyleTargetLabelMiddle, activity.valueForKey("styleConditionMask"))
		var isGroup = activity.isGroup ? activity.isGroup() : false;
		if(style.contentKey && !isMilestone && (!isGroup || (isGroup && activity.valueForKey("isCollapsed")))){		
			style.contentFormatter.setPreferedWidth(width-4);
			style.contentFormatter.environment = this.environment;
			var middleLabel = style.contentFormatter.stringForObjectValue(activity.valueForKey(style.contentKey));
			if(middleLabel.length){
				var rect = this.connectShapeRects[activity.uri()];
				var fontSize = style.fontSize || 10;
				var top = rect.y+this.verticalLabelOffset+(rect.h-(fontSize*1.5))*0.5 - 1.0;
				var left = rect.x+2;
				var width = width-4;
				if(width>4)
					this.drawLabel(left, top, middleLabel, fontSize, style.fontWeight, style.fontStyle, style.fontStretch, style.fontFamily, style.color, "M", width, group);
			}
		}
		style = this.styleList.flattenedStyleForTarget(StyleList.activityViewStyleTargets.MEStyleTargetLabelRight, activity.valueForKey("styleConditionMask"))
		if(style.contentKey){		
			style.contentFormatter.environment = this.environment;
			var rightLabel = style.contentFormatter.stringForObjectValue(activity.valueForKey(style.contentKey));
			if(rightLabel.length){
				var rect = activity.rightLabelRect;
				var fontSize = style.fontSize || 10;				
				var left = rect.x+rect.w+barHeight;
				var top = rect.y+this.verticalLabelOffset+(rect.h-(fontSize*1.5))*0.5 - 1.0;
				this.drawLabel(left, top, rightLabel, fontSize, style.fontWeight, style.fontStyle, style.fontStretch, style.fontFamily, style.color, "R", null, group);
			}
		}	
	},
	
	createRelationships: function(group, rows) {
		var row;
		for(var rowIndex in rows){		// todo: Use an object instead of an array for drawnRows
			row = rows[rowIndex];
			if(row && typeof row != "function"){
				var rels = row.object.valueForKey("nextActivityRelationships");
				if(rels){			
					for(var relIndex=0; relIndex<rels.length; relIndex++){
						var rel = rels[relIndex];
						var nextActivity = rel.valueForKey("nextActivity");
						if(nextActivity && this.drawnRows[nextActivity.valueForKey(WRLSnapshotURIKey)] && !this.drawnRelations[rel.ID]){
							this.drawnRelations[rel.ID] = 1;
							this.drawRelationship(rel, group);			
						}
					}
				}
			}
		}
	}
});
