/**
 * Drag-and-Drop-Manager als Verwaltung
 * 
 * Hier sind alle Draggables und DragTargets verzeichnet
 */
var DragAndDropManager = function()
{
	this.ZuordnungsArray = new Array();
	this.dropTargets     = new Array();
	this.actualDragObject = null;             // 
	this.actualDropTarget = null;             // 
	this.StartDropTarget = null;             // 
//	this.StartDropTargetOrgWidth  = null;             //	 
//	this.StartDropTargetOrgHeight = null;             // 
	this.lastDropTargetWithMouseDown = null;  // Das DropTarget auf dem zuletzt der MouseDown-Event ausgelöst wird merken, für den MouseUp-Event
	this.lastDraggablePosition = null;        // Die letzte Position im DropTarget   
	this.x_onMouseDown = -1;                  // Die x-MausKoordinaten beim onMouseDown   
	this.y_onMouseDown = -1;                  // Die y-MausKoordinaten beim onMouseDown
	
	this.interval_vertical   = null;
	this.interval_horizontal = null;
	
	this.interval_time = 5;						// 
	this.scroll_offset = 20;					// 
	this.scroll_step   = 5;   					// 
};




/*
 * Methoden-deklaration
 */





// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // 
// Event-Methoden - für MouseDown, MouseMove, MouseUp 
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
   

/**
 * Einstiegspunkt
 */
DragAndDropManager.prototype.onMouseDown = function(event)
{
   event = event || window.event;                                  // event überprüfen und IE-kompatibel machen
	
	// kurzschreibweise definieren, damit der Code besser lesbar wird
	var m_ = m_DragAndDropManager;
	
	// Das aktuelle Draggable müsste null sein
	if(m_.actualDragObject)
      return;		
	
	// Koordinaten der Maus abspeichern, um zu später falsche onMouseMove Events auszufiltern
	var mousePos = getMouseCoords(event);
	m_.x_onMouseDown = mousePos.x;
   m_.y_onMouseDown = mousePos.y;
	
    // this zeigt auf das Element auf dem onMouseDown registriert ist, das ist beim MouseDown das HTML-Element names
	// "htmlAreaToDragOn" und beim onmousemove und onmouseup das "document"
	
	// das aktuelle Draggable merken
	m_.actualDragObject = this.parentObject;  
	
	// DropTarget unter der Mouseposition ermitteln und merken
	m_.actualDropTarget = m_.getDropTargetUnderMousePosition(event);
	m_.StartDropTarget = m_.actualDropTarget;

	//
	// Wir merken uns die OrginalGröße vom Container und setzen diese temporär auf feste Werte,
	// damit es nicht zu unbeabsichtigtem Scrollen oder so kommt, wenn übergangsweise das
	// DragObjekt aus dem Container entfernt wird.
	//
	// m_.StartDropTargetOrgWidth = -1;
	// m_.StartDropTargetOrgHeight  = -1;

	if (m_.actualDropTarget && m_.actualDropTarget.htmlElementToDropOn && m_.actualDropTarget.htmlElementToDropOn.style)
	{	//
		// Zuerst bestimmen wir die maximale Höhe aller Droptargets, um sie auf die gleiche Höhe zu bringen,
		// um Draggables bequem zwischen unterschiedlichen Containern hin und herschieben zu können.
		// Bei den Container, die dabei nicht das aktuelle Draggable enthalten, muss dabei dessen Höhe draufgerechnet
		// werden, damit auch genug Platz für das neue Objekt ist, da die Höhe dann ja nicht mehr flexibel ist.
		// !!!ACHTUNG!!! Wir nehmen hier fix an, dass die Container Spaltenweise angeordnet sind!!!
		var maxHeight = 0;
		var dragObjectHeight = getElementHeightValue(m_.actualDragObject.rootElement);
		for(var i = 0; i < m_.dropTargets.length; i++)
		{
			var targetHeight = m_.dropTargets[i].getHeightValue();
			if (m_.dropTargets[i] == m_.actualDropTarget) 
			{
				if (targetHeight > maxHeight) 
					maxHeight = targetHeight;
			}
			else 
			{
				if (targetHeight + dragObjectHeight > maxHeight) 
					maxHeight = targetHeight + dragObjectHeight;
			}
		}
		maxHeight = maxHeight + "px"; // !!!Achtung!!! Einbrennung auf Pixel als Einheit!!!
		
		// Nun setzen wir diese Höhe für alle Container!
		for (var i = 0; i < m_.dropTargets.length; i++) 
		{
			m_.dropTargets[i].setHeight(maxHeight);
		}		
		m_.actualDropTarget.setWidth(m_.actualDropTarget.getWidth());		// Wahrscheinlich überflüssig, die Breite festzusetzen. 
//		m_.StartDropTargetOrgHeight = m_.actualDropTarget.htmlElementToDropOn.style.height;
//		m_.actualDropTarget.htmlElementToDropOn.style.height = getStyleValue (m_.actualDropTarget.htmlElementToDropOn, "height");
//		m_.StartDropTargetOrgWidth = m_.actualDropTarget.htmlElementToDropOn.style.width;
//		m_.actualDropTarget.htmlElementToDropOn.style.width = getStyleValue (m_.actualDropTarget.htmlElementToDropOn, "width");
	}
	
	// Timer zurücksetzen
	clearInterval(m_.interval_vertical);
	clearInterval(m_.interval_horizontal);
	
	
	// den MouseDown-Event zum aktuellen DragObjekt delegieren, welches als Statischer Member im Draggable gespeichert ist.
	m_.actualDragObject.mouseDown(event);         
   
   // letzte Position des Draggables merken	
	if(m_.actualDropTarget)
	{
	   m_.lastDraggablePosition = m_.actualDropTarget.getPosition (m_.actualDragObject);
      
      // zum aktuellen DropTarget den Aufruf delegieren
      m_.actualDropTarget.mouseDown(event, this.parentObject);
		
      // Das Target auf dem zuletzt geklickt wurde für später merken
      m_.lastDropTargetWithMouseDown = m_.actualDropTarget;
	}
	else
	{
	}

	// Events registrieren  
	// das "this" zeigt auf das HTML-Element mit dem der onmousedown-Event verknüpft ist
	document.onmousemove = DragAndDropManager_onmousemove;
	document.onmouseup   = DragAndDropManager_onmouseup;
   
//	m_.printEverythingToFixedDiv();
//	m_.triggerDraggablePrintMethods();
	
	//writeToDocumentBody("DragAndDropManager.prototype.onMouseDown - abgearbeitet ", "ok");
   return false;
};


