// {{MadCap}} //////////////////////////////////////////////////////////////////
// Copyright: MadCap Software, Inc - www.madcapsoftware.com ////////////////////
////////////////////////////////////////////////////////////////////////////////
// <version>10.0.0.0</version>
////////////////////////////////////////////////////////////////////////////////

//
//    Class CMCTokenizer
//

function CMCTokenizer()
{
    // Private member variables
    
    var mOriginalString = "";
    var mPos            = -1;
    var mTokens         = new Array();
    
    // Public member functions
    
    this.Tokenize   = function( str )
    {
        var token   = null;
        
        mOriginalString = str;
        mPos = -1;
        
        for ( var i = 0; token = ReadNextToken(); i++ )
        {
            mTokens[i] = token;
        }
        
        return mTokens;
    }
    
    // Private member functions
    
    function IsNameChar( c )
    {
        if      ( !c )                { return false; }
        else if ( c == "\"" )         { return false; }
        else if ( c == "+"  )         { return false; }
        else if ( c == "^"  )         { return false; }
        else if ( c == "|"  )         { return false; }
        else if ( c == "&"  )         { return false; }
        else if ( c == "("  )         { return false; }
        else if ( c == ")"  )         { return false; }
        else if ( IsWhiteSpace( c ) ) { return false; }
        else                          { return true;  }
    }
    
    function IsWhiteSpace( c )
    {
        if ( !c )
        {
			return false;
		}
        else if ( c == " " )
        {
			return true;
		}
        else if ( c.charCodeAt( 0 ) == 12288 )
        {
			return true;
		}
        else
        {
			return false;
        }
    }
    
    function Peek()
    {
        return mOriginalString.charAt( mPos + 1 );
    }
    
    function Read()
    {
        mPos++;
    }
    
    function ReadString()
    {
        var str = "";
        
        for ( ; ; )
        {
            var c   = Peek();
            
            if ( !c )
            {
                return (str == "") ? null : str;
            }
            
            if ( c == "\\" )
            {
                Read();
                
                if ( !Peek() )
                {
                    return null;
                }
                
                Read();
                
                continue;
            }
            
            if ( c == "\"" )
            {
                Read();
                
                break;
            }
            else
            {
                Read();
                str += c;
            }
        }
        
        return str;
    }
    
    function ReadNextToken()
    {
        var c           = Peek();
        var token       = null;
        var tokenText   = "";
        
        if ( !c )
        {
            token = null;
        }
        else if ( IsWhiteSpace( c ) )
        {
            for ( c = Peek(); IsWhiteSpace( c ); c = Peek() )
            {
                Read();
                tokenText += c;
            }
            
            token = new CMCToken( tokenText, CMCToken.WhiteSpace );
        }
        else if ( c == "(" )
        {
            Read();
            token = new CMCToken( c, CMCToken.LeftParen );
        }
        else if ( c == ")" )
        {
            Read();
            token = new CMCToken( c, CMCToken.RightParen );
        }
        else if ( c == "^" )
        {
            Read();
            token = new CMCToken( c, CMCToken.Subtract );
        }
        else if ( c == "+" || c == "&" )
        {
            Read();
            token = new CMCToken( c, CMCToken.And );
        }
        else if ( c == "|" )
        {
            Read();
            token = new CMCToken( c, CMCToken.Or );
        }
        else if ( c == "!" )
        {
            Read();
            token = new CMCToken( c, CMCToken.Not );
        }
        else if ( c == "\"" )
        {
            Read();

            var str = ReadString();

            if (str.Contains(gConnectors))
            {
                str = str.replace(gConnectorRegex, " ");
                str = str.Trim();
            }
            
            token = new CMCToken( str, (str == null) ? CMCToken.Error : CMCToken.Phrase );
        }
        else
        {
            for ( c = Peek(); IsNameChar( c ); c = Peek() )
            {
                Read();
                tokenText += c;
            }
            
            if ( tokenText == "and" || tokenText == "AND" )
            {
                token = new CMCToken( tokenText, CMCToken.And );
            }
            else if ( tokenText == "or" || tokenText == "OR" )
            {
                token = new CMCToken( tokenText, CMCToken.Or );
            }
            else if ( tokenText == "not" || tokenText == "NOT" )
            {
                token = new CMCToken( tokenText, CMCToken.Not );
            }
            else
            {
				var tokenType	= CMCToken.Word;
				
				if ( gSearchDBs[0].SearchType == "NGram" )
				{
					tokenType = CMCToken.Phrase;
				}

	            if (tokenText.Contains(gConnectors))
				{
				    tokenText = tokenText.replace(gConnectorRegex, " ");
				    tokenText = tokenText.Trim();
				    token = new CMCToken(tokenText, CMCToken.Phrase);
				}
				else
				{
				    token = new CMCToken(tokenText, tokenType);
				}
            }
        }
        
        return token;
    }
}

