/*
 * Copyright (C) 2005 - 2011 Jaspersoft Corporation. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
dynamicTree.Tree.
        addVar('NODE_PATTERN', ".node").                //node itself
        addVar('NODE_WRAPPER_PATTERN', ".node > .wrap").
        addVar('NODE_ICON_PATTERN', ".node > .wrap .icon").
        addVar('NODE_CUSTOM_PATTERNS', []);

dynamicTree.Tree.
        addVar('LEAF_PATTERN', ".leaf").                //leaf itself
        addVar('LEAF_WRAPPER_PATTERN', ".leaf > .wrap").
        addVar('LEAF_ICON_PATTERN', ".leaf > .wrap .icon").
        addVar('LEAF_CUSTOM_PATTERNS', []);

dynamicTree.Tree.addVar('draggables', []);
/**
 *
 */
dynamicTree.Tree.addMethod('getTreeNodeByEvent', function(event) {
    var element = Event.element(event); //event.originalTarget || event.srcElement;
    return this.getTreeNodeByElement(element);
});
dynamicTree.Tree.addMethod('getTreeNodeByElement', function(element) {
    while(element && element.readAttribute && element.readAttribute('id') !== this.id) {
        var node = element.treeNode;
        if (node && node.getTreeId() === this.getId()) {
            return node;
        } else {
            element = $(element.parentNode);
        }
    }
    return null;
});
/**
 *
 */
dynamicTree.Tree.addMethod('isNodeEvent', function(event) {
    //var element = Event.element(event);//event.originalTarget || event.srcElement;

    //var patterns = [this.NODE_PATTERN, this.NODE_WRAPPER_PATTERN, this.NODE_ICON_PATTERN];
    //if (isArray(this.NODE_CUSTOM_PATTERNS)) { patterns = patterns.concat(this.NODE_CUSTOM_PATTERNS); }

    var listItem = matchMeOrUp(event.element().parentNode,'LI');
    return listItem && listItem.hasClassName(layoutModule.NODE_CLASS);
});

/**
 *
 */
dynamicTree.Tree.addMethod('isLeafEvent', function(event) {
//    var element = Event.element(event);//event.originalTarget || event.srcElement;
//
//    var patterns = [this.LEAF_PATTERN, this.LEAF_WRAPPER_PATTERN, this.LEAF_ICON_PATTERN];
//    if (isArray(this.LEAF_CUSTOM_PATTERNS)) { patterns = patterns.concat(this.LEAF_CUSTOM_PATTERNS); }
//
//    return matchAny(element, patterns) != null;

    var listItem = matchMeOrUp(event.element().parentNode,'LI');
    return listItem && listItem.hasClassName(layoutModule.LEAF_CLASS);
});
/**
 *
 */
dynamicTree.Tree.addMethod('isIconEvent', function(event) {
//    var element = Event.element(event);//event.originalTarget || event.srcElement;
//
//    return matchAny(element,[this.NODE_ICON_PATTERN, this.LEAF_ICON_PATTERN], true) != null;
    return event.element().hasClassName(layoutModule.ICON_CLASS);
});
/**
 *
 */
dynamicTree.Tree.addMethod('_registerEvents', function() {
    this._cleanUpListeners();
    this._registerClickEvents();
    this._registerCustomEvents(); // focus and blur
    this._registerKeyEvents();
    this._registerMouseEvents();
});
/**
 *
 */
dynamicTree.Tree.addMethod('_cleanUpListeners', function() {
    var treeContainer = this._getElement();

    treeContainer.stopObserving('click');
    treeContainer.stopObserving('dblclick');

    if (isSupportsTouch()) {
        treeContainer.stopObserving('touchstart');
        treeContainer.stopObserving('drag:touchstart');
        treeContainer.stopObserving('touchend');
    } else {
        treeContainer.stopObserving('mousedown');
        treeContainer.stopObserving('drag:mousedown');
        treeContainer.stopObserving('mouseup');
    }
    treeContainer.stopObserving('mouseover');
    treeContainer.stopObserving('mouseout');
});
/**
 *
 */
