Ext.namespace('Zarafa.hierarchy.ui');

/**
 * @class Zarafa.hierarchy.ui.HierarchyItemDropZone
 * @extends Zarafa.hierarchy.ui.HierarchyTreeDropZone
 *
 * Specialized {@link Ext.tree.TreeDropZone Drop Zone} which
 * is used for dragging and dropping {@link Zarafa.core.data.MAPIRecord messages}
 * in the {@link Zarafa.hierarchy.ui.HierarchyTreePanel Hierarchy Tree}.
 */
Zarafa.hierarchy.ui.HierarchyItemDropZone = Ext.extend(Zarafa.hierarchy.ui.HierarchyTreeDropZone, {

	/**
	 * Called when the currently dragged item is being dropped on a particular node.
	 * This will call {@link #isValidDropPoint} to check if the item can actually
	 * be dropped here, and if so it will {@link #processDrop drop the item}.
	 *
	 * @param {Object} n The object which describes the node over which
	 * the item is being hovered
	 * @param {Ext.grid.GridDragZone} dd The DragZone which is used
	 * for dragging the current item
	 * @param {Ext.EventObject} e The Event Object used for dropping the item
	 * @param {Object} data The data object describing the Grid Items which
	 * are being dragged
	 * @private
	 */
	onNodeDrop : function(n, dd, e, data)
	{
		var point = this.getDropPoint(e, n, dd);
		var targetNode = n.node;
		targetNode.ui.startDrop();
		if (!this.isValidDropPoint(n, point, dd, e, data)) {
			targetNode.ui.endDrop();
			return false;
		}
		// first try to find the drop items
		var dropItem = data.selections;
		return this.processDrop(targetNode, data, point, dd, e, dropItem);
	},

	/**
	 * Called when the currently dragged item is being dropped on the container.
	 * THis will call {@link #isValidDropPoint} to check if the item can actually
	 * be dropped here, and if so it will {@link #processDrop drop the item}.
	 *
	 * @param {Ext.grid.GridDragZone} dd The DragZone which is used
	 * for dragging the current item
	 * @param {Ext.EventObject} e The Event Object used for dropping the item
	 * @param {Object} data The data object describing the Grid Items which
	 * are being dragged
	 * @private
	 */
	onContainerDrop : function(dd, e, data)
	{
		if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
			var targetNode = this.tree.getRootNode();
			targetNode.ui.startDrop();
			var dropItem = data.selections;
			return this.processDrop(targetNode, data, 'append', dd, e, dropItem);
		}
		return false;
	},

	/**
	 * Called when the item is currently being dropped by the user. This will
	 * fire the {@link #beforeitemdrop} event and if the item can be dropped,
	 * then will call {@link #completeDrop} to perform the final step of dropping
	 * the item.
	 *
	 * @param {Ext.tree.TreeNode} target The node on which the item is being dropped
	 * @param {Object} data The data object describing the Grid Items which
	 * are being dragged
	 * @param {String} point The DropPoint (see {@link #getDropPoint}).
	 * @param {Ext.grid.GridDragZone} dd The DragZone which is used
	 * for dragging the current item
	 * @param {Ext.EventObject} e The Event Object used for dropping the item
	 * @param {Ext.data.Record|Array} dropItem The items which are being dropped
	 * @private
	 */
	processDrop: function(target, data, point, dd, e, dropItem)
	{
		var dropEvent = {
			tree : this.tree,
			target: target,
			data: data,
			point: point,
			source: dd,
			rawEvent: e,
			dropItem: dropItem,
			cancel: Ext.isEmpty(dropItem),
			dropStatus: false
		};
		var retval = this.tree.fireEvent("beforeitemdrop", dropEvent);
		if(retval === false || dropEvent.cancel === true || Ext.isEmpty(dropEvent.dropItem)){
			target.ui.endDrop();
			return dropEvent.dropStatus;
		}

		this.completeDrop(dropEvent);
		return true;
	},

	/**
	 * Called by {@link #processDrop} to perform the final step of of dropping the item.
	 * This will call the {@link #itemdrop} event.
	 *
	 * @param {Object} de The dropEvent as generated by {@link #processDrop}.
	 * @private
	 */
	completeDrop : function(de){
		de.target.ui.endDrop();
		this.tree.fireEvent("itemdrop", de);
	},

	/**
	 * Check if the current item which is being dragged can be dropped
	 * over the current node over which the item is hovering.
	 *
	 * This will return false when the dragged records are dragged from
	 * the folder over which we are currently hovering or when the folder
	 * doesn't support messages from the given message class.
	 *
	 * @param {Object} n The object which describes the node over which
	 * the item is being hovered
	 * @param {String} pt The DropPoint (see {@link #getDropPoint}).
	 * @param {Ext.grid.GridDragZone} dd The DragZone which is used
	 * for dragging the current item
	 * @param {Ext.EventObject} e The Event Object used for dropping the item
	 * @param {Object} data The data object describing the Grid Items which
	 * are being dragged
	 * @private
	 */
	isValidDropPoint : function(n, pt, dd, e, data)
	{
		var ret = false;
		if (
			n.node &&
			!Ext.isEmpty(data.selections) &&
			n.node.getFolder().isDropTargetForItems()
		) {
			var folder = n.node.getFolder();
			var record = data.selections[0];

			if (!folder.isIPMSubTree()) {
				var folderEntryId = folder.get('entryid');
				var itemEntryId = record.get('parent_entryid');

				// Check if the new parent equals the old parent
				ret = !Zarafa.core.EntryId.compareEntryIds(folderEntryId, itemEntryId);
				if (ret === true) {
					// If we are hovering over the wastebasket, the item
					// can be dropped
					ret = folder.isSpecialFolder('wastebasket');
					if (ret !== true) {
						var containerClass = folder.get('container_class');
						var messageClass = record.get('message_class');

						// Check if the message class is compatible with the container class.
						ret = Zarafa.core.MessageClass.isContainerClassCompatible(messageClass, containerClass);
					}
				}
			}
		}

		return ret;
	},

	/**
	 * Obtain the DropPoint string ('above', 'append' and 'below') which
	 * indicate at which position we are currently dragging the object
	 * over the current node.
	 *
	 * Items can only be dropped inside a folder, in other words,
	 * it can only append the item to the folder. Hence this function
	 * will always return 'append'.
	 *
	 * @param {Ext.EventObj} e The Event Object used for dragging the item
	 * @param {Object} n The object which describes the node over which
	 * the item is being hovered
	 * @parmam {Ext.tree.TreeDragZone} dd The DragZone which is used
	 * for dragging the current item
	 * @private
	 */
	getDropPoint : function(e, n, dd)
	{
		return 'append';
	}
});