var gConnectors = [".", ",", "'", "/", ":", ";", "\\", "-", "*", "<", ">", "!", "@"];
var gConnectorRegex = new RegExp("[" + gConnectors.join("") + "]", "g");

//
//    End class CMCTokenizer
//

//
//    Class CMCToken
//

function CMCToken( tokenText, type )
{
    // Private member variables
    
    var mTokenText  = tokenText;
    var mType       = type;
    
    // Public member functions
    
    this.GetTokenText   = function ()
    {
        return mTokenText;
    };
    
    this.GetType        = function ()
    {
        return mType;
    };
}

// Static properties

CMCToken.Eof        = 0;
CMCToken.Error      = 1;
CMCToken.WhiteSpace = 2;
CMCToken.Phrase     = 3;
CMCToken.Word       = 4;
CMCToken.RightParen = 5;
CMCToken.LeftParen  = 6;
CMCToken.Not        = 7;
CMCToken.Subtract   = 8;
CMCToken.And        = 9;
CMCToken.Or         = 10;
CMCToken.ImplicitOr = 11;

//
//    End class CMCToken
//

//
//    Class CMCParser
//

function CMCParser( str )
{
    // Private member variables
    
    var mSelf           = this;
    var mSearchString   = str;
    var mCurrentToken   = -1;
    var mTokenizer      = new CMCTokenizer();
    var mSearchTokens   = mTokenizer.Tokenize( mSearchString );
    
    // Public member functions

    this.GetStemMap = function (OnCompleteFunc)
    {
        function OnLoadChunkComplete()
        {
            completed++;

            if (completed == length)
            {
                for (var i = 0; i < mSearchTokens.length; i++)
                {
                    var token = mSearchTokens[i];

                    if (token.GetType() == CMCToken.Word)
                    {
                        var term = token.GetTokenText();
                        var phrases = new CMCDictionary();

                        stemMap.Add(term, phrases);

                        for (var j = 0; j < gSearchDBs.length; j++)
                        {
                            var searchDB = gSearchDBs[j];

                            if (searchDB.SearchType == "NGram")
                            {
                                for (var k = 0; k < term.length - searchDB.NGramSize + 1; k++)
                                {
                                    var subText = term.substring(k, k + searchDB.NGramSize);
                                    var stem = stemWord(subText);

                                    searchDB.LookupPhrases(stem, phrases);
                                }
                            }
                            else
                            {
                                var stem = stemWord(term);

                                searchDB.LookupPhrases(stem, phrases);
                            }
                        }
                    }
                    else if (token.GetType() == CMCToken.Phrase)
                    {
                        var term = token.GetTokenText();
                        var phrases = new CMCDictionary();

                        phrases.Add(term, true);
                        stemMap.Add(term, phrases);
                    }
                }

                OnCompleteFunc(stemMap);
            }
        }

        var completed = 0;
        var length = 0;

        //

        var stemMap = new CMCDictionary();

        // Calculate "length"
        for (var i = 0; i < mSearchTokens.length; i++)
        {
            var token = mSearchTokens[i];

            if (token.GetType() == CMCToken.Word)
            {
                var term = token.GetTokenText();

                for (var j = 0; j < gSearchDBs.length; j++)
                {
                    var searchDB = gSearchDBs[j];

                    if (searchDB.SearchType == "NGram")
                    {
                        for (var k = 0; k < term.length - searchDB.NGramSize + 1; k++)
                        {
                            length++;
                        }
                    }
                    else
                    {
                        length++;
                    }
                }
            }
            else if (token.GetType() == CMCToken.Phrase)
            {
                length++;
            }
        }

        // Load all the chunks first
        for (var i = 0; i < mSearchTokens.length; i++)
        {
            var token = mSearchTokens[i];

            if (token.GetType() == CMCToken.Word)
            {
                var term = token.GetTokenText();

                for (var j = 0; j < gSearchDBs.length; j++)
                {
                    var searchDB = gSearchDBs[j];

                    if (searchDB.SearchType == "NGram")
                    {
                        for (var k = 0; k < term.length - searchDB.NGramSize + 1; k++)
                        {
                            var subText = term.substring(k, k + searchDB.NGramSize);
                            var stem = stemWord(subText);

                            searchDB.LoadChunk(stem, OnLoadChunkComplete);
                        }
                    }
                    else
                    {
                        var stem = stemWord(term);

                        searchDB.LoadChunk(stem, OnLoadChunkComplete);
                    }
                }
            }
            else if (token.GetType() == CMCToken.Phrase)
            {
                OnLoadChunkComplete();
            }
        }
    };
    
    this.ParseExpression    = function ()
    {
        var node = ParseUnary();
        
        SkipWhiteSpace();
        
        if ( Peek() == CMCToken.Eof )
        {
            return node;
        }
        else if ( Peek() == CMCToken.And || Peek() == CMCToken.Or || Peek() == CMCToken.Subtract )
        {
            EatToken();
            
            var binNode = new CMCNode( mSearchTokens[mCurrentToken],
                                       node,
                                       this.ParseExpression() );
                                    
            return binNode;
        }
        else if ( Peek() == CMCToken.Word || Peek() == CMCToken.Phrase || Peek() == CMCToken.Not || Peek() == CMCToken.LeftParen )
        {
            var binNode = new CMCNode( new CMCToken( node.GetToken().GetTokenText() + " " + mSearchTokens[mCurrentToken + 1].GetTokenText(), CMCToken.ImplicitOr ),
                                       node,
                                       this.ParseExpression() );
                                       
            return binNode;
        }
        else if ( Peek() == CMCToken.RightParen )
        {
            return node;
        }
        
        throw gInvalidTokenLabel;
    };
    
    // Private member functions
    
    function EatToken()
    {
        mCurrentToken++;
    }
    
    function ParseUnary()
    {
        SkipWhiteSpace();
        
        if ( Peek() == CMCToken.Word )
        {
            EatToken();
            
            return new CMCNode( mSearchTokens[mCurrentToken], null, null );
        }
        else if ( Peek() == CMCToken.Phrase )
        {
            EatToken();
            
            return new CMCNode( mSearchTokens[mCurrentToken], null, null );
        }
        else if ( Peek() == CMCToken.Not )
        {
            EatToken();
            
            return new CMCNode( mSearchTokens[mCurrentToken],
                                ParseUnary(),
                                null );
        }
        else if ( Peek() == CMCToken.LeftParen )
        {
            EatToken();
            
            var token   = mSearchTokens[mCurrentToken];
            var node    = new CMCNode( token, mSelf.ParseExpression(), null );
            
            if ( Peek() != CMCToken.RightParen )
            {
                throw "Missing right paren ')'.";
            }
            
            EatToken();
            
            return node;
        }
        
        throw gInvalidTokenLabel;
    }
    
    function Peek()
    {
        if ( mSearchTokens[mCurrentToken + 1] == null )
        {
            return CMCToken.Eof;
        }
        else
        {
            return mSearchTokens[mCurrentToken + 1].GetType();
        }
    }
    
    function SkipWhiteSpace()
    {
        for ( ; Peek() == CMCToken.WhiteSpace; )
        {
            EatToken();
        }
    }
}