/**
 * Einstiegspunkt
 */
DragAndDropManager.prototype.onMouseMove = function(event)
{	
   event = event || window.event;                                  // event überprüfen und IE-kompatibel machen
   
	// Schauen ob sich der Mauszeiger seit dem MouseDown-Event wirklich bewegt hat
	var mousePos = getMouseCoords(event); 
	
   if (this.x_onMouseDown == mousePos.x && this.y_onMouseDown == mousePos.y)
	   return;
 	
   // wenn kein DragObjekt bewegt wird, wird die Methode verlassen
   if (!this.actualDragObject)
	   return;
	
   var mouseButton;
	if(navigator.appName == "Microsoft Internet Explorer")
	{
		mouseButton = event.button;
	}
	else
	{
		mouseButton = event.which;
	}

   // Außer beim IE8: Wenn kein MouseButton beim Move gedrückt ist
	if( (navigator.userAgent.indexOf('MSIE 8') < 0) && (mouseButton == 0))
	{
      // setze Draggable zurück an ursprüngliche Position
      this.actualDragObject.bringZurueckZurUrspruenglichenPosition();
		if(this.lastDropTargetWithMouseDown && this.lastDraggablePosition && this.actualDragObject)
         this.lastDropTargetWithMouseDown.insertAt(this.lastDraggablePosition, this.actualDragObject);
		this.actualDragObject = null;
		this.restoreSizeOfStartDropTarget();
      return;
	}
	
  // Scrollen wenn nötig
   this.scrollIfNecessary(event);
   
 	// den MouseMove-Event delegieren zum aktuellen DragObjekt, damit sich dieses bewegt und am Mauspfeil bleibt 
   this.actualDragObject.mouseMove(event);
	
   // DropTarget unter dem Mauspfeil ermitteln	
	var dropTarget = this.getDropTargetUnderMousePosition(event);
	
	// Wenn das ermittelte DropTarget unter der Mouseposition ein anderes ist, als das Letzte. Es kann auch null sein. 
	if(this.actualDropTarget != dropTarget)                               
	{
      if(this.actualDropTarget)
		    this.actualDropTarget.mouseOut(event, this.actualDragObject);
		
		//this.entfernePlatzhalterBeiAllenDropTargets();                    // alle möglichen Platzhalter löschen
		this.actualDropTarget = dropTarget;                               // gefundenes DropTarget merken
		
		// dem DropTarget mitteilen, dass da kein MouseOver mehr ist, quasi ein onLeave-Event
      if(this.actualDropTarget)
		    this.actualDropTarget.mouseEnter(event,this.actualDragObject);
	}
	if(this.actualDropTarget)
	{
	   this.actualDropTarget.mouseMove(event,this.actualDragObject);
	}
	
//	this.printEverythingToFixedDiv();
//	this.triggerDraggablePrintMethods();
	
   return;

};