dynamicTree.Tree.addMethod('_registerClickEvents', function() {
    var treeContainer = this._getElement();

    treeContainer.observe('click', function(event) {
        var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
        if (!node) {return;}

        var isIcon = this.isIconEvent(event);
        var isNode = this.isNodeEvent(event);
        var isLeaf = this.isLeafEvent(event);

        if (isNode) {
            isIcon && treeContainer.fire('nodeIcon:click', {targetEvent: event, node: node});
            treeContainer.fire('node:click', {targetEvent: event, node: node});
        } else if (isLeaf) {
            isIcon && treeContainer.fire('nodeIcon:click', {targetEvent: event, node: node});
            treeContainer.fire('leaf:click', {targetEvent: event, node: node});
        }

        if (!isIPad() && isNode && isIcon) {
            node.handleNode(event);
        }

    }.bindAsEventListener(this));

    treeContainer.observe('dblclick', function(event) {
        var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
        if (!node) {return;}

        var isIcon = this.isIconEvent(event);
        var isNode = this.isNodeEvent(event);
        var isLeaf = this.isLeafEvent(event);

        if (isNode) {
            isIcon && treeContainer.fire('nodeIcon:dblclick', {targetEvent: event, node: node});
            treeContainer.fire('node:dblclick', {targetEvent: event, node: node})
        } else if (isLeaf) {
            isIcon && treeContainer.fire('leafIcon:dblclick', {targetEvent: event, node: node});
            treeContainer.fire('leaf:dblclick', {targetEvent: event, node: node})
        }

        if (this.handleNodeOnDblclick && (isNode || isLeaf) && !isIcon) {
            node.handleNode(event);
        }
    }.bindAsEventListener(this));

});