//
//    End class CMCParser
//

//
//    Class CMCNode
//

function CMCNode( token, op1, op2 )
{
    // Private member variables
    
    var mToken      = token;
    var mOperand1   = op1;
    var mOperand2   = op2;
    
    // Public member functions

    this.Evaluate = function (buildWordMap, buildPhraseMap, OnCompleteFunc)
    {
        var tokenType = mToken.GetType();

        if (tokenType == CMCToken.Word)
        {
            this.EvaluateWord(buildWordMap, buildPhraseMap, OnCompleteFunc);

            return;
        }
        else if (tokenType == CMCToken.Phrase)
        {
            this.EvaluatePhrase(buildWordMap, buildPhraseMap, OnCompleteFunc);

            return;
        }
        else if (tokenType == CMCToken.And ||
				  tokenType == CMCToken.ImplicitOr ||
				  tokenType == CMCToken.Or ||
				  tokenType == CMCToken.Subtract)
        {
            var needWordMap = mToken.GetType() == CMCToken.ImplicitOr;
            var needPhraseMap = mToken.GetType() == CMCToken.ImplicitOr || mToken.GetType() == CMCToken.Or;

            mOperand1.Evaluate(needWordMap, needPhraseMap, function (leftResults)
            {
                mOperand2.Evaluate(false, false, function (rightResults)
                {
                    if (mToken.GetType() == CMCToken.And)
                    {
                        OnCompleteFunc(leftResults.ToIntersection(rightResults, buildWordMap, buildPhraseMap));
                    }
                    else if (mToken.GetType() == CMCToken.ImplicitOr)
                    {
                        rightResults.PromotePhrases(leftResults, mToken);
                        leftResults.ToUnion(rightResults, buildWordMap, buildPhraseMap);

                        OnCompleteFunc(leftResults);
                    }
                    else if (mToken.GetType() == CMCToken.Or)
                    {
                        leftResults.ToUnion(rightResults, buildWordMap, buildPhraseMap);

                        OnCompleteFunc(leftResults);
                    }
                    else if (mToken.GetType() == CMCToken.Subtract)
                    {
                        OnCompleteFunc(leftResults.ToDifference(rightResults, buildWordMap, buildPhraseMap));
                    }
                });
            });

            return;
        }
        else if (tokenType == CMCToken.LeftParen)
        {
            if (mOperand1)
            {
                mOperand1.Evaluate(buildWordMap, buildPhraseMap, OnCompleteFunc);

                return;
            }

            OnCompleteFunc(new CMCQueryResultSet());

            return;
        }
        else if (tokenType == CMCToken.Not)
        {
            function OnEvaluateOperand1Complete(resultSet)
            {
                var val = resultSet;
                var results = new CMCQueryResultSet();

                for (var i = 0; i < gSearchDBs.length; i++)
                {
                    var searchDB = gSearchDBs[i];

                    for (var j = 0; j < searchDB.URLSources.length; j++)
                    {
                        var found = false;
                        var currResult = null;

                        for (var k = 0; k < val.GetLength(); k++)
                        {
                            currResult = val.GetResult(k);

                            if (currResult.Entry.TopicID == j && currResult.SearchDB == i)
                            {
                                found = true;
                                break;
                            }
                        }

                        if (!found)
                        {
                            var entry = new CMCEntry(0, j, -1);
                            var result = new CMCQueryResult(i, entry, 0, null);

                            results.Add(result, buildWordMap, buildPhraseMap, false);
                        }
                    }
                }

                OnCompleteFunc(results);
            }

            if (mOperand1)
            {
                mOperand1.Evaluate(buildWordMap, buildPhraseMap, OnEvaluateOperand1Complete);
            }
            else
            {
                OnEvaluateOperand1Complete(new CMCQueryResultSet());
            }

            return;
        }
    };

    this.EvaluateWord = function (buildWordMap, buildPhraseMap, OnCompleteFunc)
    {
        function OnLoadChunkComplete()
        {
            completed++;

            if (completed == length)
            {
                stems.ForEach(function (key, value)
                {
                    for (var i = 0; i < gSearchDBs.length; i++)
                    {
                        var searchDB = gSearchDBs[i];

                        if (searchDB.SearchType == "NGram")
                        {
                            for (var j = 0; j < key.length - searchDB.NGramSize + 1; j++)
                            {
                                var subText = key.substring(j, j + searchDB.NGramSize);

                                searchDB.LookupStem(resultSet, subText, i, buildWordMap, buildPhraseMap);
                            }
                        }
                        else
                        {
                            searchDB.LookupStem(resultSet, key, i, buildWordMap, buildPhraseMap);
                        }

                        for (var j = 0; j < resultSet.GetLength(); j++)
                        {
                            var result = resultSet.GetResult(j);

                            if (result.ParentPhraseName == tokenText)
                            {
                                result.Ranking = result.Ranking + 1000;
                            }
                            else if (result.ParentPhraseName.toLowerCase() == tokenText.toLowerCase())
                            {
                                result.Ranking = result.Ranking + 500;
                            }

                            //

                            if (stems.GetLength() > 0 && stemk == 0)
                            {
                                result.Ranking = result.Ranking + 50; // If synonyms are used, give the original stem a little higher ranking
                            }
                        }

                        stemk++;
                    }

                    return true;
                });

                OnCompleteFunc(resultSet);
            }
        }

        var completed = 0;
        var length = 0;

        //

        var tokenText = mToken.GetTokenText();

        //

        var stems = new CMCDictionary();
        var startStem = stemWord(tokenText);

        stems.Add(startStem, true);

        for (var j = 0; j < gSearchDBs.length; j++)
        {
            var searchDB = gSearchDBs[j];

            if (searchDB.SynonymFile != null)
            {
                searchDB.SynonymFile.AddSynonymStems(tokenText, startStem, stems);
            }

            if (searchDB.DownloadedSynonymFile != null)
            {
                searchDB.DownloadedSynonymFile.AddSynonymStems(tokenText, startStem, stems);
            }
        }

        //

        var resultSet = new CMCQueryResultSet();

        var stemk = 0;

        // Calculate "length"
        stems.ForEach(function (key, value)
        {
            for (var i = 0; i < gSearchDBs.length; i++)
            {
                var searchDB = gSearchDBs[i];

                if (searchDB.SearchType == "NGram")
                {
                    for (var j = 0; j < key.length - searchDB.NGramSize + 1; j++)
                    {
                        length++;
                    }
                }
                else
                {
                    length++;
                }
            }

            return true;
        });

        // Load all the chunks first
        stems.ForEach(function (key, value)
        {
            for (var i = 0; i < gSearchDBs.length; i++)
            {
                var searchDB = gSearchDBs[i];

                if (searchDB.SearchType == "NGram")
                {
                    for (var j = 0; j < key.length - searchDB.NGramSize + 1; j++)
                    {
                        var subText = key.substring(j, j + searchDB.NGramSize);

                        searchDB.LoadChunk(subText, OnLoadChunkComplete);
                    }
                }
                else
                {
                    searchDB.LoadChunk(key, OnLoadChunkComplete);
                }
            }

            return true;
        });
    };

    this.EvaluatePhrase = function (buildWordMap, buildPhraseMap, OnCompleteFunc)
    {
        function OnLoadChunkComplete()
        {
            completed++;

            if (completed == length)
            {
                for (var i = 0; i < terms.length; i++)
                {
                    var term = terms[i];
                    var resultSet2 = new CMCQueryResultSet();

                    //

                    var stems = new CMCDictionary();
                    var startStem = stemWord(term);

                    stems.Add(startStem, true);

                    //

                    stems.ForEach(function (key, value)
                    {
                        for (var j = 0; j < gSearchDBs.length; j++)
                        {
                            var searchDB = gSearchDBs[j];

                            if (searchDB.SearchType == "NGram")
                            {
                                for (var k = 0; k < key.length - searchDB.NGramSize + 1; k++)
                                {
                                    var subText = key.substring(k, k + searchDB.NGramSize);

                                    searchDB.LookupStem(resultSet2, subText, j, true, buildPhraseMap);
                                }
                            }
                            else
                            {
                                searchDB.LookupStem(resultSet2, key, j, true, buildPhraseMap);
                            }
                        }

                        return true;
                    });

                    if (!resultSet)
                    {
                        resultSet = resultSet2;
                        continue;
                    }

                    var newResultSet = resultSet.ToPhrases(resultSet2, mToken, true, buildPhraseMap);

                    if (newResultSet.GetLength() == 0)
                    {
                        OnCompleteFunc(newResultSet);
                        return;
                    }

                    resultSet = newResultSet;
                }

                if (!resultSet)
                {
                    resultSet = new CMCQueryResultSet();
                }

                OnCompleteFunc(resultSet);
            }
        }

        var completed = 0;
        var length = 0;

        //

        var tokenText = mToken.GetTokenText();
        var terms = SplitPhrase(tokenText);
        var resultSet = null;

        for (var i = 0; i < terms.length; i++)
        {
            var term = terms[i];

            //

            var stems = new CMCDictionary();
            var startStem = stemWord(term);

            stems.Add(startStem, true);

            for (var j = 0; j < gSearchDBs.length; j++)
            {
                var searchDB = gSearchDBs[j];

                if (searchDB.SynonymFile != null)
                {
                    searchDB.SynonymFile.AddSynonymStems(tokenText, startStem, stems);
                }

                if (searchDB.DownloadedSynonymFile != null)
                {
                    searchDB.DownloadedSynonymFile.AddSynonymStems(tokenText, startStem, stems);
                }
            }
        }

        // Calculate "length"
        for (var i = 0; i < terms.length; i++)
        {
            var term = terms[i];

            //

            var stems = new CMCDictionary();
            var startStem = stemWord(term);

            stems.Add(startStem, true);

            stems.ForEach(function (key, value)
            {
                for (var j = 0; j < gSearchDBs.length; j++)
                {
                    var searchDB = gSearchDBs[j];

                    if (searchDB.SearchType == "NGram")
                    {
                        for (var k = 0; k < key.length - searchDB.NGramSize + 1; k++)
                        {
                            length++;
                        }
                    }
                    else
                    {
                        length++;
                    }
                }

                return true;
            });
        }

        // Load all the chunks first
        for (var i = 0; i < terms.length; i++)
        {
            var term = terms[i];

            //

            var stems = new CMCDictionary();
            var startStem = stemWord(term);

            stems.Add(startStem, true);

            stems.ForEach(function (key, value)
            {
                for (var j = 0; j < gSearchDBs.length; j++)
                {
                    var searchDB = gSearchDBs[j];

                    if (searchDB.SearchType == "NGram")
                    {
                        for (var k = 0; k < key.length - searchDB.NGramSize + 1; k++)
                        {
                            var subText = key.substring(k, k + searchDB.NGramSize);

                            searchDB.LoadChunk(subText, OnLoadChunkComplete);
                        }
                    }
                    else
                    {
                        searchDB.LoadChunk(key, OnLoadChunkComplete);
                    }
                }

                return true;
            });
        }
    };

	this.GetToken   = function ()
	{
		return mToken;
	};
}

