/** @fileoverview Note: this functionality is NOT YET IN USE
 *   Used Javascript to walk DOM of XML Document and look for matches, 
 *   this is really a better idea than pure XSLT which (for XSLT 1.0) makes the logic needed excruiating to follow
 *   
 * @requires: xwrapper.js
 * @requires: project_settings.js
 */
 
/** List of the element names which are containers of content;
    only children of these elements will be searched. */
var CONTAINER_ELEMENTS = {
    'Section': 1,
    'Guide': 1,
    'Procedure':1,
    'System':1
};
var TITLE_ELEMENTS = ['Title','Label','Name'];


function getElementTitle(node) {
    var nameNode = null;
    for (var i = 0; i < TITLE_ELEMENTS.length; i++) {
        nameNode = node.getElementsByTagName( TITLE_ELEMENTS[i] );
        if (!xw_isEmpty(nameNode) && (nameNode.length > 0)) {
            nameNode = nameNode[0];
            break;
        }
    }
    if (!xw_isEmpty(nameNode)) {
        return nameNode.firstChild.nodeValue;
    } else {
        return 'No Title';
    }
}

function executeSearch(xwdoc, terms) {
    //alert('executeSearch; terms = ' + terms);
    if ('string' == typeof(terms)) {
        terms = new SearchTerms(terms);
    }
    var search = new SearchCommand(terms, xwdoc);
    searchContainers(search);
    //alert('search.hits.length = ' + search.hits.length);
    search.hits.sort(_sortHits);
    return searchToXml(search);
}

function searchToXml(search) {
    var xdoc = xw_createEmptyDocument('search-results');
    var xRoot = xdoc.document.documentElement;
    //
    // NOW, transfer results to a new xml document so we can run XSLT...
    //alert('Search returned ' + this.hits.length + ' results.');
    for (var i = 0; i < search.hits.length; i++) {
        var hitNode = search.hits[i].node;
        var node = xRoot.ownerDocument.createElement('hit');
        node.setAttribute('title', getElementTitle(hitNode));
        node.setAttribute('id', hitNode.getAttribute('id') );
        node.setAttribute('weight', search.hits[i].weight);
        node.appendChild( hitNode.cloneNode(true) );
        xRoot.appendChild(node);
    }
    return xdoc;
}
/** First we need to get the 'top-level' container elements which we will be searching. */
function searchContainers(search) {
    var children = xw_childNodesToArray(search.documentElement);
    var node;
    while (children.length > 0) {
        node = children.shift();
        if ((!xw_isEmpty(node.nodeName)) && (1 == CONTAINER_ELEMENTS[node.nodeName])) {
            searchXml(search, node);
        } else {
            children = children.concat( xw_childNodesToArray(node) );
        }
    }
}
/** Top-level search, iterates until it either finds 'Container' elements or it terminates. */
function searchXml(search, pnode) {
    var children = xw_childNodesToArray(pnode);
    var contentNodes = new Array();
    var node;
    while (children.length > 0) {
        node = children.pop();
        if ((!xw_isEmpty(node.nodeName)) && (1 == CONTAINER_ELEMENTS[node.nodeName])) {
            searchXml(search, node);
        } else {
            contentNodes.push( node );
        }
    }
    if (contentNodes.length > 0) {
        searchNodeContents(search, pnode, contentNodes);
    }
}
/** Given a container node and its non-container children, 
    search those children for and if matches are found add a SearchHit to the hits array. */
function searchNodeContents(search, node, contentNodes) {
    var tmpNodes;
    var textNodes = new Array();
    for (var i = 0; i < contentNodes.length; i++) {
        tmpNodes = xw_getTextNodes(contentNodes[i]);
        textNodes = textNodes.concat( tmpNodes );
    }
    search.searchTextNodes(node, textNodes);
}

/** excapsulates both SearchTerms and the hits they generate. */ 
function SearchCommand(terms, xwdoc) {
    this.terms = terms;
    this.hits = new Array();
    //alert('xwdoc.document = ' + xwdoc.document);
    //alert('xwdoc.document.documentElement = ' + xwdoc.document.documentElement);
    this.documentElement = xwdoc.document.documentElement;  //Element;
}
SearchCommand.prototype.searchTextNodes = function (parentNode, textNodes) {
    var weight = 0;
    for (var i = 0; i < textNodes.length; i++) {
        weight += this.terms.match( textNodes[i].nodeValue );
    }
    //alert('Attempting to match text in node: ' + parentNode.nodeName  + ', hitWeight: ' + weight);
    if (weight > 0) {
        var hit = new SearchHit(weight, parentNode);
        this.hits.push(hit);
    }
};

/** SearchHit - used to represent data on each node which is a match */
function SearchHit(weight, node) {
    this.weight = weight;
    this.node = node;
}

/** SearchTerms - encpasulates the logic used to search a piece of text for a match against the user's query; 
    this includes breaking the user's query into an array of regular expressions for use in searching 
    as well as calculating a weight for the resulting search (zero if no match > 0 if a match).
*/
function SearchTerms(text) {
    this.terms = text;
    var pattern = this.convertToRegExpPattern(this.terms);
    //alert('Search Terms, pattern: ' + pattern);
    this.matcher = new RegExp(pattern, 'gi');
}
/** RegExp used to split a search query into sub-queries which are either single words, or a group of quoted words. */
SearchTerms.prototype.splitter = /(\\?['"])[^'"]*\1|([\S]+)/g;
/** RegExp used to normalize strings to remove characters that could possible create problems when parsing other RegExps. */
SearchTerms.prototype.normalizer = /[\\\|\*\+\?\(\)]/g;

/** Splits the input text into an array of Strings, which will be used 
    to construct regular expressions used in the match() method. */
SearchTerms.prototype.convertToRegExpPattern = function (text) {
    text = text.replace(this.normalizer, '');
    var terms = text.match(this.splitter);
    for (var i = 0; i < terms.length; i++) {
        if ('\\' == terms[i].charAt(0)) {
            terms[i] = terms[i].replace(/\\/g, '');
        } else if ('"' == terms[i].charAt(0)) {
            terms[i] = terms[i].replace(/\"/g, '');
        }
        terms[i] = '(' + terms[i] + ')';
        //alert('terms[i] = ' + terms[i]);
    }
    return terms.join('|');
};
/** Method which returns the match information for a given String; 
    currently this consists of an integer, zero if no match, > zero depending 
    on the number of terms matched. */
SearchTerms.prototype.match = function (text) {
    var matches = text.match(this.matcher);
    if (!xwrapper.isEmpty(matches)) {
        return matches.length;
    } else {
        return 0;
    }
};
SearchTerms.prototype.toString = function() {
    return this.terms;
};

/** Simple method used to sort the SearchHits array by the weight factor. */
function _sortHits(hit1, hit2) {
    return (hit2.weight - hit1.weight);
}
