/**
 *
 * xModify is an extension of the jQuery library that processes requests
 * and update the HTML DOM using the xModify syntax.  Included in xModify is
 * a series of instructions that can be used to update html.
 *
 * insert-after, insert-before, append, set-attribute, set-style
 * set-innerhtml, replace-children, remove-element, remove-attribute
 * replace
 *
 *
 */
$.extend({
    /*
     * Library version of xModify.
     */
    xmodifyVersion: "0.3.0",
    
    /**
     * Main function to kick off an xModify request.
     * The results of the request will be automatically
     * processed.
     *
     * Look at the documenation for the jQuery.ajax function
     * for information on the parameters.
     *
     * http://docs.jquery.com/Ajax
     *
     */
    xmodify: function(ajaxProperties){
        var complete = ajaxProperties.complete;
        ajaxProperties.complete = function(result, request){
            if (result.responseXML == null){
                ajaxProperties.error.call(this, result, request);
            }else{
                jQuery.xProc(result.responseXML);
                if (complete){
                    complete.call(this, result, request);
                }
            }
        }
        return jQuery.ajax(ajaxProperties);
    },
	
	/**
	 * This method provides a shortcut for blending html into the 
	 * current html application. Check the jQuery documentation 
	 * specifics on the format and functionality of jQuery's selectors
	 * 
	 * 
	 * Mode can be any of the following append, replace, replace-children
	 * insert-after, insert-before
	 * 
	 * @param {Object} ajaxProperties
	 * @param {Object} select
	 * @param {Object} blendMode
	 */
	blend: function(ajaxProperties, select, blendMode){
        var complete = ajaxProperties.complete;
        var variables = {};
        ajaxProperties.complete = function(result, request){
            if (result.responseText == null){
                ajaxProperties.error.call(this, result, request);
            }else{
				var node = document.createElement(blendMode);
				node.setAttribute("select", select);
				node.appendChild(document.createTextNode(result.responseText));
                jQuery.xProcElem(node, variables);
                if (complete){
                    complete.call(this, result, request);
                }
            }
        }
        return jQuery.ajax(ajaxProperties);
	},
    
    /**
     * Will return the modification element.
     *
     * Assumes the modifications elements are under XAL
     * <xal>
     *    <modifications>
     *    </modifications>
     * </xal>
     *
     */
    xProc: function(modificationDocument){
        var variables = {};
        jQuery.each( 
            modificationDocument.documentElement.childNodes, function(){
               //look up all the modification elements
				if ( this.nodeType == 1 && jQuery.xNodeName(this.nodeName) == "modifications"){
				    //process all the modification elements
                    jQuery.each(this.childNodes, function(){
            				if (this.nodeType == 1){
                                jQuery.xProcElem(this, variables);
                            }
                        }
            		);
                }
            }
		);
    },
    
    /**
     * Will return the modification element.
     *
     * Assumes the modifications elements are under XAL
     *
     * MAIN ELEMENT NAMES     
     * insert-after         //This one is roughed out.
     * insert-before        //This one is roughed out.
     * append               //This one is roughed out.
     * set-attribute        //This one is roughed out.
     * set-style            //This one is roughed out.
     * set-innerhtml        //This one is roughed out.
     * replace-children     //This one is roughed out.
     * remove-element       //This one is roughed out.
     * remove-attribute     //This one is roughed out.
     * replace              //This one is roughed out. 
     * 
     * insert-at            //This one is roughed out.  
     *   
     * clone                //This one is roughed out.  
     * create-document      //NOT IMPLEMENTED
     * 
     * variable             //This one is roughed out.
     * attribute            //This one is roughed out.       
     * value-of             //This one is roughed out.
     *
     * TODOs
     *
     * Look into events and styles. Events and styles work
     * as long as the programmer types them in correctly
     *
     * Need to add the must exist attribute
     * Need to add events.
     */
    xProcElem: function(modElement, variables){
        var nodeName = jQuery.xNodeName(modElement.nodeName);
        var select = modElement.getAttribute("select");
       if (nodeName == "append"){
            jQuery.xProcIn(modElement.childNodes, function(nodeValue){    
                jQuery.xSelect(select).append(nodeValue);        
					            }, variables);
        }else if (nodeName == "set-attribute"){
            jQuery.each(modElement.childNodes, function(){
            	if (this.nodeType == 1){
                    jQuery.xSelect(select).attr(this.getAttribute("name"), this.getAttribute("value"));
    			}
            });
        }else if (nodeName == "set-style"){
            jQuery.each(modElement.childNodes, function(){
            	if (this.nodeType == 1){
                    jQuery.xSelect(select).css(this.getAttribute("name"), this.getAttribute("value"));
    			}
            });
        }else if (nodeName == "insert-before"){
            jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                jQuery.xSelect(select).before(nodeValue);        
            }, variables);
        }else if (nodeName == "insert-after"){
            jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                jQuery.xSelect(select).after(nodeValue);        
            }, variables);
        }else if (nodeName == "replace-children" ||
            nodeName == "set-innerhtml"){
            jQuery.xSelect(select).empty();        
            jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                jQuery.xSelect(select).append(nodeValue);        
            }, variables);
        }else if (nodeName == "remove-element"){
            jQuery.xSelect(select).remove();        
        }else if (nodeName == "remove-attribute"){
            jQuery.each(modElement.childNodes, function(){
            		if (this.nodeType == 1){
    				    jQuery.xSelect(select).removeAttr(this.getAttribute("name"));
    				}
                });
        }else if (nodeName == "replace"){
            jQuery.xSelect(select).each(function(){
                    //if the next sibling doesn't exist then place it at the end
                    //other wise place it before the next sibling.
                    if (this.nextSibling){
                        var node = this.nextSibling;
                        $(this).remove();
                        jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                            $(node).before(nodeValue);        
                        }, variables);
                    }else{
                        var node = this.parentNode;
                        $(this).remove();
                        jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                            $(node).append(nodeValue);        
                        }, variables);
                    }
                });       
        }else if (nodeName == "insert-at"){
            var index = modElement.getAttribute("index");
            jQuery.xSelect(select).each(function(){
                    var beforeNode = null;
                    var node = this;
                    
                    //if the index is more than the number
                    //of children nodes append it.
                    if (this.childNodes.length > index){
                        beforeNode = this.childNodes[index];
                    }
                    
                    jQuery.xProcIn(modElement.childNodes, function(nodeValue){
                            if (beforeNode){
                                $(beforeNode).before(nodeValue); 
                            }else{
                                $(node).append(nodeValue); 
                            }       
                        }, variables);
            	});	
        }else if (nodeName == "variable"){
            var name = modElement.getAttribute("name");
            var deep = modElement.getAttribute("deep");
            variables[name] = jQuery.xSelect(select).clone(deep);
        }
    },     
    
    /**
     * Called to process the an instruction will loop over each of the
     * instruction's children and fire a callback
     */
    xProcIn: function(nodes, callback, variables){
        $(nodes).each(function(){
            //make sure there is a nodeValue and this isn't an empty string
            if ((this.nodeType == 3 || this.nodeType == 4) && jQuery.trim(this.nodeValue) != ""){
                callback.call(this, this.nodeValue);
    		}else if (this.nodeType == 1 && jQuery.xNodeName(this.nodeName) == "value-of"){
                callback.call(this, variables[this.getAttribute("name")]);
            }else if (this.nodeType == 1 && jQuery.xNodeName(this.nodeName) == "clone"){
                var select = this.getAttribute("select");
                var deep = this.getAttribute("deep");
                callback.call(this, jQuery.xSelect(select).clone(deep));
    		}else if (this.nodeType == 1){
    		    //got an XML node need to convert it into an HTML node
                callback.call(this, jQuery.xConvertToString(this));
     		}
		});
    }, 
    
    /**
     * Convert an XML structure into a string 
     */
    xConvertToString: function(node){
	    var elements = "<" + node.nodeName;
	    if (node.attributes){
    	    for (var i = 0; i < node.attributes.length; i++){
    	          elements += " " + node.attributes[i].name + "=\"" + node.attributes[i].value + "\"";
    	    } 
	    }
	    elements += ">";	    
	    for (var i = 0; i < node.childNodes.length; i++){
	          if (node.childNodes[i].nodeType == 1){
	            elements += jQuery.xConvertToString(node.childNodes[i]);
	          }else{
	            elements += node.childNodes[i].nodeValue;
	          }
	    }
	    elements += "</" + node.nodeName + ">";
	    return elements;
   },
    
    xNodeName: function(name){
    	var i = name.indexOf(":");
		if (i != -1){
			name = name.substring(i + 1);
		}
		return name.toLowerCase();
    },
	
	xSelect: function(select){
		var obj = $(select);
		return $(jQuery.makeArray(obj));
	}

});