//
//    End class CMCNode
//

//
//    Class CMCEntry
//

function CMCEntry( rank, topicID, word )
{
    // Private member variables
    
    this.Rank		= rank;
    this.TopicID	= topicID;
    this.Word		= word;
}

//
//    End class CMCEntry
//

//
//    Class CMCQueryResult
//

function CMCQueryResult( dbIndex, entry, rank, parentPhraseName )
{
    // Public properties
    
    this.SearchDB			= dbIndex;
    this.Entry				= entry;
    this.Ranking			= rank;
    this.ParentPhraseName	= parentPhraseName;
    this.RankPosition		= 0;
}

//
//    End class CMCQueryResult
//

//
//    Class CMCQueryResultSet
//

function CMCQueryResultSet()
{
    // Public properties
    
    this.mResults	= new Array();
    this.SortColumn	= null;
    this.SortOrder	= null;
    this.mWordMap	= new CMCDictionary();
    this.mPhraseMap	= new CMCDictionary();
    this.mTopicMap	= new CMCDictionary();
}

CMCQueryResultSet.prototype.Add	= function( result, buildWordMap, buildPhraseMap, buildTopicMap )
{
    this.mResults[this.mResults.length] = result;
    
    var searchDB	= result.SearchDB;
    var entry		= result.Entry;
    
    if ( buildWordMap )
    {
		var key	= searchDB + "_" + entry.TopicID + "_" + entry.Word;
		
		this.mWordMap.Add( key, result );
    }
    
    if ( buildPhraseMap )
    {
		var key	= result.ParentPhraseName + "_" + searchDB + "_" + entry.TopicID + "_" + entry.Word;
		
		this.mPhraseMap.Add( key, true );
    }
    
    if ( buildTopicMap )
    {
		var key			= searchDB + "_" + entry.TopicID;
		var indexList	= this.mTopicMap.GetItem( key );
		
		if ( !indexList )
		{
			indexList = new Array();
			this.mTopicMap.Add( key, indexList );
		}
		
		indexList[indexList.length] = this.mResults.length - 1;
    }
};