/**
 * Einstiegspunkt
 */
DragAndDropManager.prototype.onMouseUp = function(event)
{
	event = event || window.event;                                  // event überprüfen und IE-kompatibel machen

	// Timer zurücksetzen
	clearInterval(this.interval_vertical);
	clearInterval(this.interval_horizontal);
		
	// Wenn das aktuelle DragObject existiert 
   if(this.actualDragObject)
   {
		// den MouseUp-Event delegieren zum aktuellen DragObjekt
		this.actualDragObject.mouseUp(event); 
   }
	
	// Wenn das aktuelle DropTarget existiert 
   if(this.actualDropTarget)
   {
      // Das Draggable aus der alten Position löschen
		if(this.lastDropTargetWithMouseDown)
         this.lastDropTargetWithMouseDown.removeDraggable(this.actualDragObject);
      // den MouseUp-Event delegieren zum aktuellen DropTarget
      this.actualDropTarget.mouseUp(event,this.actualDragObject);
   }
	else if(!this.dropTargets || this.dropTargets.length == 0)
	{
		; // do nothing
	}
	else
	{
       // setze Draggable zurück an ursprüngliche Position
		 
		 
		 if(this.lastDropTargetWithMouseDown && this.actualDragObject)
		 {
         this.lastDropTargetWithMouseDown.insertAt(this.lastDraggablePosition, this.actualDragObject);
		 }
       this.actualDragObject.bringZurueckZurUrspruenglichenPosition();
	}
	
	this.restoreSizeOfStartDropTarget();
	
	// aktuelles Draggable löschen
	this.actualDragObject = null;
	
	// Events wieder löschen
	document.onmousemove  = null;
   document.onmouseup    = null;
	
//	this.printEverythingToFixedDiv();
//	this.triggerDraggablePrintMethods();
	
   return;
};





// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // 
// Scroll
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // //

/**
 * Einstiegspunkt
 */
DragAndDropManager.prototype.scrollIfNecessary = function(event)
{
   var scroll_step = this.scroll_step;
	var m_ = this;
                                                                      
   // links
   if(event.clientX < this.scroll_offset && event.clientX < 0)      
   {     
      if ( ! this.interval_vertical)
        this.interval_vertical = setInterval( 
        	function() { 
        			setScrollPosition (getScrollPosition().x - scroll_step , getScrollPosition().y); 
		        	m_.actualDragObject.addPostionOffset(-scroll_step,0);
		        	if( getScrollPosition().x <= 0 )
		        	{ 
		        		clearInterval(m_.interval_vertical);
		        	}
        		}, this.interval_time);
   }  
   // rechts
   else if(event.clientX > window.innerWidth - this.scroll_offset)
   {
      if ( ! this.interval_vertical)
         this.interval_vertical = setInterval( 
         	function() { 
         			setScrollPosition (getScrollPosition().x + scroll_step , getScrollPosition().y ); 
         			m_.actualDragObject.addPostionOffset(scroll_step,0); 
		        	if( getScrollPosition().x >= window.innerWidth )
		        	{ 
		        		clearInterval(m_.interval_vertical);
		        	}      			
         		}, this.interval_time);
   }
   else
   {
      clearInterval(this.interval_vertical);
      this.interval_vertical = null;
   }

            
   // oberhalb
   if(event.clientY < this.scroll_offset)
   {
      if ( ! this.interval_horizontal)
         this.interval_horizontal = setInterval( 
         	function() { 
         			setScrollPosition (getScrollPosition().x, getScrollPosition().y - scroll_step ); 
         			m_.actualDragObject.addPostionOffset(0,-scroll_step); 
		        	if( getScrollPosition().y <= 0 )
		        	{ 
		        		clearInterval(m_.interval_horizontal);
		        	}
         		}, this.interval_time);
   }
   // unterhalb
   else if(event.clientY > window.innerHeight - this.scroll_offset)       
   {
      if ( ! this.interval_horizontal)
         this.interval_horizontal = setInterval( 
         	function() { 
         			setScrollPosition (getScrollPosition().x, getScrollPosition().y + scroll_step ); 
         			m_.actualDragObject.addPostionOffset(0,scroll_step); 
		        	if( getScrollPosition().y >= window.innerHeight )
		        	{ 
		        		clearInterval(m_.interval_horizontal);
		        	}
         		}, this.interval_time);
   }
   else
   {
      clearInterval(this.interval_horizontal);
      this.interval_horizontal = null;
   }
}



// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // 
// Commands
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // //

/**
 * Fügt der Verwaltung vom Manager ein DropTarget hinzu
 */
DragAndDropManager.prototype.addDropTarget = function(dropTarget)
{
	this.dropTargets.push(dropTarget);
   return;
}

/**
 * Fügt der Verwaltung vom Manager ein DropTarget hinzu
 */
DragAndDropManager.prototype.restoreSizeOfStartDropTarget = function()
{
	if (this.StartDropTarget && this.StartDropTarget.htmlElementToDropOn && this.StartDropTarget.htmlElementToDropOn.style)
	{
//		this.StartDropTarget.htmlElementToDropOn.style.height = this.StartDropTargetOrgHeight;
//		this.StartDropTarget.htmlElementToDropOn.style.width = this.StartDropTargetOrgWidth;
		this.StartDropTarget.restoreWidth();
		for (var i = 0; i < this.dropTargets.length; i++) 
		{
			this.dropTargets[i].restoreHeight();
		}		
	}
	this.StartDropTarget = null;
//	this.StartDropTargetOrgHeight = -1;
//	this.StartDropTargetOrgWidth = -1;
   return;
}


/**
 * Hiermit wird eine Zuordnung von Draggable zu DropTarget-Art zu gemacht
 * @param {Object} dropObjekte Array mit DragObjekten
 * @param {Object} zielContainer Array mit ZielContainern
 */ 
DragAndDropManager.prototype.addZuordnung = function(draggables, dropTargets)
{
    this.ZuordnungsArray.push(draggables);    // index 2*n 
    this.ZuordnungsArray.push(dropTargets);  // index 2*n+1
}

/**
 * Löscht alle möglichen Platzhalter, in dem es bei jedem DropTarget die Methode entfernePlatzhalter aufruft
 */ 
DragAndDropManager.prototype.entfernePlatzhalterBeiAllenDropTargets = function()
{
    for (var i = 0; i < m_DragAndDropManager.dropTargets.length; i++)
    {
       m_DragAndDropManager.dropTargets[i].entfernePlatzhalter();
    }
}

/**
 * Fügt dem ersten DropTarget im Manager das übergebene GipsApp an der Position 0 hinzu.
 * @param {Object} gipsApp
 */
DragAndDropManager.prototype.pushGipsAppToFirstDropTarget = function(gipsApp)
{
	if(this.dropTargets[0])
	   this.dropTargets[0].insertAt(0,gipsApp);
//	this.printEverythingToFixedDiv();
//	this.triggerDraggablePrintMethods();
}


/**
 * Diese Funktion ist zum Löschen eine GipsApps. Dazu wird der Löschbefehl weitergeleitet
 * an den Server, um den Eintrag in der DB zu löschen, 
 * an das DropTarget, um alle Referenzen darauf zu entfernen
 * an das Draggable selbst, damit es sich und seine HTML-Elemente löscht
 * 
 * @param {Object} urlZumLoeschen
 * @param {Object} id
 */
