// Searchable node : ca, be hidden / visible depending on the filter
YAHOO.widget.SearchableNode = function(oData, oParent, expanded) {
	YAHOO.widget.SearchableNode.superclass.constructor.call(this,oData,oParent,expanded);
};

YAHOO.extend(YAHOO.widget.SearchableNode, YAHOO.widget.TextNode, {
	
	lastLevelStyle: '', // To know if we are on a category or a child leaf
	
	visible: true,

	setVisible: function(isVisible)
	{
		this.visible = isVisible;
		if (isVisible)
		{
			YAHOO.util.Dom.setStyle(this.getElId(), 'display', '');
		}
		else
		{
			YAHOO.util.Dom.setStyle(this.getElId(), 'display', 'none');
		}
	},
	
	setVisibleRecursively: function(isVisible)
	{
		this.setVisible(isVisible);
		for(var i=0 ; i<this.children.length ; i++)
		{
			var child = this.children[i];
			child.setVisibleRecursively(isVisible);
		}
	},
	
	hasVisibleCategory: function(lastLevelStyles)
	{
		for(var i=0 ; i<this.children.length ; i++)
		{
			var child = this.children[i];
			if (!hasClass(child.labelElId, lastLevelStyles))
			{ // Category
				if (child.visible) return true;
				if (child.hasVisibleCategory(lastLevelStyles)) return true;
			}
		}
		return false;
	},

	hasVisibleChild: function(lastLevelStyles)
	{
		for(var i=0 ; i<this.children.length ; i++)
		{
			var child = this.children[i];
			if (hasClass(child.labelElId, lastLevelStyles))
			{ // Last child
				if (child.visible) return true;
			}
			else
			{ // Category
				if (child.hasVisibleChild(lastLevelStyles)) return true;
			}
		}
		return false;
	}
});

/**
 * The check box marks a task complete.  It is a simulated form field 
 * with three states ...
 * 0=unchecked, 1=some children checked, 2=all children checked
 * When a task is clicked, the state of the nodes and parent and children
 * are updated, and this behavior cascades.
 *
 * @extends YAHOO.widget.TextNode
 * @constructor
 * @param oData    {object}  A string or object containing the data that will
 *                           be used to render this node.
 * @param oParent  {Node}    This node's parent node
 * @param expanded {boolean} The initial expanded/collapsed state
 * @param checked  {boolean} The initial checked/unchecked state
 */
YAHOO.widget.CheckNode = function(oData, oParent, expanded, checked) {
	YAHOO.widget.CheckNode.superclass.constructor.call(this,oData,oParent,expanded);
    this.setUpCheck(checked || oData.checked);

};