CMCQueryResultSet.prototype.AddAllUnique	= function( results, buildWordMap, buildPhraseMap )
{
	var count	= results.GetLength();
	
    for ( var i = 0; i < count; i++ )
    {
        var result      = results.GetResult( i );
        var entry       = result.Entry;
        var searchDB    = result.SearchDB;
        var phrase      = result.ParentPhraseName;
        var rank        = entry.Rank;
        var topic       = entry.TopicID;
        var word        = entry.Word;
        var key         = phrase + "_" + searchDB + "_" + topic + "_" + word;
        
        if ( this.mPhraseMap.GetItem( key ) )
        {
            continue;
        }
        
        this.Add( result, buildWordMap, buildPhraseMap, false );
    }
};

CMCQueryResultSet.prototype.Compact	= function()
{
    var newResults  = new Array();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        if ( this.mResults[i] )
        {
            newResults[newResults.length] = this.mResults[i];
        }
    }
    
    this.mResults = newResults;
};

CMCQueryResultSet.prototype.GetLength	= function()
{
    return this.mResults.length;
};

CMCQueryResultSet.prototype.GetResult	= function( i )
{
    return this.mResults[i];
};

CMCQueryResultSet.prototype.GetWordMap	= function()
{
	return this.mWordMap;
};

CMCQueryResultSet.prototype.RemoveAt	= function( i )
{
    this.mResults[i] = null;
};

