YAHOO.namespace("mazzle");	

/**
 * Thesaurus class
 *
 * @class Thesaurus
 * @constructor
 * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
 * @param elContainer {String} String ID of an existing DIV.
 * @param oServer {Object} url of the server
 * @param aNodes {Array} root nodes
 * @param oConfigs {Object} Object literal with configuration params.
 */
YAHOO.mazzle.Thesaurus = function(elContainer, oServer, oConfigs) {	
	// set variables
	this.oServer = oServer;
	this._elContainer = YAHOO.util.Dom.get(elContainer);
	
	// Set any config params passed in to override defaults
	if(oConfigs && (oConfigs.constructor == Object)) {
		for(var sConfig in oConfigs) {
			if(sConfig) {
				this[sConfig] = oConfigs[sConfig];
			}
		}
	}
    this._initDataSource();
    
    if(this.nodes) {
        this._initTree(this.nodes);
    }
    else if(this.query) {
        this._loadTree(this.query);
    }
};




/////////////////////////////////////////////////////////////////////////////
//
// Public member variables
//
/////////////////////////////////////////////////////////////////////////////

/**
 * Server to get data from.
 *
 * @property oServer
 * @public
 */
YAHOO.mazzle.Thesaurus.prototype.oServer = '/api/resource';

/**
 * Root nodes.
 *
 * @property aNodes
 * @public
 */
YAHOO.mazzle.Thesaurus.prototype.nodes = null;

/**
 * Searh query
 *
 * @property query
 * @public
 */
YAHOO.mazzle.Thesaurus.prototype.query = null;

/**
 * Callback object for selection of an item
 *
 * @property onSelect
 * @public
 */
YAHOO.mazzle.Thesaurus.prototype.onSelect = null;

/**
 * Add a checkbox
 *
 * @property check
 * @public
 */
YAHOO.mazzle.Thesaurus.prototype.check = false;

/////////////////////////////////////////////////////////////////////////////
//
// Public methods
//
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
//
// Private member variables
//
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
//
// Public methods
//
/////////////////////////////////////////////////////////////////////////////
  
YAHOO.mazzle.Thesaurus.prototype._initDataSource = function() {
    
    // datasource
    var oServer  = "/pk/api/autocomplete?";
	var sQueryString = "&view=tree&match=stem";
	if(this.filter) {
	    sQueryString += "&"+queryString("filter", this.filter);
	}
	var oResponseSchema = {fields: ["result"]};    
	    
    this._oTreeDataSource = new YAHOO.util.DataSource(oServer);
    this._oTreeDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
    this._oTreeDataSource.scriptQueryAppend = sQueryString;
    this._oTreeDataSource.responseSchema = oResponseSchema;
    this._oTreeDataSource.maxCacheEntries = 20; 
};
  
 YAHOO.mazzle.Thesaurus.prototype._initTree = function(oData) {
     var oSelf = this;
     
     // init the tree
    var oTree = new YAHOO.widget.TreeView(this._elContainer);
    var oRoot = oTree.getRoot();
    // init the tree data (array of nodes or a single root node)
    if(oData.constructor==Array) {
        oSelf.initTreeNodes(oRoot, oData);
    }
    else {
        oSelf.initTreeNode(oData);
    }
    
    // click handler
	oTree.subscribe("labelClick", function(oNode) {
        oSelf._showNode(oNode.data.uri, oSelf);
    });
    
    oTree.subscribe("checkClick", function(oNode) {
        oSelf._selectNode(oNode, oSelf);
    });
    
    // draw the tree
    oTree.draw();
};	
  
YAHOO.mazzle.Thesaurus.prototype._loadTree = function(sQuery) {
    var oSelf = this;
    
    var elContainer = this._elContainer;
    elContainer.innerHTML = loadingHTML();

	function successHandler(oRequest,oResponse){
	    // the root node is only a place holder
	    var aNodes = oResponse.results[0].result.children;
		// init the tree
        oSelf._initTree(aNodes);
	}
	function failureHandler(o){
	    // just to bad
	}
    var oCallback = {
		success:successHandler,
		failure:failureHandler
	};
	var request = queryString("query", sQuery)+this._oTreeDataSource.scriptQueryAppend;
	this._oTreeDataSource.sendRequest(request, oCallback);
}; 
  
YAHOO.mazzle.Thesaurus.prototype.initTreeNodes = function(oRoot, aNodes) {
	for (var i=0;i<aNodes.length;i++) {
        this.initTreeNode(oRoot, aNodes[i]);
	}
};