YAHOO.extend(YAHOO.widget.CheckNode, YAHOO.widget.SearchableNode, {

    /**
     * True if checkstate is 1 (some children checked) or 2 (all children checked),
     * false if 0.
     * @type boolean
     */
    checked: false,

    /**
     * checkState
     * 0=unchecked, 1=some children checked, 2=all children checked
     * @type int
     */
    checkState: 0,

	/**
     * The node type
     * @property _type
     * @private
     * @type string
     * @default "TextNode"
     */
    _type: "CheckNode",
	
	checkNodeParentChange: function() {
        //this.updateParent();
    },
	
    setUpCheck: function(checked) {
        // if this node is checked by default, run the check code to update
        // the parent's display state
        if (checked && checked === true) {
            this.check();
        // otherwise the parent needs to be updated only if its checkstate 
        // needs to change from fully selected to partially selected
        } else if (this.parent && 2 === this.parent.checkState) {
             this.updateParent();
        }

        // set up the custom event on the tree for checkClick
        /**
         * Custom event that is fired when the check box is clicked.  The
         * custom event is defined on the tree instance, so there is a single
         * event that handles all nodes in the tree.  The node clicked is 
         * provided as an argument.  Note, your custom node implentation can
         * implement its own node specific events this way.
         *
         * @event checkClick
         * @for YAHOO.widget.TreeView
         * @param {YAHOO.widget.Node} node the node clicked
         */
        if (this.tree && !this.tree.hasEvent("checkClick")) {
            this.tree.createEvent("checkClick", this.tree);
        }

		this.tree.subscribe('clickEvent',this.checkClick);
        this.subscribe("parentChange", this.checkNodeParentChange);


    },

    /**
     * The id of the check element
     * @for YAHOO.widget.CheckNode
     * @type string
     */
    getCheckElId: function() { 
        return "ygtvcheck" + this.index; 
    },

    /**
     * Returns the check box element
     * @return the check html element (img)
     */
    getCheckEl: function() { 
        return document.getElementById(this.getCheckElId()); 
    },

    /**
     * The style of the check element, derived from its current state
     * @return {string} the css style for the current check state
     */
    getCheckStyle: function() { 
        return "ygtvcheck" + this.checkState;
    },


   /**
     * Invoked when the user clicks the check box
     */
    checkClick: function(oArgs) { 
		var node = oArgs.node;
		var target = YAHOO.util.Event.getTarget(oArgs.event);
		if (YAHOO.util.Dom.hasClass(target,'ygtvspacer')) {
	        if (node.checkState === 0) {
	            node.check();
	        } else {
	            node.uncheck();
	        }

	        node.onCheckClick(node);
	        this.fireEvent("checkClick", node);
		    return false;
		}
    },

    /**
     * Override to get the check click event
     */
    onCheckClick: function() { 
    },

    /**
     * Refresh the state of this node's parent, and cascade up.
     */
    updateParent: function() { 
        var p = this.parent;

        if (!p || !p.updateParent) {
            return;
        }

        var somethingChecked = false;
        var somethingNotChecked = false;

        for (var i=0, l=p.children.length;i<l;i=i+1) {

            var n = p.children[i];

            if ("checked" in n) {
                if (n.checked) {
                    somethingChecked = true;
                    // checkState will be 1 if the child node has unchecked children
                    if (n.checkState === 1) {
                        somethingNotChecked = true;
                    }
                } else {
                    somethingNotChecked = true;
                }
            }
        }

        if (somethingChecked) {
            p.setCheckState( (somethingNotChecked) ? 1 : 2 );
        } else {
            p.setCheckState(0);
        }

        p.updateCheckHtml();
        p.updateParent();
    },

    /**
     * If the node has been rendered, update the html to reflect the current
     * state of the node.
     */
    updateCheckHtml: function() { 
        if (this.parent && this.parent.childrenRendered) {
            this.getCheckEl().className = this.getCheckStyle();
        }
    },

    /**
     * Updates the state.  The checked property is true if the state is 1 or 2
     * 
     * @param the new check state
     */
    setCheckState: function(state) { 
        this.checkState = state;
        this.checked = (state > 0);
    },

    /**
     * Check this node
     */
    check: function() { 
        this.setCheckState(2);
        for (var i=0, l=this.children.length; i<l; i=i+1) {
            var c = this.children[i];
            if (c.check) {
                c.check();
            }
        }
        this.updateCheckHtml();
        this.updateParent();
    },

    /**
     * Uncheck this node
     */
    uncheck: function() { 
        this.setCheckState(0);
        for (var i=0, l=this.children.length; i<l; i=i+1) {
            var c = this.children[i];
            if (c.uncheck) {
                c.uncheck();
            }
        }
        this.updateCheckHtml();
        this.updateParent();
    },
    // Overrides YAHOO.widget.TextNode

    getContentHtml: function() {                                                                                                                                           
        var sb = [];                                                                                                                                                       
        sb[sb.length] = '<td';                                                                                                                                             
        sb[sb.length] = ' id="' + this.getCheckElId() + '"';                                                                                                               
        sb[sb.length] = ' class="' + this.getCheckStyle() + '"';                                                                                                           
        sb[sb.length] = '>';                                                                                                                                               
        sb[sb.length] = '<div class="ygtvspacer"></div></td>';                                                                                                             
                                                                                                                                                                           
        sb[sb.length] = '<td><span';                                                                                                                                       
        sb[sb.length] = ' id="' + this.labelElId + '"';                                                                                                                    
        if (this.title) {                                                                                                                                                  
            sb[sb.length] = ' title="' + this.title + '"';                                                                                                                 
        }                                                                                                                                                                  
        sb[sb.length] = ' class="' + this.labelStyle  + '"';                                                                                                               
        sb[sb.length] = ' >';                                                                                                                                              
        sb[sb.length] = this.label;                                                                                                                                        
        sb[sb.length] = '</span></td>';                                                                                                                                    
        return sb.join("");                                                                                                                                                
    }  
});


YAHOO.widget.TaskNode = function(oData, oParent, expanded, checked) {
	YAHOO.widget.TaskNode.superclass.constructor.call(this,oData,oParent,expanded);
    this.setUpCheck(checked || oData.checked);

};

YAHOO.extend(YAHOO.widget.TaskNode, YAHOO.widget.CheckNode, {

	_type: "TaskNode",

    /**
     * Refresh the state of this node's parent, but don't cascade up.
     */
    updateParent: function() { 
        var p = this.parent;

        if (!p || !p.updateParent) {
            return;
        }

        var somethingChecked = false;
        var somethingNotChecked = false;

        for (var i=0;i< p.children.length;++i) {
            if (p.children[i].checked) {
                somethingChecked = true;
                // checkState will be 1 if the child node has unchecked children
                if (p.children[i].checkState == 1) {
                    somethingNotChecked = true;
                }
            } else {
                somethingNotChecked = true;
            }
        }

        if (somethingChecked) {
            p.setCheckState( (somethingNotChecked) ? 1 : 2 );
        } else {
           // p.setCheckState(0); Modification Olivier : we do not want to uncheck parents
        }

        p.updateCheckHtml();
        p.updateParent();
    }
});