CMCQueryResultSet.prototype.RemoveTopicId	= function( result )
{
    var topic       = result.Entry.TopicID;
    var searchDB    = result.SearchDB;
    var topicKey	= searchDB + "_" + topic;
    var indexList	= this.mTopicMap.GetItem( topicKey );
    
    if ( indexList )
    {
		for ( var i = 0; i < indexList.length; i++ )
		{
			var currResult	= this.mResults[indexList[i]];
			var entry		= currResult.Entry;
			var wordKey		= searchDB + "_" + topic + "_" + entry.Word;
			var phraseKey	= currResult.ParentPhraseName + "_" + searchDB + "_" + topic + "_" + entry.Word;
			
			this.mWordMap.Remove( wordKey );
			this.mPhraseMap.Remove( phraseKey );
			this.mTopicMap.Remove( topicKey );
			
			//
			
			this.RemoveAt( indexList[i] );
		}
    }
};

CMCQueryResultSet.prototype.ShallowClone	= function( buildWordMap, buildPhraseMap, buildTopicMap )
{
    var resultSet   = new CMCQueryResultSet();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        resultSet.Add( this.mResults[i], buildWordMap, buildPhraseMap, buildTopicMap );
    }
    
    return resultSet;
};

CMCQueryResultSet.prototype.Sort	= function( sortCol, sortOrder )
{
	this.SortColumn = sortCol;
    this.SortOrder = sortOrder;
    
    gCompareSet = this;
    this.mResults.sort( this.CompareResults );
    gCompareSet = null;
};