DragAndDropManager.prototype.destroyDraggable = function(closeButtonDiv, urlZumLoeschen,id)
{
   // Destroy-Meldung an Server schicken um Datenbankeintrag zu entfernen
   sendRequest(urlZumLoeschen, null);
   
	// Draggable als Objekt holen   
	var draggable = null;
	var element = closeButtonDiv;
	while ((typeof(element)=="object")&&(typeof(element.tagName)!="undefined"))  
	{
		// Suchen bis das von der Klasse Draggable künstlich injizierte Objekt parentObject gefunden wurde
		if(element.parentObject && element.parentObject instanceof Draggable)                
		{
			draggable = element.parentObject;
			break;
		}
		element = element.parentNode;
	}
	
	
	
   // Destroy-Meldung an die DropTargets weiterleiten, um innere Datenstrukturen aufzuräumen
	for(var i = 0; i < this.dropTargets.length; i++)
	{
	   this.dropTargets[i].removeDraggable(draggable);	
	}
	
   
   // Destroy-Meldung um das DragObjekt (und alle seine HTML-Elemente) zu löschen
   draggable.removeHTML();             //
   delete draggable;                   // Objekt abräumen
	
   // Datenstrukturen im Manager aufräumen
	this.ZuordnungsArray.removeObject(draggable); // TODO: wird das ZuordnungsArray überhaupt verwendet? 
	this.actualDragObject = null;             // 
   this.actualDropTarget = null;             // 
   this.lastDropTargetWithMouseDown = null;  // 
   this.lastDraggablePosition = null;        // 
   
//	this.triggerDraggablePrintMethods();
//	this.printEverythingToFixedDiv();
}

 
 
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // 
// Queries
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // //


/**
 *  Gibt das im Manager registrierte DropTarget zurück, welches sich unter der Maus befindet
 */
DragAndDropManager.prototype.getDropTargetUnderMousePosition = function(event)
{

   for(var i = 0 ; i < this.dropTargets.length; i++)
   {
      if(isMouseOverElement(this.dropTargets[i].htmlElementToDropOn,event))
      {
         return this.dropTargets[i];
      }
   }
   return null;
}

/**
 * Gibt das aktuelle DragObject zurück
 */
DragAndDropManager.prototype.getActualDragObject = function()
{
	return this.actualDragObject;	
}


//////////////////////////////////////////////////////////////////////////
    //                                                              //    
    //                        Globaler Member                       //    
    //                                                              //    
//////////////////////////////////////////////////////////////////////////
var m_DragAndDropManager = new DragAndDropManager();

function DragAndDropManager_onmousemove(event)
{
	m_DragAndDropManager.onMouseMove(event);
}
function DragAndDropManager_onmouseup(event)
{
	m_DragAndDropManager.onMouseUp(event);
}



/**
 * Print-Funktion
 */
DragAndDropManager.prototype.printMember = function()
{
	for(var i = 0 ; i < this.ZuordnungsArray.length; i++)
	{
	   console.log("this.ZuordnungsArray[" , i, "]", this.ZuordnungsArray[i]);
	}
	for(var i = 0 ; i < this.dropTargets.length; i++)
	{
	   console.log("this.dropTargets[", i, "]", this.dropTargets[i].htmlElementToDropOn.id);
	}
	
	console.log("actualDragObject",this.actualDragObject);
	console.log("this.actualDropTarget",this.actualDropTarget);
	console.log("this.lastDropTargetWithMouseDown",this.lastDropTargetWithMouseDown);
	console.log("",this.lastDraggablePosition);
}	
	
	


/**
 * 
 */
DragAndDropManager.prototype.printEverythingToFixedDiv = function()
{
   for(var i = 0; i < this.dropTargets.length; i++)
	{
//	   for(var i = 0; i < this.draggables.length; i++)
	   if(this.dropTargets[i].htmlElementToDropOn && this.dropTargets[i].htmlElementToDropOn.id)
		{
         writeKeyValuesToFixedDiv(this.dropTargets[i].htmlElementToDropOn.id + "_Anzahl", this.dropTargets[i].draggables.length );
			
		}
	}
};

DragAndDropManager.prototype.triggerDraggablePrintMethods = function()
{
	for(var i = 0; i < this.dropTargets.length; i++)
	{
		for(var j = 0; j < this.dropTargets[i].draggables.length; j++)
		{
			this.dropTargets[i].draggables[j].printEverythingToDebuggingInfoDiv();
		}
	}
}

