var Organizer = new Class({

	initialize: function(container, options) {		
		this.containerObj = $(container);		
		if (!this.containerObj) return;			
		this.id = this.containerObj.id;
		
		/**
		 * JSON association NODE ID and object JS
		 */
		/* JSON */ this.jsonList = {};
		
		/**
		 * Order list with nodes IDs
		 */
		/* array */ this.idList = new Array();
		
		/**
		 * Number of items to show
		 */
		/* int */ this.numberItemsToShow = parseInt(options.numberItemsShow);
		
		/**
		 * Indicate if navigation is active
		 */
		/* bool */ this.navigation = options.navigation;
		
		/**
		 * Indicate if navigation is active
		 */
		/* bool */ this.dragAndDrop = options.dragAndDrop;
		
		/**
		 * Actual idx to control pagging (idx of first item on visible items)
		 */
		/* int */ this.actualIdx = 0;
		
		/**
		 * flag to block multiple mouse scroll event
		 */
		/* boolean */ this.evenScrollRun = false;	
		
		/**
		 * Css to identifie drag zone
		 */
		/*String*/this.dropZoneCss = options.dropZoneCss;
								
		var organizerObj = this;		
		if(this.navigation == true)
		{
			//init navigation events
			
			// Scroll UP 		
			var func = function (){	organizerObj.scrollUp();return true; }	
			$(this.id+"Header").addEvent('click', func);											
			//TODO Add suport to drag and onmouseover scroll UP or DOWN
			
			// Scroll DOWN
			func = function (){ organizerObj.scrollDown();return true; }
			$(this.id+"Footer").addEvent('click', func);	
						
			// Mouse scroll event						
			var mouseScrollEvent = function(event)
			{	
				var delta = 0;
				if (!event) /* For IE. */
					event = window.event;
				            
				try
				{
					//verify if is possible to execute event
					if(organizerObj.evenScrollRun == false)
					{	    
				        if (event.wheelDelta) { /* IE/Opera. */
					        delta = event.wheelDelta/120;
					        /** In Opera 9, delta differs in sign as compared to IE.
					         */
					        if (window.opera)
					        	delta = -delta;
				        } else if (event.detail) { /** Mozilla case. */
				             /** In Mozilla, sign of delta is different than in IE.
				              * Also, delta is multiple of 3.
				              */
				             delta = -event.detail/3;
				        }
				        /** If delta is nonzero, handle it.
				         * Basically, delta is now positive if wheel was scrolled up,
				         * and negative, if wheel was scrolled down.
				         */			        
				        organizerObj._moveScroll(delta);			        			        
					}
				}
				catch(err)
				{
					throw "mouseScrollEvent:"+err;
				}	
				 /** Prevent default actions caused by mouse wheel.
		         * That might be ugly, but we handle scrolls somehow
		         * anyway, so don't bother here..
		         */
		        if (event.preventDefault)
		        	event.preventDefault();
				event.returnValue = false;	
			}			
			//add event to items container
			var element = document.getElementById(this.id+"ItemListContainer");				
			if (element.addEventListener) // DOMMouseScroll is for mozilla.
				element.addEventListener('DOMMouseScroll', mouseScrollEvent , false);	
			//IE/Opera.
			element.onmousewheel = element.onmousewheel = mouseScrollEvent;
			
			this._updateArrows();	
		}
		
		
		if(this.dragAndDrop == true)
		{			
			//init frag area				
			var divItems = $(this.id+"ItemListContainer");				
			var optDropzone = {	   	
			    drop: function(el, draggable) {	    
			    	try
			    	{	 
				    	// BEGIN CORECT POSITION
				    	var mouseY = draggable.mouse.now.y;
				    	var actualY = this.getPosition().y;		
				    	var nodeBefore = undefined;		    	
				    	var i, items = this.getChildren();
				    	for(i = 0; i < items.length; i++)
				    	{
							itemNode = items[i];    			
						    var y = itemNode.getSize().size.y;	    			
			    			var nextMiddleY = actualY + (y / 2);    			
			    			if(mouseY < nextMiddleY)
			    			{
			    				nodeBefore = itemNode;    	
			    				break;
			    			}    			
			    			actualY += y;	
				    	}		    		        
				        //this.removeClass("dragenter");      		        
				        if(nodeBefore != undefined)
							el.injectBefore(nodeBefore);			
				        else		        
				            el.injectInside(this);	        
				        
				        el.setStyles({ left: 0+"px", top: 0+"px"});//, width: "auto"
			    	}
			    	catch(e)
			    	{
			    		//if(console)
			    		//	console.debug("DROP ERROR:" + e);
			    	}			    	 
			    	organizerObj.dropSelfElement( el.id );			    	
			    }	 
			}			
			divItems.addEvents(optDropzone);	
		}
	},		
	
	/**
	 * Add html node that represent one item, and update the list of items.
	 * @param html - html string that represent a node (id requered)
	 * @param JS object associated with html node
	 */	
	addItemHtml : function (/* String */ html, /* Object */ jsObject)
	{
		if(html == undefined)
			throw "["+this.id+"].addItem: html not valid";
			
		var nodes = this._createNodesFromText(html);																				
		if(nodes.length > 0)		
			this.addItemNode(nodes[0], jsObject);		
		else
			throw "["+this.id+"].addItemHtml: HTML is not valid";	
	},
	
	_createNodesFromText : function (text)
	{
		var elem = document.createElement("div");
		elem.style.display= "none";
		document.body.appendChild(elem);
		elem.innerHTML = text;
		var elems = $$(elem.childNodes);
		document.body.removeChild(elem);
		return elems;
	},
	
	/**
	 * Add one node that represent one item, and update the list of items.
	 * @param node - DOM node (id requered)
	 * @param object associated with DOM node
	 */	
	addItemNode : function (/* DOM */ node, /* Object */ jsObject)
	{
		try
		{	
			if(node.id == undefined)
				throw "["+this.id+"].addItem: Node don't have id";
			
			//verify if new already exist					
			if(this._getElementPosition(node.id) != undefined)											
				return false;	
			
			//add object list
			this.jsonList[node.id] = jsObject;					
			this.idList[this.idList.length] = node.id;
			
			//verify if item is visible or not
			if( !this._itemIsVisible(this.idList.length - 1) )
				node.style.display = "none";
			
			//append child			
			var itemsContainer = $(this.id+"ItemListContainer");
			itemsContainer.appendChild(node);
			
			if(this.dragAndDrop == true)
			{	
				var optDraggable;
				
				if(this.dropZoneCss == undefined)
				{					
					optDraggable = {										    
						onBeforeStart: function(draggable) {	    	
							draggable.setOpacity(0.5).setStyle("z-index", 1);
						},
					    onComplete: function(draggable) {
					    	draggable.setOpacity(1.0).setStyle("z-index", 0);
					        draggable.setStyles({ left: 0+"px", top: 0+"px", width: "auto"});
					    }
					};	
				}	
				else
				{					
					optDraggable = {
						handle: node.getElementsBySelector('.' + this.dropZoneCss)[0],
						onBeforeStart: function(draggable) {	    	
							draggable.setOpacity(0.5).setStyle("z-index", 1);
						},
					    onComplete: function(draggable) {
					    	draggable.setOpacity(1.0).setStyle("z-index", 0);
					        draggable.setStyles({ left: 0+"px", top: 0+"px", width: "auto"});
					    }
					}
				}
				//set 
				var regions = new Array();
				regions.push($(this.id+"ItemListContainer"));
				optDraggable.droppables = regions;				
				node.makeDraggable(optDraggable);
			}	
			
			this._updateArrows();					
						
			return true;				
		}
		catch(err)
		{
			throw "["+this.id+"].addItemNode: "+err;
		}				
	},
	
	/**
	 * Verify if item is visible
	 * @param itemIdx - item index
	 * @return true if is visible. false not visible
	 */
	_itemIsVisible : function (/* int */itemIdx)
	{		
		return (this.navigation == false) ||
		 	(itemIdx >= this.actualIdx && itemIdx < (this.actualIdx + this.numberItemsToShow));
	},
	
	/**
	* Remove new. If position is undefined this was searched on array
	* @param newObj - object that represent one new
	* @param newPosition - position to remove new (is not required)
	* @return true if new is removed
	*/
	removeItemByNodeId : function (/* String */ nodeId)
	{		
		try
		{		
			if(nodeId == undefined)
				return false;
				
			if(this.jsonList[nodeId] == undefined)
				return false;
				
			var itemPos = this._getElementPosition(nodeId);				
			if( this._removeElemIdByPos( itemPos ) == true )
			{
				this.jsonList[nodeId] = undefined;
				
				//remove html node
				var itemsContainer = document.getElementById(this.id+"ItemListContainer");
				itemsContainer.removeChild( document.getElementById(nodeId) );
				
				this._refreshItemList();
				
				return true;				
			}			
		}		
		catch(err)
		{
			throw "["+this.id+"].removeItemByNodeId: " + err;
		}
		return false;
	},
	
	/**
	 * Refresh list of visible items
	 */
	_refreshItemList : function()
	{
		if(this.navigation == true)
		{
			//show all visible items
			var start = 0;
			var end = 0;
			if(this.numberItemsToShow > this.idList.length)
			{			
				start = 0;
				end = this.idList.length;
			}
			else
			{			
				var idx = this.actualIdx + this.numberItemsToShow;						
				if( idx <  this.idList.length )
				{
					start = this.actualIdx;
					end = idx;
				}
				else
				{
					start = this.idList.length - this.numberItemsToShow;
					end = start + this.numberItemsToShow;
				}
			}
			//save actual index
			this.actualIdx = start;
			var i, elem;
			for(i = start; i < end; i++)							
				document.getElementById(this.idList[i]).style.display = "block";
		}
	},
	
	/**
	 * Method call when one item is droped on drop area.
	 * Get position on container and change position on array.
	 * @param elemId - DOM node ID
	 */
	dropSelfElement : function ( /* string */ elemId)
	{		
		try
		{
			var itemsContainer = document.getElementById(this.id+"ItemListContainer");
			var elemPos = this._getNodePosition( itemsContainer.childNodes, elemId, 1); //1 = type DIV
			
			var elemPosAux = this._getElementPosition(elemId);			
			if(elemPosAux == undefined)			
				return;
				
			var elemId = this.idList[elemPosAux]; 
			
			this._removeElemIdByPos(elemPosAux);
			this._addItemId(elemId, parseInt(elemPos));	
		}
		catch(err)
		{
			throw "["+this.id+"].dropSelfElement: " + err;
		}
	},	
	
	/**
	 * Add one item at givem array position. 
	 * If position is not defined or not valid, the item is not add.
	 * @param elemId - node id
	 * @param position - position to add item
	 * @return true if item is add. False not add
	 */
	_addItemId : function ( /* String */ elemId,  /* integer */ position)
	{
		try
		{			
			if(position == undefined || position < 0)
				return false;
				
			if(!this.idList[position] == undefined)					
				this.idList[position] = elemId;					
			else
			{
				//get last position
				var pos = 0;	
				while(this.idList[pos] != undefined){ pos++; }				
				//add one position for all news greaters than  position	
				while(pos > position)
				{
					this.idList[pos] = this.idList[pos-1];				
					pos--;
				}
				//add new portlet
				this.idList[position] = elemId;
			}
			return true;
		}
		catch(err)
		{
			throw "["+this.id+"]._addItemId: "+err;
		}
		return false;
	},
	
	/**
	* Remove item by node id. 
	* @param elemId - Node id.
	* @return true if item is removed
	*/
	_removeElemIdById : function (/* String */ elemId)
	{
		try
		{		
			return this._removeElemIdByPos( this._getElementPosition(elemId) );						
		}
		catch(err)
		{
			throw "["+this.id+"]._removeElemIdById: " + err;
		}
		return false;
	},
	
	/**
	* Remove item by node position.  	
	* @param elemPosition - position to remove item 
	* @return true if ittem is removed
	*/
	_removeElemIdByPos : function (/* int */ elemPosition)
	{
		try
		{		
			if(elemPosition == undefined)
				return;
								
			while(this.idList[elemPosition] != undefined)
			{									
				this.idList[elemPosition] = this.idList[elemPosition + 1];				
				elemPosition++;
			}
			
			//put correct lenght
			this.idList.length = elemPosition - 1;	
			
			return true;				
		}
		catch(err)
		{
			throw "["+this.id+"]._removeElemIdByPos: " + err;
		}
		return false;
	},
	
	/**
	 * Search item id on array of ids, by ID and return the position
	 * @param id - item identification
	 * @return item position (int). Undefined if not finded
	 */
	_getElementPosition : function (/* String */ id)
	{
		try
		{
			var i = 0;
			while(this.idList[i] != undefined)
			{	
				if(this.idList[i] == id)		
					return i;										
				i++;
			}					
		}
		catch(err)
		{
			throw "["+this.id+"]._getElementPosition:"+err;
		}	
		return undefined;
	},
	
	/**
	 * Verify if a item exist on list of events.
	 * @param id - item identification
	 * @return true if exist. False not exist
	 */
	isElementExist : function (/* String */ id)
	{
		return this.jsonList[id] != undefined;
	},
	
	/**
	 * Get a JS object by id.
	 * @param id - item identification
	 * @return JS object. Undefined if not exist
	 */
	getJSObject : function (/* String */ id)
	{
		return this.jsonList[id];
	},
	
	/**
	 * Get the item position of container, by element ID
	 * @param containerChildNodes - list of nodes container
	 * @param ElemID - Element ID
	 * @param nodeType - type of node to search
	 * @return item position (int). if not find return undefined
	 */	 
	_getNodePosition : function(/* node Array */ containerChildNodes, /* String */ ElemID, /* int */ nodeType)
	{
		try
		{
			var /* int */ i, /* int */ pos = 0, /* DOM Element */ nodeDiv;
			//iterate all divs for search divID		
			for(i = 0; i < containerChildNodes.length; i++)
			{
				//get layout node
				nodeDiv = containerChildNodes[i];			
		
				if(nodeDiv.nodeType == nodeType)
				{				
					if(nodeDiv.id == ElemID)					
						return pos;				
					pos++;
				}			
			}
		}
		catch(err)
		{
			throw "["+this.id+"]._getNodePosition: " + err;
		}
		return undefined;//not find
	},
	
	clear : function()
	{		
		try
		{
			var i;
			for(i = 0; i < this.idList.length; i++)
			{				
				this.removeItemByNodeId( this.idList[i] );
			}
		}
		catch(err)
		{
			throw "["+this.id+"].clear: " + err;
		}		
	},
	
	/**
	 * Return ordered array with the all JS object
	 */
	getSortedJSObjects : function()
	{
		var objs = new Array();	
		try
		{
			var i;
			for(i = 0; i < this.idList.length; i++)
			{
				objs[objs.length] = this.jsonList[ this.idList[i] ];
			}
		}
		catch(err)
		{
			throw "["+this.id+"].getSortedJSObjects: " + err;
		}		
		return objs;
	},
	
	/**
	 * Return ordered array with the all elems ids
	 */
	getSortedIds : function()
	{
		var objs = new Array();	
		try
		{
			var i;
			for(i = 0; i < this.idList.length; i++)
			{
				objs[objs.length] = this.idList[i];
			}
		}
		catch(err)
		{
			throw "["+this.id+"].getSortedIds: " + err;
		}		
		return objs;		
	},
		
	/**
	* Method called to scroll/move down (navigation). 
	*/
	scrollDown : function ()
	{		
		try
		{	
			var idx = this.actualIdx + this.numberItemsToShow;			
			if( idx <  this.idList.length )
			{
				//hidden first item
				var elemDom = document.getElementById( this.idList[this.actualIdx] );				
				elemDom.style.display = "none";
				
				//show last item
				elemDom = document.getElementById( this.idList[idx] );				
				elemDom.style.display = "block";								
				
				this.actualIdx++;
				
				//update arrows
				this._updateArrows();
			}													
		}
		catch(err)
		{
			throw "["+this.id+"].scrollDown: " + err;
		}		
	},		
		
	/**
	* Method called to scroll/move up (navigation). 
	*/
	scrollUp : function ()
	{		
		try
		{
			var idx = this.actualIdx - 1;
			if( idx >= 0 )
			{
				this.actualIdx--;
				
				//hidden last item
				elemDom = document.getElementById( this.idList[this.actualIdx + this.numberItemsToShow] );				
				elemDom.style.display = "none";	
				
				//show first item
				var elemDom = document.getElementById( this.idList[idx] );				
				elemDom.style.display = "block";
				
				//update arrows
				this._updateArrows();
			}		
		}
		catch(err)
		{
			throw "["+this.id+"].scrollUp:"+err;
		}				
	}, 
	
	/**
	 * Move/scroll navigation depending on delta.
	 * If delta > 0 scroll/move Up
 	 * If delta < 0 scroll/move Down
	 */
	_moveScroll : function(/* int */ delta)
	{
		try
		{
			//verify if is possible to execute event
			if(delta != undefined && this.evenScrollRun == false 
				&& this.navigation == true && this.idList.length > this.numberItemsToShow)
			{						        	
	    		//block the event access 
				this.evenScrollRun = true;	        		
	    		
	    		var organizerObj = this;
	    		var funcMove = function()
	    		{
	    			try
	    			{
		        		//move items UP delta > 0 or DOWN if delta < 0
		        		if(delta < 0)					        		
		        			organizerObj.scrollDown();
		        		else if (delta > 0)
		        			organizerObj.scrollUp();
	    			}
	    			catch(err)
	    			{
	    				throw "["+this.id+"]._moveScroll.funcMove:"+err;
	    			}	
	    			
	        		//unblock acess of event mouse
	        		organizerObj.evenScrollRun = false;
	    		}	
	    		
	    		var t=setTimeout(funcMove, 50);        	
			}
		}
		catch(err)
		{
			throw "["+this.id+"]._moveScroll:"+err;
		}	
	},		
	
	/**
	 * Update the state of arrows (UP and DOWN)	
	 */
	_updateArrows : function ()
	{
		try
		{
			if(this.navigation == true)
			{
				this._showArrow( ((this.actualIdx + this.numberItemsToShow) <  this.idList.length), 
									this.id+"ImgDown");
				this._showArrow(this.actualIdx > 0, this.id+"ImgUp");	
			}
		}
		catch(err)
		{
			throw "["+this.id+"]._updateArrows:"+err;
		}
	},
	
	/**
	 * Show one arrow 
	 * @param show - true to show arrow, false to hidden
	 * @param elemID - element ID
	 */
	_showArrow : function (/* boolean */ show, /* string */ elemID)
	{
		try
		{
			var elem = document.getElementById(elemID);
			if(show == true)			
				elem.style.visibility = "visible";			
			else
				elem.style.visibility = "hidden";
		}
		catch(err)
		{
			throw "["+this.id+"]._showArrow:"+err;
		}
	},
	
	// TODO ADD SUPPORT TO USE NAVIGATION WITH ITEM DRAG
	mouseOverUp : function()
	{		
		//this._moveScroll(1);
	},
	
	mouseOverDown : function()
	{	
		//this._moveScroll(-1);
	},
	
	mouseOut : function()
	{
		//this.mouseOverOn = false;
	}
	
	
});





