define([
	"dojo/_base/kernel",
	"dojo/_base/declare",
	"dojo/_base/array",
	"dojo/dom-geometry",
	"dojox/mdnd/AreaManager"
],function(dojo, declare, array, geom){
	var vdm = 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 = geom.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 = geom.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");
			array.forEach(area.items, function(obj){
				//get the vertical middle of the item
				var node = obj.item.node;
				var position = geom.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
	//------------
	dojox.mdnd.areaManager()._dropMode = new dojox.mdnd.dropMode.VerticalDropMode();
	return vdm;
});