YAHOO.mazzle.Thesaurus.prototype.initTreeNode = function(oRoot, oNode) {
    var oSelf = this;
    // function to load data for node
    var loadDataForNode = function(node, onCompleteCallback) {
        var sURI = node.data.uri;
    	// server request
    	var link = "/pk/api/resource";
    	link += "?r="+encodeURIComponent(sURI);
    	link += "&method=child";
    	link += "&display=label";
    	link += "&display=alias";
    	link += "&display=hasChild";
    	if(oSelf.rel) {
    	    link += "&rel="+encodeURIComponent(oSelf.rel);
    	}

    	function successHandler(o){
    		var response = YAHOO.lang.JSON.parse(o.responseText);
    		var children = response.result.results.bindings;
    		var display = response.graph;
    		for(var i=0;i<children.length;i++) {
    		    var uri = children[i].child.value
    			var oChild = {
    			    uri: uri,
    			    label: display[uri].label[0].value,
    			    alias: display[uri].alias[0].value,
    			    children: display[uri].hasChild[0].value
    			}
    			oSelf.initTreeNode(node, oChild, oSelf);
    		}
    		onCompleteCallback();
    	}
    	function failureHandler(o){
    	    onCompleteCallback();
    	}
	
    	var callback = {
    		success:successHandler,
    		failure:failureHandler
    	};
    	var request = YAHOO.util.Connect.asyncRequest('GET', link, callback);
    };

    
    var children = oNode.children;
    var label = oNode.label;
    var alias = oNode.alias;
    var sHTML = "";
    
    if(label.length>50) {
        label = label.substr(0,45)+'...';
    }
    var labelClass = oNode.hit&&"hit"||"node";
    if(alias) {
        sHTML = '<span>'+alias+':</span>';
    }
    sHTML += '<span title="'+alias+':'+oNode.label+'" class="'+labelClass+'">'+label+'</span>';
    
    var oData = {"label":sHTML,"uri":oNode.uri};
    var tmpNode;
    var expanded = false;
    var hasChildren = false;
    
    // does the node have children
    if(children.constructor==Array&&children.length>0) {
        expanded = true;
    }
    else if(children) {
        hasChildren = true;
    }
        
    // create Node
    if(this.check) {
        tmpNode = new YAHOO.widget.TaskNode(oData,oRoot,expanded);
    }
    else {
        tmpNode = new YAHOO.widget.TextNode(oData,oRoot,expanded);
    }
    
    // create children
    if(expanded) {
        this.initTreeNodes(tmpNode, oNode.children, oSelf);
    }
    else if(hasChildren) {
        tmpNode.setDynamicLoad(loadDataForNode, 1);
    }
};    

YAHOO.mazzle.Thesaurus.prototype._showNode = function(sURI, oSelf) {
    if(oSelf.localview) {
        oSelf.localview.loadData(encodeURIComponent(sURI));
    }
    else {
        localView(sURI);
    }
};

YAHOO.mazzle.Thesaurus.prototype._selectNode = function(oNode, oSelf) {
    var sURI = oNode.data.uri;
    var sLabel = oNode.data.label;
    
    if(oSelf.onSelect) {
        doCallback(oSelf.onSelect, sURI, sLabel);
    }
    else {
        // now what?
    }
};


/***************************************************
* search panel
***************************************************/

/**
 * Search panel class
 *
 * @class SearchPanel
 * @constructor
 * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
 * @param elContainer {String} String ID of an existing DIV.
 * @param query {String} search query to find an element to be shown
 * @param oConfigs {Object} Object literal with configuration params.
 */
YAHOO.mazzle.SearchPanel = function(elContainer, oServer, oConfigs) {	
	
	// set variables
	this.oServer = oServer;
	
	// Set any config params passed in to override defaults
	if(oConfigs && (oConfigs.constructor == Object)) {
		for(var sConfig in oConfigs) {
			if(sConfig) {
				this[sConfig] = oConfigs[sConfig];
			}
		}
	}

	// init the components
	this._initContainer(elContainer);
};


/**
 * Initializes the search panel container once at object creation.
 *
 * @method _initContainer
 * @private
 * @param elContainer {String}
 */