CMCQueryResultSet.prototype.SetRankPositions	= function()
{
	var sortCol = this.SortColumn;
    var sortOrder = this.SortOrder;

    this.SortColumn = EMCSortColumn.Rank;
    this.SortOrder = -1;
    
    gCompareSet = this;
    this.mResults.sort( this.CompareResults );
    gCompareSet = null;
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        this.mResults[i].RankPosition = i + 1;
    }

    this.SortColumn = sortCol;
    this.SortOrder = sortOrder;
};

var gCompareSet	= null;

CMCQueryResultSet.prototype.ToDifference	= function( results, buildWordMap, buildPhraseMap )
{
    var newResults  = this.ShallowClone( buildWordMap, buildPhraseMap, true );
    
    for ( var i = 0; i < results.GetLength(); i++ )
    {
		newResults.RemoveTopicId( results.GetResult( i ) );
    }
    
    newResults.Compact();
    
    return newResults;
};

CMCQueryResultSet.prototype.ToIntersection	= function( results, buildWordMap, buildPhraseMap )
{
    var newResults  = new CMCQueryResultSet();
    var map1        = new CMCDictionary();
    var map2        = new CMCDictionary();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
		var result	= this.mResults[i];
        var key		= result.SearchDB + "_" + result.Entry.TopicID;
        
        map1.Add( key, true );
    }
    
    for ( var i = 0; i < results.GetLength(); i++ )
    {
		var result	= results.GetResult( i );
        var key		= result.SearchDB + "_" + result.Entry.TopicID;
        
        map2.Add( key, true );
    }
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        var result  = this.mResults[i];
        var key     = result.SearchDB + "_" + result.Entry.TopicID;
        
        if ( map2.GetItem( key ) )
        {
            newResults.Add( result, buildWordMap, buildPhraseMap, false );
        }
    }
    
    for ( var i = 0; i < results.GetLength(); i++ )
    {
        var key = results.GetResult( i ).SearchDB + "_" + results.GetResult( i ).Entry.TopicID;
        
        if ( map1.GetItem( key ) )
        {
            newResults.Add( results.GetResult( i ), buildWordMap, buildPhraseMap, false );
        }
    }
    
    return newResults;
};

CMCQueryResultSet.prototype.ToMerged	= function()
{
    var mergedSet   = new CMCQueryResultSet();
    var map         = new CMCDictionary();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        var result  = this.mResults[i];
        var key     = result.SearchDB + "_" + this.mResults[i].Entry.TopicID;
        var item	= map.GetItem( key );
        
        if ( item )
        {
            item.Ranking = item.Ranking + result.Ranking;
            continue;
        }
        
        map.Add( key, result );
        mergedSet.Add( result, false, false, false );
    }
    
    return mergedSet;
};

CMCQueryResultSet.prototype.ToPhrases	= function( results, token, buildWordMap, buildPhraseMap )
{
    if ( !results )
    {
		var set1	= new CMCQueryResultSet();
		
        return set1;
    }
    
    var adjacentEntries = this.FindAdjacentEntries( results, token, buildWordMap, buildPhraseMap );
    
    return adjacentEntries;
};

CMCQueryResultSet.prototype.ToUnion	= function( results, buildWordMap, buildPhraseMap )
{
    this.AddAllUnique( results, buildWordMap, buildPhraseMap );
};

