dojo.provide("dojox.mdnd.dropMode.VerticalDropMode");
dojo.require("dojox.mdnd.AreaManager");

dojo.declare(
	"dojox.mdnd.dropMode.VerticalDropMode",
	null,
{
	// summary:
	//		Enabled a type of calcul for Dnd.
	//		Default class to find the nearest target.

	// _oldXPoint: Integer
	//		used to save a X position
	_oldXPoint: null,

	// _oldYPoint: Integer
	//		used to save a Y position
	_oldYPoint: null,

	// _oldBehaviour: String
	// 		see <getDragPoint>
	_oldBehaviour: "up",

	addArea: function(/*Array*/areas, /*Object*/object){
		// summary:
		//		Add a DnD Area into an array sorting by the x position.
		// areas:
		//		array of areas
		// object:
		//		data type of a DndArea
		// returns:
		//		a sorted area

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: addArea");
		var length = areas.length;
		var position = dojo.position(object.node, true);
		object.coords = {'x':position.x, 'y':position.y};
		if(length == 0){
			areas.push(object);
		}
		else{
			var x = object.coords.x;
			for(var i = 0; i < length; i++){
				if(x < areas[i].coords.x){
					for(var j = length-1; j >= i; j--)
						areas[j + 1] = areas[j];
					areas[i] = object;
					break;
				}
			}
			if(i == length){
				areas.push(object);
			}
		}
		return areas;	// Array
	},

	updateAreas: function(/*Array*/areaList){
		// summary:
		//		Refresh intervals between areas to determinate the nearest area to drop an item.
		//		Algorithm :
		//		the marker should be the vertical line passing by the
		//		central point between two contiguous areas.
		//		Note:
		//		If the page has only one targetArea, it's not necessary to calculate coords.
		// areaList:
		//		array of areas

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: initAreas");
		var length = areaList.length;
		if(length > 1){
			var currentRight, nextLeft;
			for(var i = 0; i < length; i++){
				var area = areaList[i];
				var nextArea;
				area.coords.x1 = -1;
				area.coords.x2 = -1;
				if(i == 0){
					nextArea = areaList[i+1];
					this._updateArea(area);
					this._updateArea(nextArea);
					currentRight = area.coords.x + area.node.offsetWidth;
					nextLeft = nextArea.coords.x;
					area.coords.x2 = currentRight + (nextLeft-currentRight)/2;
				}
				else if(i == length-1){
					area.coords.x1 = areaList[i-1].coords.x2;
				}
				else{
					nextArea = areaList[i+1];
					this._updateArea(nextArea);
					currentRight = area.coords.x + area.node.offsetWidth;
					nextLeft = nextArea.coords.x;
					area.coords.x1 = areaList[i-1].coords.x2;
					area.coords.x2 = currentRight + (nextLeft-currentRight)/2;
				}
			}
		}
	},

	_updateArea : function(/*Object*/area){
		// summary:
		//		update the DnD area object (i.e. update coordinates of its DOM node)
		// area:
		//		the DnD area
		// tags:
		//		protected

		//console.log("dojox.mdnd.dropMode.VerticalDropMode  ::: _updateArea");
		var position = dojo.position(area.node, true);
		area.coords.x = position.x;
		area.coords.y = position.y;
	},

	initItems: function(/*Object*/area){
		// summary:
		//		initialize the horizontal line in order to determinate the drop zone.
		// area:
		//		the DnD area

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: initItems");
		dojo.forEach(area.items, function(obj){
			//get the vertical middle of the item
			var node = obj.item.node;
			var position = dojo.position(node, true);
			var y = position.y + position.h/2;
			obj.y = y;
		});
		area.initItems = true;
	},

	refreshItems: function(/*Object*/area, /*Integer*/indexItem, /*Object*/size, /*Boolean*/added){
		// summary:
		//		take into account the drop indicator DOM element in order to compute horizontal lines
		// area:
		//		a DnD area object
		// indexItem:
		// 		index of a draggable item
		// size:
		//		dropIndicator size
		// added:
		//		boolean to know if a dropIndicator has been added or deleted

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: refreshItems");
		if(indexItem == -1){
			return;
		}
		else if(area && size && size.h){
			var height = size.h;
			if(area.margin){
				height += area.margin.t;
			}
			var length = area.items.length;
			for(var i = indexItem; i < length; i++){
				var item = area.items[i];
				if(added){
					item.y += height;
				}
				else{
					item.y -= height;
				}
			}
		}
	},

	getDragPoint: function(/*Object*/coords, /*Object*/size, /*Object*/mousePosition){
		// summary:
		//		return coordinates of the draggable item
		// description:
		//		return for:
		// 			- X point : the middle
		//	  		- Y point : search if the user goes up or goes down with his mouse.
		//	  		- Up : top of the draggable item
		//	  		- Down : bottom of the draggable item
		// coords:
		//		an object encapsulating X and Y position
		// size:
		//		an object encapsulating width and height values
		// mousePosition:
		//		coordinates of mouse
		// returns:
		//		an object of coordinates
		// 		example : {'x':10,'y':10}

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: getDragPoint");
		var y = coords.y;
		if(this._oldYPoint){
			if(y > this._oldYPoint){
				this._oldBehaviour = "down";
				y += size.h;
			}
			else
				if(y <= this._oldYPoint){
					this._oldBehaviour = "up";
				}
		}
		this._oldYPoint = y;
		return {
			'x': coords.x + (size.w / 2),
			'y': y
			};	// Object
	},

	getTargetArea: function(/*Array*/areaList, /*Object*/ coords, /*integer*/currentIndexArea ){
		// summary:
		//		get the nearest DnD area.
		//		Coordinates are basically provided by the <getDragPoint> method.
		// areaList:
		//		a list of DnD areas objects
		// coords:
		//		coordinates [x,y] of the dragItem
		// currentIndexArea:
		//		an index representing the active DnD area
		// returns:
		//		the index of the DnD area

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: getTargetArea");
		var index = 0;
		var x = coords.x;
		var end = areaList.length;
		if(end > 1){
			var start = 0, direction = "right", compute = false;
			if(currentIndexArea == -1 || arguments.length < 3){
				// first time : Need to search the nearest area in all areas.
				compute = true;
			}
			else{
				// check if it's always the same area
				if(this._checkInterval(areaList, currentIndexArea, x)){
					index = currentIndexArea;
				}
				else{
					if(this._oldXPoint < x){
						start = currentIndexArea + 1;
					}
					else{
						start = currentIndexArea - 1;
						end = 0;
						direction = "left";
					}
					compute = true;
				}
			}
			if(compute){
				if(direction === "right"){
					for(var i = start; i < end; i++){
						if(this._checkInterval(areaList, i, x)){
							index = i;
							break;
						}
					}
				}
				else{
					for(var i = start; i >= end; i--){
						if(this._checkInterval(areaList, i, x)){
							index = i;
							break;
						}
					}
				}
			}
		}
		this._oldXPoint = x;
		return index;	// Integer
	},

	_checkInterval: function(/*Array*/areaList, /*Integer*/index, /*Coord*/x){
		// summary:
		//		check if the dragNode is in the interval.
		//		The x coordinate is basically provided by the <getDragPoint> method.
		// areaList:
		//		a list of DnD areas objects
		// index:
		//		index of a DnD area (to get the interval)
		// x:
		//		coordinate x, of the dragNode
		// returns:
		//		true if the dragNode is in intervall
		// tags:
		//		protected
		var coords = areaList[index].coords;
		if(coords.x1 == -1){
			if(x <= coords.x2){
				return true;
			}
		}
		else
			if(coords.x2 == -1){
				if(x > coords.x1){
					return true;
				}
			}
			else{
				if(coords.x1 < x && x <= coords.x2){
					return true;
				}
			}
		return false;	// Boolean
	},

	getDropIndex: function(/*Object*/ targetArea, /*Object*/ coords){
		// summary:
		//		Return the index where the drop has to be placed.
		// targetArea:
		//		a DnD area object
		// coords:
		//		coordinates [x,y] of the draggable item
		// returns:
		// 		a number
		//		or -1 if the area has no children or the drop index represents the last position in to the area

		//console.log("dojox.mdnd.dropMode.VerticalDropMode ::: getDropIndex");
		var length = targetArea.items.length;
		var coordinates = targetArea.coords;
		var y = coords.y;
		if(length > 0){
			// course all children in the target area.
			for(var i = 0; i < length; i++){
				// compare y value with y value of children
				if(y < targetArea.items[i].y){
					return i;	// Integer
				}
				else{
					if(i == length-1){
						return -1;
					}
				}
			}
		}
		return -1;
	},

	destroy: function(){
		//	can be overwritten.
	}
});

//------------
//Singleton
//------------
(function(){
	dojox.mdnd.areaManager()._dropMode = new dojox.mdnd.dropMode.VerticalDropMode();
}());