YAHOO.mazzle.SearchPanel.prototype._initContainer = function(elContainer) {
    
    // Creates an overlay.
	this._elPanel = YAHOO.util.Dom.get(elContainer);
	this._elPanel.className = "mazzle-lv-panel";

	var elPanelHeader = document.createElement("div");
	elPanelHeader.className = "hd";
	this._elPanel.appendChild(elPanelHeader);

	var elPanelBody = document.createElement("div");
	elPanelBody.className = "bd";
	this._elPanel.appendChild(elPanelBody);
			
	this._oPanel = new YAHOO.widget.Panel(this._elPanel, {
		visible:false,draggable:true,close:true
	});

	this._oPanel.render();
	
	// overwrite elContainer with body of panel
	elContainer = document.createElement("div");
	this._elContainer = elPanelBody.appendChild(elContainer);
	YAHOO.util.Dom.addClass(this._elContainer, "mazzle-lv-container");
	
	// Local view content.
    if (!this._elContent) {

        // The oContent div helps size the iframe and shadow properly.
        var elContent = document.createElement("div");
        elContent.className = "mazzle-lv-content";
        this._elContent = this._elContainer.appendChild(elContent);

        var elHeader = document.createElement("div");
        elHeader.className = "mazzle-lv-hd";
        //elHeader.style.display = "none";
        this._elHeader = this._elContent.appendChild(elHeader);

        var elBody = document.createElement("div");
        elBody.className = "mazzle-lv-bd yui-g";
        //elBody.style.display = "none";
        this._elBody = this._elContent.appendChild(elBody);

        // YUI grid 1/2 - 1/2
		var elLeft = document.createElement("div");
		elLeft.className = "yui-u first";
		elBody.appendChild(elLeft);
		
		var elRight = document.createElement("div");
        elRight.className = "yui-u";
        elBody.appendChild(elRight);

        var elBottom= document.createElement("div");
        elBody.appendChild(elBottom);
        elBottom.setAttribute("style", "clear:both");

        var elFooter = document.createElement("div");
        elFooter.className = "mazzle-lv-ft";
        elFooter.style.display = "none";
        this._elFooter = this._elContent.appendChild(elFooter);
        
        // init localview in the right container
        var elLocalView = document.createElement("div");
        elLocalView.className = "localview";
        
        this._elLocalView = elRight.appendChild(elLocalView);
        this.oLocalView = new YAHOO.mazzle.LocalView(this._elLocalView, '/pk/api/localview', '', {"image":false, "links":false, "module":true, "source":"none",
            tables:{
                "subject":{
                	responseSchema: {fields: ["property", "value"]},
                	columns : [	
                		{key:"property", label:"Property", sortFn:sortProp},
                		{key:"value", label:"Value", rdfa:true, edit:true, sortFn:sortValue}
                	]
                },
                "object":{
                    caption: "used as metadata in:",
                	responseSchema: {fields: ["property", "value"]},
                	columns : [	
                		{key:"property", label:"Property", sortFn:sortProp},
                		{key:"value", label:"Subject", rdfa:true, edit:true, sortFn:sortValue}
                	]
                }
            }
        });
        
        var elTree = document.createElement("div");
        elTree.className = "tree";
        elLeft.appendChild(elTree);
        this.oThesaurus = new YAHOO.mazzle.Thesaurus(elTree, '/pk/api/resource', 
            { "localview":this.oLocalView, "check":true });
    }
};


YAHOO.mazzle.SearchPanel.prototype.loadData = function(sQuery, oFilter, oCallback) {
    if (sQuery) {
	    // show the panel
        this._oPanel.setHeader('search: '+sQuery);
		this._oPanel.center();
		this._oPanel.show();
        
        
        this._elHeader.innerHTML = '<h3>Search Results for: '+sQuery+'</h3>';
        this._elHeader.innerHTML += '<div>Click a checkbox to add an item.  The items are directly added to the annotation field. When you are done just close this window</div>';
        
        // pass the callback
        this.oThesaurus.onSelect = oCallback;
        
        // make data source
        this.oThesaurus._oTreeDataSource.scriptQueryAppend += queryString("filter", oFilter);
        this.oThesaurus._loadTree(sQuery);
	}
};


YAHOO.mazzle.SearchPanel.prototype.loadTree = function(oScheme, onSelect) {
    var oSelf = this;
    if (oScheme) {
	    // show the panel
        this._oPanel.setHeader('browse');
		this._oPanel.center();
		this._oPanel.show();
        
        this._elHeader.innerHTML = '';
        this._elHeader.innerHTML += '<div>Click a checkbox to add an item.  The items are directly added to the annotation field. When you are done just close this window</div>';
        
        // pass the callback
        this.oThesaurus.onSelect = onSelect;
        
        function successHandler(o){
	        // the root node is only a place holder
	        var aNodes = YAHOO.lang.JSON.parse(o.responseText);
    		// init the tree
            oSelf.oThesaurus._initTree(aNodes);
    	}
    	function failureHandler(o){
    	    // just to bad
    	}
        var oCallback = {
    		success:successHandler,
    		failure:failureHandler
    	};
	
	    var sLink = "/pk/api/tree?"+queryString("scheme", oScheme);
	    var request = YAHOO.util.Connect.asyncRequest('GET', sLink, oCallback);
	}
};


/***************************************************
* task node
***************************************************/