dynamicTree.Tree.addMethod('_registerMouseEvents', function() {
    var treeContainer = this._getElement();
    treeContainer.observe(isSupportsTouch() ? 'touchstart' : 'mousedown', function(event) {
    	var element = event.element();

        var node = matchMeOrUp(element, layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
        if (!node) return;
        
        event.treeEvent = true;
        
        var isIcon = this.isIconEvent(event);
        var isNode = this.isNodeEvent(event);
        var isLeaf = this.isLeafEvent(event);
        
        if(isIPad()){
        	if(!JRS.vars.ajax_in_progress){
	            this.twofingers = false;
	            if(event.touches.length == 2) {
	            	this.twofingers = true;
	            	if(node.isSelected() || (designerBase && designerBase.isInSelection(node))){
		            	var li = jQuery(element).parents('li:first');     
		            	li.hasClass('selected') && document.fire(layoutModule.ELEMENT_CONTEXTMENU, {targetEvent: event, node: element});
		            	return;   	            		
	            	}     			
	            }  
        	} else {
            	alert('please wait');
            }
        } 
        
        if(!isIPad() || !JRS.vars.ajax_in_progress) {
            if (this.selectOnMousedown && (!isSupportsTouch() || event.touches.length == 1)) {
                this._selectOrEditNode(event, node, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
            }

            if (isNode) {
                isIcon && treeContainer.fire('nodeIcon:mousedown', {targetEvent: event, node: node});
                treeContainer.fire('node:mousedown', {targetEvent: event, node: node})
            } else if (isLeaf) {
                isIcon && treeContainer.fire('leafIcon:mousedown', {targetEvent: event, node: node});
                treeContainer.fire('leaf:mousedown', {targetEvent: event, node: node})
            }        	
        }

    }.bindAsEventListener(this));

    //scriptaculous stopped mousedown event but we made it throw this instead
    treeContainer.observe(isSupportsTouch() ? 'drag:touchstart' :'drag:mousedown', function(e) {
        var event = e.memo.targetEvent;
       
        var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
        if (!node) {return;}

        event.treeEvent = true;

        var isIcon = this.isIconEvent(event), isNode = this.isNodeEvent(event), isLeaf = this.isLeafEvent(event);

        if (this.selectOnMousedown && !isRightClick(event)) {
            this._selectOrEditNode(event, node, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
        }

        //var eventNames = [];

        if (isNode) {
            isIcon && treeContainer.fire('nodeIcon.drag:mousedown', {targetEvent: event, node: node});
            treeContainer.fire('node.drag:mousedown', {targetEvent: event, node: node})
        } else if (isLeaf) {
            isIcon && treeContainer.fire('leafIcon.drag:mousedown', {targetEvent: event, node: node});
            treeContainer.fire('leaf.drag:mousedown', {targetEvent: event, node: node})
        }

    }.bindAsEventListener(this));

    treeContainer.observe(isSupportsTouch() ? 'touchend' : 'mouseup', function(event) {
        var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
        if (!node) {return;}

        event.treeEvent = true;

        var isIcon = this.isIconEvent(event), isNode = this.isNodeEvent(event), isLeaf = this.isLeafEvent(event);

        if(isIPad() && !this.twofingers) {
	    	if(this.clickid == node.id) {
	    		if(!JRS.vars.ajax_in_progress && event.timeStamp - this.clicktime < 700) {
	    			if (isNode) {
	    	            isIcon && treeContainer.fire('nodeIcon:dblclick', {targetEvent: event, node: node});
	    	            treeContainer.fire('node:dblclick', {targetEvent: event, node: node})
	    	        } else if (isLeaf) {
	    	            isIcon && treeContainer.fire('leafIcon:dblclick', {targetEvent: event, node: node});
	    	            treeContainer.fire('leaf:dblclick', {targetEvent: event, node: node})
	    	        }
	
	    	        if (this.handleNodeOnDblclick && (isNode || isLeaf) && !isIcon) {
	    	            node.handleNode(event);
	    	        }	    				
	    		}
	    	}
	        this.clicktime = event.timeStamp;
	        this.clickid = node.id;
    	}
        if(!isIPad() || !JRS.vars.ajax_in_progress) {
        	if (!this.selectOnMousedown && !TouchController.element_scrolled && (!isSupportsTouch() || event.changedTouches.length == 1)) {
                this._selectOrEditNode(event, node, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
            }
            this._deselectOthers(event, node, isMetaHeld(event), isShiftHeld(event), isRightClick(event));

            var eventNames = [];

            if (isNode) {
                isIcon && treeContainer.fire('nodeIcon:mouseup', {targetEvent: event, node: node});
                treeContainer.fire('node:mouseup', {targetEvent: event, node: node})
            } else if (isLeaf) {
                isIcon && treeContainer.fire('leafIcon:mouseup', {targetEvent: event, node: node});
                treeContainer.fire('leaf:mouseup', {targetEvent: event, node: node})
            }
            
            if (isIPad() && isNode && isIcon && !TouchController.element_scrolled) {
                node.handleNode(event);
            }
        }
        
    }.bindAsEventListener(this));

    if ('createTouch' in document) {
    	/*_*\ 
        document.observe('touchmove', function(e) {
            var event = e.targetEvent ? e.targetEvent : e;
            var touch =  event.changedTouches[0];
            var element = $(document.elementFromPoint(touch.pageX, touch.pageY));
            var node = matchMeOrUp(element, layoutModule.BUTTON_PATTERN) && this.getTreeNodeByElement(element);
            if (node) {
                var draggable = this.createDraggableIfNeeded(event, node);
                draggable && draggable.initDrag(event);
                if (node != treeContainer.lastHoveredNode) {
                    treeContainer.lastHoveredNode && treeContainer.fire('tree:mouseout', { targetEvent: event, node: node });
                    treeContainer.lastHoveredNode = node;
                    treeContainer.fire('tree:mouseover', {targetEvent: event, node: node});
                }
            }
        }.bindAsEventListener(this));
        */
    } else {
        treeContainer.observe('mouseover', function(event) {
            var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
            if (!node) {return;}
            this.createDraggableIfNeeded(event, node);
            !isIE7() && treeContainer.fire('tree:mouseover', {targetEvent: event, node: node});
        }.bindAsEventListener(this));

        if (!isIE7()) {
            treeContainer.observe('mouseout', function(event){
                var node = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getTreeNodeByEvent(event);
                if (!node) {return;}
                treeContainer.fire('tree:mouseout', {
                    targetEvent: event,
                    node: node
                });
            }.bindAsEventListener(this));
        }
    }

    //register default drop zone on tree
    Droppables.add(treeContainer,{
        accept: this.dropClasses,
        onDrop: (function(dragging){
            this.elementDropped = dragging;
        }).bind(this)
    });
});

dynamicTree.Tree.addMethod('_registerKeyEvents', function() {
    var treeContainer = this._getElement();

    treeContainer.observe('key:down', function(event) {
        var node = this.getTreeNodeByEvent(event);
        if (dynamicTree.treeNodeEdited === node) {
            return;
        }
        node && this._selectNextNode(node, event.memo.targetEvent);
    }.bindAsEventListener(this));

    treeContainer.observe('key:up', function(event) {
        var node = this.getTreeNodeByEvent(event);
        if (dynamicTree.treeNodeEdited === node) {
            return;
        }
        node && this._selectPreviousNode(node, event.memo.targetEvent);
    }.bindAsEventListener(this));

    treeContainer.observe('key:right', function(event) {
        var node = this.getTreeNodeByEvent(event);
        if (dynamicTree.treeNodeEdited === node) {
            return;
        }
        node && this._selectInwards(node, event.memo.targetEvent);
    }.bindAsEventListener(this));

    treeContainer.observe('key:left', function(event) {
        var node = this.getTreeNodeByEvent(event);
        if (dynamicTree.treeNodeEdited === node) {
            return;
        }
        node && this._selectOutwards(node, event.memo.targetEvent);
    }.bindAsEventListener(this));
});


dynamicTree.Tree.addMethod('_registerCustomEvents', function() {
    var treeContainer = this._getElement();

    treeContainer.observe('mousedown', function(event) {
        if (!dynamicTree.activeTreeId || dynamicTree.activeTreeId !== this.getId()) {
            treeContainer.fire('tree:blur', {targetEvent: event, tree: dynamicTree.getActiveTree()});

            dynamicTree.activeTreeId = this.getId();
            treeContainer.fire('tree:focus', {targetEvent: event, tree: dynamicTree.getActiveTree()});
        }
    }.bindAsEventListener(this));

    treeContainer.observe('mouseover', function(event) {
        var node = this.getTreeNodeByEvent(event);

        if (!node) {
            treeContainer.fire('tree:mouseover', {targetEvent: event, tree: this});
        }
    }.bindAsEventListener(this));

    treeContainer.observe('mouseout', function(event) {
        var node = this.getTreeNodeByEvent(event);

        if (!node) {
            treeContainer.fire('tree:mouseout', {targetEvent: event, tree: this});
        }
    }.bindAsEventListener(this));
});


dynamicTree.Tree.addMethod('fireOpenEvent', function(node, event) {
    var treeContainer = this._getElement();
    treeContainer.fire('node:open', {node: node, targetEvent: event});
});

dynamicTree.Tree.addMethod('fireSelectEvent', function(node, event) {
    var treeContainer = this._getElement();
    treeContainer.fire(node.isParent() ? 'node:selected' : 'leaf:selected', {node: node, targetEvent: event});
});


dynamicTree.Tree.addMethod('fireUnSelectEvent', function(node, event) {
    var treeContainer = this._getElement();
    treeContainer.fire(node.isParent() ? 'node:unselected' : 'leaf:unselected', {node: node, targetEvent: event});
});


dynamicTree.Tree.addMethod('fireUnSelectAllEvent', function(event) {
    var treeContainer = this._getElement();
    treeContainer.fire('items:unselected', {targetEvent: event});
});

dynamicTree.Tree.addMethod('fireEditEvent', function(node, newVal) {
    var treeContainer = this._getElement();
    treeContainer.fire(node.isParent() ? 'node:edit' : 'leaf:edit', {node: node, newValue: newVal})
});

dynamicTree.Tree.addMethod('fireStartEditEvent', function(node, input) {
    var treeContainer = this._getElement();
    treeContainer.fire(node.isParent() ? 'node:startEdit' : 'leaf:startEdit', {node: node, input: input})
});

dynamicTree.Tree.addMethod('fireEndEditEvent', function(node) {
    var treeContainer = this._getElement();
    treeContainer.fire(node.isParent() ? 'node:endEdit' : 'leaf:endEdit', {node: node})
});
/**
 *
 */
dynamicTree.Tree.addMethod('observe', function(eventName, handler) {
    this._getElement().observe(eventName, handler);
});
/**
 *
 */
dynamicTree.Tree.addMethod('stopObserving', function(eventName, handler) {
    this._getElement().stopObserving(eventName, handler);
});
dynamicTree.Tree.addMethod('createDraggableIfNeeded', function(event, node) {
    //make draggable - test in this order - for efficiency
    //test 1) does the tree have any drag patterns?
    //test 2) is a draggable already created for the clicked element?
    //test 3) does clicked element or its ancestors match any draggable patterns?
    //test 4) is a draggable already created for the clicked element or matching ancestor?
    var thisElem = event ? event.element() : node._getElement().down(this.dragPattern);
    if (this.dragPattern && !this.draggables[thisElem.identify()]) {
        var matchingElem = matchAny(thisElem, [this.dragPattern], true);
        if (matchingElem && !this.draggables[matchingElem.identify()]) {
            this.draggables[matchingElem.identify()] =
            new Draggable(thisElem, {
                superghosting: true,
                mouseOffset: true,
                delay: (isIE() || isSupportsTouch() ? 200 : 0),
                onStart: this.setDragStartState.bind(this, node),
                onEnd: this.setDragEndState.bind(this, node)
            });

            return this.draggables[matchingElem.identify()];
        }
    }

    return null;
});

dynamicTree.Tree.addMethod('setDragStartState', function(node, draggable, event) {
    var templateClassName = node._getElement().templateClassName;
    if (templateClassName) { draggable.element.addClassName(templateClassName); }

    draggable.element.setStyle({width: null, height: null});
    draggable.element.addClassName(layoutModule.DRAGGING_CLASS).addClassName(this.getId());
    // Customize draggable style
    if (this.dragClasses) {
        draggable.element.addClassName(this.dragClasses);
    }

    if (this.selectedNodes.length > 1) {
        draggable.element.update(new Template(this.TREE_NN_ITEMS_SELECTED).evaluate({count: this.selectedNodes.length}));
    }

    draggable.element.node = node;
    draggable.element.nodes = this.selectedNodes;

    draggable.options.scroll = this._getElement();
    draggable.options.scrollSensitivity = layoutModule.SCROLL_SENSITIVITY;
    Draggables.dragging = this.regionID || true;
});

dynamicTree.Tree.addMethod('setDragEndState', function(node, draggable, event) {
    delete Draggables.dragging;
});
