module('apps.GridBagLayoutMorphs').requires('apps.GridBagLayout').toRun(function () {


/****************************************
example Grid container class
****************************************/	
BoxMorph.subclass('MyGridLayoutMorph', {
	style: { 
		borderWidth: 1, 
		borderColor: Color.red 
	},
	
	initialize: function ($super, position) {

		if (!position) {
			position = pt(50, 100);
		}
		$super(position.extent(pt(200, 150)));

		this.setFill(null);
		this.layoutManager = new GridBagLayoutManager(this);
	}
	
});




/****************************************
Grid lines class
****************************************/
BoxMorph.subclass('GridLine', {
	style: {borderWidth: 1, borderColor: Color.black },
	cursorChanged: false,
	lineOverhead: 10, // How many pixel the line is higher than the container

	/*******************************
	 * Initialize
	 *******************************/
	initialize: function ($super, bounds, container) {
		var rect;
		
		$super(bounds);
		this.container = container;
		this.suppressGrabbing = true;
		this.suppressHandles = true;
		this.openForDragAndDrop = false;
		
			
		rect = new Rectangle(bounds.x, bounds.y, this.lineOverhead, this.lineOverhead);
		this.toogleMorph = new GridLineToogleMorph(rect, this);
		this.toogleMorph1 = new GridLineToogleMorph(rect, this);
		
		this.createAddMorph(bounds);
	},


	
	/*******************************
	 * Adds a morph which can be used to add new column and rows
	 *******************************/	
	createAddMorph: function (bounds) {
		var world, rect;
		world = WorldMorph.current();
		rect = new Rectangle(bounds.x,bounds.y, 24, 24);
		this.addingMorph = new GridLineAddMorph(rect,
			URL.codeBase.withFilename('media/plus.png').toString(), true, this);
		this.addingMorph.setFill(null);
		
		world.addMorph(this.addingMorph);
		this.addingMorph.setBounds(rect);
	},
	
	
	
	/*******************************
	 * Set the columns connected with this line
	 *******************************/	
	setColumns: function (left, right, maxColumns) {
		var ext, bounds, world, translation;
		
		this.leftCol = left;
		this.rightCol = right;
		
		world = WorldMorph.current();
		bounds = this.shape.bounds();
		
		if (right <= maxColumns) {

			ext = new pt(this.getWidthOfColumn(right), this.lineOverhead);
			this.toogleMorph.setExtent(ext);
			this.toogleMorph.setCol(this.container.layoutManager.cols[right].fixed, right);
			
			
			this.toogleMorph1.setExtent(ext);
			this.toogleMorph1.setCol(this.container.layoutManager.cols[right].fixed, right);
			translation = pt(0, this.shape.bounds().bottomLeft().y - this.lineOverhead);
			this.toogleMorph1.moveBy(translation);
			
			world.addMorphBack(this.toogleMorph);
			world.addMorphBack(this.toogleMorph1);
		}
		
		translation = pt(-12, -2 * this.lineOverhead);
		this.addingMorph.moveBy(translation);
		this.addingMorph.setBorderWidth(0);
	},
	
	/*******************************
	 * Set the rows connected with this line
	 *******************************/
	setRows: function (up, down, maxRows) {
		var ext, bounds, world, translation;
		this.topRow = up;
		this.bottomRow = down;
		
		world = WorldMorph.current();
		bounds = this.shape.bounds();
		
		if (down <= maxRows) {
		
			ext = new pt(this.lineOverhead, this.getHeightOfRow(down));
			this.toogleMorph.setExtent(ext);
			this.toogleMorph.setRow(this.container.layoutManager.rows[down].fixed, down);
			translation = pt(-1, 0);
			this.toogleMorph.moveBy(translation);
			
			
			this.toogleMorph1.setExtent(ext);
			this.toogleMorph1.setRow(this.container.layoutManager.rows[down].fixed, down);
			translation = pt(this.shape.bounds().bottomRight().x - this.lineOverhead + 1, 0);
			this.toogleMorph1.moveBy(translation);
			
			world.addMorph(this.toogleMorph);
			world.addMorph(this.toogleMorph1);
		}
		
		translation = pt(-2 * this.lineOverhead, -12);
		this.addingMorph.moveBy(translation);
		this.addingMorph.setBorderWidth(0);
	},
	
	 /*******************************
	 * State that we handle mouse down events
	 *******************************/
	handlesMouseDown: function (evt) { return true; },
	
	 /*******************************
	 * Handle mouse down events and forward them to the slide functions
	 *******************************/
	onMouseDown: function (evt) {
		var upX, upY, lm, boundUpperY, boundDownY, boundUpperX, boundDownX;
		upX = this.getPosition().x;
		upY = this.getPosition().y;
		lm = this.container.layoutManager;
		boundUpperY = upY + this.lineOverhead;
		boundDownY = (upY + this.shape.bounds().height - this.lineOverhead);
		boundUpperX = upX + this.lineOverhead;
		boundDownX = (upX + this.shape.bounds().width - this.lineOverhead);
		
		lm = this.container.layoutManager;
		
		if (lm.container === undefined) {
			lm.container = this.container;
			lm.recreateMorphCache();
		}
	
		if (this.leftCol !== undefined) {
		
			//slide in container
			if (evt.point().y >= boundUpperY && evt.point().y <= boundDownY) {
				console.log("Span: lCol:"+this.leftCol+" rCol:"+this.rightCol);
				this.prepareHorizontalSpanning(evt);
			} else { // slide outside 
				console.log("Span: lCol:"+this.leftCol+" rCol:"+this.rightCol);
				this.verticalSlide(evt);
			}
		} else {
			//slide in container
			if (evt.point().x >= boundUpperX && evt.point().x <= boundDownX) {	
				console.log("Span: bRow:"+this.bottomRow+" tRow:"+this.topRow);
				this.prepareVerticalSpanning(evt);
			} else {//slide outside
				console.log("Slide: bRow:"+this.bottomRow+" tRow:"+this.topRow);
				this.horizontalSlide(evt);
			}

		}
	},
	
	 /*******************************
	 * Establish a connection and prepare horizontal movement
	 *******************************/
	horizontalSlide: function (evt) { 
		this.oldPoint = evt.point();
		this.pointerConnection = connect(evt.hand, 'origin', this, 'movedVerticallyBy', {converter: function (pos) {
			var resizer, p1, p2, deltaY;
			resizer = this.getTargetObj();
			p1 = resizer.oldPoint;
			p2 = pos;
			deltaY = p2.y - p1.y;
			resizer.oldPoint = pos;
			return deltaY;
		}});	
	},
	
	setCursor: function(evt) {
		if (this.cursorChanged !== true) {
			if (this.leftCol !== undefined) {
				evt.hand.lookLikeAnUpDownArrow();
				evt.hand.rotateBy(Math.PI / 2);
			} else {
				evt.hand.lookLikeAnUpDownArrow();
			}
			this.cursorChanged = true;
		}
	},
	
	/*******************************
	 * Establish a connection and prepare vertical movement
	 *******************************/	
	verticalSlide: function (evt) {
		this.oldPoint = evt.point();
		this.pointerConnection = connect(evt.hand, 'origin', this, 'movedHorizontallyBy', {converter: function (pos) {
			var resizer, p1, p2, deltaX;
			resizer = this.getTargetObj();
			p1 = resizer.oldPoint;
			p2 = pos;
			deltaX = p2.x - p1.x;
			resizer.oldPoint = pos;
			return deltaX;
		}});	
	},
	
	 /*******************************
	 * Establish a connection and prepare vertical spanning
	 *******************************/	
	prepareHorizontalSpanning: function (evt) {
		this.oldPoint = evt.point();
		
		this.pointerConnection = connect(evt.hand, 'origin', this, 'horizontalSpanning', {converter: function (pos) {
			var resizer, p1, p2, deltaX;
			resizer = this.getTargetObj();
			p1 = resizer.oldPoint;
			p2 = pos;
			deltaX = p2.x - p1.x;
			return deltaX;
		}});	
	},
	
	 /*******************************
	 * Establish a connection and prepare horizontal spanning
	 *******************************/	
	prepareVerticalSpanning: function (evt) {
		this.oldPoint = evt.point();
		
		this.pointerConnection = connect(evt.hand, 'origin', this, 'verticalSpanning', {converter: function (pos) {
			var resizer, p1, p2, deltaY;
			resizer = this.getTargetObj();
			p1 = resizer.oldPoint;
			p2 = pos;
			deltaY = p2.y - p1.y;
			return deltaY;
		}});	
	},
	
	/*******************************
	 * Establish a connection and prepare horizontal spanning
	 *******************************/	
	horizontalSpanning: function (deltaX) {
		var lm =this.container.layoutManager;
		
		if (this.leftCol < 0) {
			return;
		}
		
		lm.showColSpan(deltaX, pt(this.oldPoint.x, this.oldPoint.y), this.leftCol);
	},
	
	/*******************************
	 * Establish a connection and prepare vertical spanning
	 *******************************/	
	verticalSpanning: function (deltaY) {
		var lm =this.container.layoutManager;
		
		if (this.bottomRow < 0) {
			return;
		}
		
		lm.showRowSpan(deltaY, pt(this.oldPoint.x, this.oldPoint.y), this.topRow);
	},

	 /*******************************
	 * Restore mouse pointer
	 *******************************/
	onMouseUp: function (evt) {
		evt.hand.lookNormal(); // needed when hand is not over morph anymore
		this.pointerConnection.disconnect();
		this.pointerConnection = null;
	},

	 /*******************************
	 * Move vertically
	 *******************************/
	movedVerticallyBy: function (deltaY) {
		var lm, newPos;
		lm = this.container.layoutManager;
		lm.gridResizeRows(deltaY, this.topRow, this.bottomRow, this.container);
		newPos = pt(0,deltaY);
		this.moveBy(newPos);
	},
	
	/*******************************
	 * Move horizontal
	 *******************************/
	movedHorizontallyBy: function (deltaX) {
		var lm, newPos;
		
		//call parent layout manager
		lm = this.container.layoutManager;
		
		lm.gridResizeCols(deltaX, this.leftCol, this.rightCol, this.container);
		newPos = pt(deltaX, 0);
		this.moveBy(newPos);
	},

	 /*******************************
	 * Restore mouse pointer
	 *******************************/
	onMouseOut: function (evt) {	
		if (this.leftCol !== undefined) {
			evt.hand.rotateBy(-Math.PI / 2);
		}
		evt.hand.lookNormal();
		this.cursorChanged = false;
	},
	
	 /*******************************
	 * Override of onMouseMove to supress drag and drop
	 *******************************/
	onMouseMove: function (evt, hasFocus) {	
		return; 
	},
	
	onMouseOver: function(evt) {
		this.setCursor(evt);
	},
	
	/*******************************
	 * Implementation of costum menu
	 *******************************/
	morphMenu: function ($super, evt) { 
		var menu = $super(evt);
		menu.addLine();
		menu.addItem(["Hide grid", this.hideGrid]);
		return menu;
	},
	
	 /*******************************
	 * Hide grid
	 *******************************/
	hideGrid: function () {
		this.container.layoutManager.hideGrid();
	},
	
	 /*******************************
	 * Remove morph and toogle morph
	 *******************************/
	remove: function ($super) {
	
		if (this.toogleMorph !== undefined) {
			this.toogleMorph.remove();
			this.toogleMorph1.remove();
		}
		
		if (this.addingMorph !== undefined) {
			this.addingMorph.remove();
		}
		
		$super();
	},
	
	/*******************************
	 * Returns the with of the supplied column
	 *******************************/
	getWidthOfColumn: function (colNum) {
		return this.container.layoutManager.cols[colNum].size;
	},
	
	/*******************************
	 * Returns the height of the supplied row
	 *******************************/
	getHeightOfRow: function (rowNum) {
		return this.container.layoutManager.rows[rowNum].size;
	},
	
	/*******************************
	 * toggles the row mode (fixed / dynamic)
	 *******************************/
	toggleRowMode: function (rowNum) {
		this.container.layoutManager.toggleRowFixed(rowNum);
	},
	
	/*******************************
	 * toggles the col mode (fixed / dynamic)
	 *******************************/
	toggleColMode: function (colNum) {
		this.container.layoutManager.toggleColFixed(colNum);
	},
	
	/*******************************
	 * Toogle fixed or dynamic mode of row or column
	 *******************************/	
	toogleMode : function(oldColor) {
		
		if (oldColor === Color.green) {
			this.toogleMorph.setFill(Color.red);
			this.toogleMorph1.setFill(Color.red);
		} else {
			this.toogleMorph.setFill(Color.green);
			this.toogleMorph1.setFill(Color.green);
		}
		
		if (this.toogleMorph.colNum !== undefined) {
			this.toggleColMode(this.toogleMorph.colNum);
		} else {
			this.toggleRowMode(this.toogleMorph.rowNum);
		}
	},
	
	/*******************************
	 * Adds a new column or row
	 *******************************/		
	addNewRowOrCol : function() {
		var lm =this.container.layoutManager;
		
		if (lm.container === undefined) {
			lm.container = this.container;
			lm.recreateMorphCache();
		}
		
		if (this.leftCol !== undefined) {
			//column
			this.container.layoutManager.addColAt(this.rightCol);
			this.container.layoutManager.relayout();
		}
		else{
			//row
			this.container.layoutManager.addRowAt(this.bottomRow);
			this.container.layoutManager.relayout();
		}
	}
});	




/****************************************
Grid lines toogle morph class used to toggle 
mode between fixed and dymanic row/column
****************************************/
BoxMorph.subclass('GridLineToogleMorph', {
	style: {borderWidth: 0, borderColor: Color.black },
	colNum : undefined,
	rowNum : undefined,

	/*******************************
	 * Initialize
	 *******************************/
	initialize: function ($super, bounds, line) {
		this.parentLine = line;
		this.suppressGrabbing=true;
		this.suppressHandles=true;
		this.openForDragAndDrop=false;
		$super(bounds);
		
		this.setFill(Color.green);
	},
	
	/*******************************
	 * State that we handle mouse down events
	 *******************************/
	handlesMouseDown: function (evt) { return true; },
	
	/*******************************
	 * Set row, which means its mode and number
	 *******************************/
	setRow: function (mode, rowNum) { 
		this.mode= mode;
		this.rowNum = rowNum;
		
		if (mode === true) {
			this.setFill(Color.red);
		}
	},
	
	/*******************************
	 * Set column, which means its mode and number
	 *******************************/
	setCol: function (mode, colNum) { 
		this.mode= mode;
		this.colNum = colNum;
		
		if (mode === true) {
			this.setFill(Color.red);
		}
	},
	 /*******************************
	 * Handle mouse down events and toogle color
	 *******************************/
	onMouseDown: function (evt) {
	
		this.parentLine.toogleMode(this.getFill());

	}
});	

/****************************************
Grid lines add morph class used to add new 
columns or rows
****************************************/
ImageMorph.subclass('GridLineAddMorph', {
	style: {borderWidth: 1, borderColor: Color.black },
	
	/*******************************
	 * Initialize
	 *******************************/
	initialize: function ($super, bounds, url, scaling, line) {
		this.suppressGrabbing = true;
		this.suppressHandles = true;
		this.openForDragAndDrop = false;
		this.parentLine = line;
		$super(bounds, url, scaling);
	},
	
	/*******************************
	 * State that we handle mouse down events
	 *******************************/
	handlesMouseDown: function (evt) { return true; },
	
	/*******************************
	 * Handle mouse down events and toogle color
	 *******************************/
	onMouseDown: function (evt) {
		this.parentLine.addNewRowOrCol();
	}
});	


}); // end of module