// (Should be) Private member functions

CMCQueryResultSet.prototype.CompareResults	= function( a, b )
{
    var ret;

    if (gCompareSet.SortColumn == EMCSortColumn.Rank)
    {
        var rank1   = a.Ranking;
        var rank2   = b.Ranking;
        
        ret = rank1 - rank2;
    }
    else if (gCompareSet.SortColumn == EMCSortColumn.RankPosition)
    {
        var pos1    = a.RankPosition;
        var pos2    = b.RankPosition;
        
        ret = pos1 - pos2;
    }
    else if (gCompareSet.SortColumn == EMCSortColumn.Title)
    {
        var searchDB    = a.SearchDB;
        var entry       = a.Entry;
        var topicID     = entry.TopicID;
        var title1      = gSearchDBs[searchDB].URLTitles[topicID] ? gSearchDBs[searchDB].URLTitles[topicID] : "";
        
        searchDB = b.SearchDB;
        entry = b.Entry;
        topicID = entry.TopicID;
        var title2  = gSearchDBs[searchDB].URLTitles[topicID] ? gSearchDBs[searchDB].URLTitles[topicID] : "";
        
        if ( title1 < title2 )
        {
            ret = -1;
        }
        else if ( title1 == title2 )
        {
            ret = 0;
        }
        else if ( title1 > title2 )
        {
            ret = 1;
        }
    }
    
    return (ret * gCompareSet.SortOrder);
}

CMCQueryResultSet.prototype.FindAdjacentEntries	= function( results, token, buildWordMap, buildPhraseMap )
{
    var newResults	= new CMCQueryResultSet();
    var wordList	= SplitPhrase( token.GetTokenText() );
    var wordListMap	= new CMCDictionary();
    
    for ( var j = 0; j < wordList.length; j++ )
    {
        wordListMap.Add( wordList[j], true );
    }
    
    var wordMap	= results.GetWordMap();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        var result		= this.mResults[i];
        var entry		= result.Entry;
        var searchDB	= result.SearchDB;
        var rank		= entry.Rank;
        var topic		= entry.TopicID;
        var word		= entry.Word;
        var key			= searchDB + "_" + topic + "_" + (parseInt( word ) + 1);
        var nextResult	= wordMap.GetItem( key );
        
        if ( nextResult )
        {
            if ( wordListMap.GetItem( nextResult.ParentPhraseName ) && wordListMap.GetItem( result.ParentPhraseName ) )
            {
                nextResult.Ranking = result.Ranking + 10000;
            }
            else
            {
                nextResult.Ranking = result.Ranking + 1000;
            }
            
            newResults.Add( nextResult, buildWordMap, buildPhraseMap, false );
        }
    }
    
    return newResults;
}

CMCQueryResultSet.prototype.PromotePhrases	= function( results, token )
{
    var wordList	= SplitPhrase( token.GetTokenText() );
    var wordListMap	= new CMCDictionary();
    
    for ( var j = 0; j < wordList.length; j++ )
    {
        wordListMap.Add( wordList[j], true );
    }
    
    var wordMap	= results.GetWordMap();
    
    for ( var i = 0; i < this.mResults.length; i++ )
    {
        var result		= this.mResults[i];
        var entry		= result.Entry;
        var searchDB	= result.SearchDB;
        var rank		= entry.Rank;
        var topic		= entry.TopicID;
        var word		= entry.Word;
        var key			= searchDB + "_" + topic + "_" + (parseInt( word ) - 1);
        var nextResult	= wordMap.GetItem( key );
        
        if ( nextResult )
        {
            if ( wordListMap.GetItem( nextResult.ParentPhraseName ) && wordListMap.GetItem( result.ParentPhraseName ) )
            {
                nextResult.Ranking = result.Ranking + 10000;
            }
            else
            {
                nextResult.Ranking = result.Ranking + 1000;
            }
        }
    }
}

//
//    End class CMCQueryResultSet
//

function SplitPhrase( text )
{
	var terms		= null;
	var searchDB	= gSearchDBs[0];
	
	if ( searchDB.SearchType == "NGram" )
	{
		terms = new Array( Math.max( 0, text.length - (searchDB.NGramSize + 1) ) );
		
		for ( var i = 0; i < text.length - searchDB.NGramSize + 1; i++ )
		{
			terms[i] = text.substring( i, i + searchDB.NGramSize );
		}
	}
	else
	{
		terms = text.split( " " );
	}
	
	return terms;
}

if ( gRuntimeFileType == "Search" )
{
}
