//
// Search - A Class to handle term searching.
//
// At initialization, the source text is parsed into words and inserted 
// into a global word->features mapping. When searching for terms, 
// a single access of the map for each search term yields a list 
// of features containing that term; simply intersecting the
// list generated by each term then gives the final search result.
//
// History: 
// Original Version: 12/10/2006,  Michael Weiss-Malik, nasamichael@google.com
//

var Search = Class.create();

Search.prototype.initialize = function() {}
Search.prototype.prepareSearch = function(data) {
    this.dictionary = this.createDictionary(data); // word => array of indices
    this.prefixes = this.createPrefixes();  // prefix letter => array of words
}

Search.prototype.createDictionary = function(data) {
    var dictionary = new Object();
    var words;
    var words_j;
    var i_string;

    for (var i=data.length-1; i>=0; i--) { 
        words = data[i].toLowerCase().split(/\W+/);
        i_string = i.toString();
        for (var j=words.length-1; j>=0; j--) {
            words_j = words[j];
            if (words_j != null) {
                // Set up direct word index
                if (words_j in dictionary)
                    dictionary[words_j].push(i_string);
                else
                    dictionary[words_j] = new Array(i_string);
            }
        }
    }
    return  dictionary;
}

Search.prototype.createPrefixes = function() {
    var prefixes = new Object();
    var p;

    for (var prop in this.dictionary) {
        // Set up prefix tree index
        p = prop.charAt(0);
        if (prefixes[p] == null)
            prefixes[p] = new Array(prop);
        else
            prefixes[p].push(prop);
    }

    return  prefixes;
}

// Simple exact word, no pattern matching needed
Search.prototype.searchForExactWord = function(searchTerm, searchResults)
 {
    var dictionary_searchTerm = this.dictionary[searchTerm];
    var resultCount = 0;
    if (dictionary_searchTerm != null)
        for (var i=dictionary_searchTerm.length-1; i>=0; i--)
            searchResults[dictionary_searchTerm[i]] = 0;
    return  resultCount;
}

// We've been handed a prefix word
Search.prototype.searchForPrefixWord = function(searchTerm, searchResults)
 {
    var termLength = searchTerm.length;
    var p = searchTerm.charAt(0);
    var prefixes_p = this.prefixes[p];
    var resultCount = 0;
    if (prefixes_p != null) {
        for (var j=prefixes_p.length-1; j>=0; j--) {
            var k = prefixes_p[j];
            if (k.substring(0, termLength) == searchTerm) {
                var dictionary_k = this.dictionary[k];
                for (var i=dictionary_k.length-1; i>=0; i--)
                    searchResults[dictionary_k[i]] = 0;
            }
        }
    }
    return  resultCount;
}

// We've been handed a single-letter prefix
Search.prototype.searchForPrefixLetter = function(searchTerm, searchResults)
 {
    var p = searchTerm.charAt(0);
    var prefixes_p = this.prefixes[p];
    var resultCount = 0;
    if (prefixes_p != null) {
        for (var j=prefixes_p.length-1; j>=0; j--) {
            var dictionary_k = this.dictionary[prefixes_p[j]];
            for (var i=dictionary_k.length-1; i>=0; i--)
                searchResults[dictionary_k[i]] = 0;
        }
    }
    return  resultCount;
}

//
// execute a search.  
// returns the indexes of matching items as an array of ints
//
Search.prototype.textSearch = function(query) {
    if (query.length == 0) return([]);
    var searchTerms = query.toLowerCase().split(/[^\w\*]+/);
    // eliminate empty elements
	for (i = searchTerms.length-1 ; i >= 0 ; i--) {
		if (searchTerms[i].length == 0) {
			searchTerms.splice(i,1);
		}
	}

    var searchResults = new Array(searchTerms.length);
    for (var i=0; i<searchTerms.length; i++) {
        searchResults[i] = new Object();
        var searchTerm = searchTerms[i];
        var count;
        if (searchTerm.charAt(searchTerm.length-1) == "*") {
            searchTerm = searchTerm.replace(/\W+/,"");
            if (searchTerm.length == 1)
                this.searchForPrefixLetter(searchTerm, searchResults[i]);
            else
                this.searchForPrefixWord(searchTerm, searchResults[i]);
        } else
            this.searchForExactWord(searchTerm, searchResults[i]);
    }

    var finalResults = new Array();

    if (searchResults.length <= 1) {
        for (var p in searchResults[0]) {
            finalResults.push(p);
        }
    } else {
     OUTER:
        for (var p in searchResults[0]) {
            for (var i=searchTerms.length-1; i; i--) {
                if (!(p in searchResults[i])) {
                    continue OUTER;
                }
            }
            finalResults.push(p);
        }
    }
    return finalResults;
}