YAHOO.widget.SingleCheckNode = function(oData, oParent, expanded, checked) {
	YAHOO.widget.SingleCheckNode.superclass.constructor.call(this,oData,oParent,expanded);
    this.setUpCheck(checked || oData.checked);

};

YAHOO.extend(YAHOO.widget.SingleCheckNode, YAHOO.widget.CheckNode, {

	_type: "SingleCheckNode",

    /**
     * Don't update parent.
     */
    updateParent: function() { 
    },
    check: function() { 
        this.setCheckState(2);
        this.updateCheckHtml();
        this.updateParent();
    },  
    uncheck: function() { 
        this.setCheckState(0);
        this.updateCheckHtml();
        this.updateParent();
    }      
});


function taskNodesCheckAll(tree) {
    var topNodes = tree.getRoot().children;
    for(var i=0; i<topNodes.length; ++i) {
        topNodes[i].check();
    }
}

function taskNodesUncheckAll(tree) {
    var topNodes = tree.getRoot().children;
    for(var i=0; i<topNodes.length; ++i) {
        topNodes[i].uncheck();
    }
}


// Gets the labels of all of the fully checked nodes
// Could be updated to only return checked leaf nodes by evaluating
// the children collection first.
// Param minState : the state the checkbox must be in
function getCheckedNodes(tree, minState, nodes) {
    nodes = nodes || tree.getRoot().children;
    checkedNodes = [];
    for(var i=0, l=nodes.length; i<l; i=i+1) {
        var n = nodes[i];
        if (n.checkState > minState) {
            checkedNodes.push(n.value); // just using label for simplicity
        }

        if (n.hasChildren()) {
	checkedNodes = checkedNodes.concat(getCheckedNodes(tree, minState, n.children));
        }
    }

    return checkedNodes;
}


function saveCheckedNodes( tree, f, minState )
{
  if (!minState) minState = 0;
  var nodes = getCheckedNodes(tree, minState);
  f.value = nodes.join(';');
}

function displayAll(nodes)
{
	for(var i=0 ; i<nodes.length ; i++)
	{
		var node = nodes[i];
		node.setVisible(true);
		if (node.hasChildren()) displayAll(node.children);
	}	
}

function hideLeavesWithNoChild(nodes, lastLevelStyles)
{
	for(var i=0 ; i<nodes.length ; i++)
	{
		var node = nodes[i];
		if (!hasClass(node.labelElId, lastLevelStyles))
		{
			if (node.hasVisibleChild(lastLevelStyles))
			{
				node.setVisible(true);
				hideLeavesWithNoChild(node.children, lastLevelStyles);
			}
			else node.setVisible(false);
		}
	}
}

function hideLeavesWithNoCategory(nodes, lastLevelStyles)
{
	for(var i=0 ; i<nodes.length ; i++)
	{
		var node = nodes[i];
		if (!hasClass(node.labelElId, lastLevelStyles))
		{
			if (node.hasVisibleCategory(lastLevelStyles))
			{
				node.setVisible(true);
				hideLeavesWithNoCategory(node.children, lastLevelStyles);
			}
		}
	}
}

function hasClass(id, classes)
{
	for(var i=0 ; i<classes.length ; i++)
	{
		if (YAHOO.util.Dom.hasClass(id, classes[i])) return true;
	}
	return false;
}

/**
	Hide/Show leaves
	If isCategory, only nodes that are node in lastLevelStyle are searched
 */
function search(nodes, searchFilter, isCategory, lastLevelStyles, recursive)
{
	if (searchFilter == '')
	{ // Show all
		displayAll(nodes);
	}
	else
	{
		if (!isCategory)
		{
			for(var i=0 ; i<nodes.length ; i++)
			{
				var node = nodes[i];
				if (hasClass(node.labelElId, lastLevelStyles))
				{
					node.setVisible(contains(node.label, searchFilter));
				}
				if (node.hasChildren()) search(node.children, searchFilter, isCategory, lastLevelStyles, true);
			}
			
			if (!recursive) hideLeavesWithNoChild(nodes, lastLevelStyles);
		}
		else
		{ // Category
			for(var i=0 ; i<nodes.length ; i++)
			{
				var node = nodes[i];
				if (!hasClass(node.labelElId, lastLevelStyles))
				{
					var visible = contains(node.label, searchFilter);
					node.setVisibleRecursively(visible);
					if (!visible && node.hasChildren()) search(node.children, searchFilter, isCategory, lastLevelStyles, true);
				}
				else if (!recursive) node.setVisible(false); // When searching on categories, root items are not displayed
			}
			
			if (!recursive) hideLeavesWithNoCategory(nodes, lastLevelStyles);
		}			
	}
}