/**
 * 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.TaskNode = function(oData, oParent, expanded, checked) {

    if (YAHOO.widget.LogWriter) {
        this.logger = new YAHOO.widget.LogWriter(this.toString());
    } else {
        this.logger = YAHOO;
    }

    if (oData) { 
        this.init(oData, oParent, expanded);
        this.setUpLabel(oData);
        this.setUpCheck(checked);
    }

};

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

    /**
     * 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,

    taskNodeParentChange: 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.subscribe("parentChange", this.taskNodeParentChange);

    },

    /**
     * The id of the check element
     * @for YAHOO.widget.TaskNode
     * @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;
    },

    /**
     * Returns the link that will invoke this node's check toggle
     * @return {string} returns the link required to adjust the checkbox state
     */
    getCheckLink: function() { 
        return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," + 
            this.index + ").checkClick()";
    },

    /**
     * Invoked when the user clicks the check box
     */
    checkClick: function() { 
        this.logger.log("previous checkstate: " + this.checkState);
        if (this.checkState === 0) {
            this.check();
        } else {
            this.uncheck();
        }

        this.onCheckClick(this);
        this.tree.fireEvent("checkClick", this);
    },

    /**
     * Override to get the check click event
     */
    onCheckClick: function() { 
        this.logger.log("onCheckClick: " + this);
    },

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

        if (!p || !p.updateParent) {
            this.logger.log("Abort udpate parent: " + this.index);
            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);
        }

        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.logger.log("check");
        this.setCheckState(2);
        /*for (var i=0; i<this.children.length; ++i) {
            this.children[i].check();
        }*/
        this.updateCheckHtml();
        this.updateParent();
    },

    /**
     * Uncheck this node
     */
    uncheck: function() { 
        alert('Uncheck currently not supported\nYou can remove your selection from the annotation field');
        /*
        this.setCheckState(0);
        for (var i=0; i<this.children.length; ++i) {
            this.children[i].uncheck();
        }
        this.updateCheckHtml();
        this.updateParent();
        */
    },

    // Overrides YAHOO.widget.TextNode
    getNodeHtml: function() { 
        this.logger.log("Generating html");
        var sb = [];

        var getNode = 'YAHOO.widget.TreeView.getNode(\'' +
                        this.tree.id + '\',' + this.index + ')';


        sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
        sb[sb.length] = '<tr>';
        
        for (var i=0;i<this.depth;++i) {
            //sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"> </td>';
            sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>';
        }

        sb[sb.length] = '<td';
        sb[sb.length] = ' id="' + this.getToggleElId() + '"';
        sb[sb.length] = ' class="' + this.getStyle() + '"';
        if (this.hasChildren(true)) {
            sb[sb.length] = ' onmouseover="this.className=';
            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getHoverStyle()"';
            sb[sb.length] = ' onmouseout="this.className=';
            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getStyle()"';
        }
        sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"> ';
        //sb[sb.length] = '</td>';
        sb[sb.length] = '<div class="ygtvspacer"></div></td>';

        // check box
        sb[sb.length] = '<td';
        sb[sb.length] = ' id="' + this.getCheckElId() + '"';
        sb[sb.length] = ' class="' + this.getCheckStyle() + '"';
        sb[sb.length] = ' onclick="javascript:' + this.getCheckLink() + '">';
        //sb[sb.length] = ' </td>';
        sb[sb.length] = '<div class="ygtvspacer"></div></td>';
        

        sb[sb.length] = '<td>';
        sb[sb.length] = '<a';
        sb[sb.length] = ' id="' + this.labelElId + '"';
        sb[sb.length] = ' class="' + this.labelStyle + '"';
        sb[sb.length] = ' href="' + this.href + '"';
        sb[sb.length] = ' target="' + this.target + '"';
        sb[sb.length] = ' onclick="return ' + getNode + '.onLabelClick(' + getNode +')"';
        if (this.hasChildren(true)) {
            sb[sb.length] = ' onmouseover="document.getElementById(\'';
            sb[sb.length] = this.getToggleElId() + '\').className=';
            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getHoverStyle()"';
            sb[sb.length] = ' onmouseout="document.getElementById(\'';
            sb[sb.length] = this.getToggleElId() + '\').className=';
            sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
            sb[sb.length] = this.tree.id + '\',' + this.index +  ').getStyle()"';
        }
        sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : '';
        sb[sb.length] = ' >';
        sb[sb.length] = this.label;
        sb[sb.length] = '</a>';
        sb[sb.length] = '</td>';
        sb[sb.length] = '</tr>';
        sb[sb.length] = '</table>';
        return sb.join("");

    },

    toString: function() {
        return "TaskNode (" + this.index + ") " + this.label;
    }

});
