/*globals MessageBox, ClusterPane, CollectionStatusPanel, DWRHelper, DWRUtil, Paginator, 
 ResultListConnector, URLBuilder, getImageDir, YAHOO, formatInteger, ResultsEmailerUI,
 YahooDlgHelper, MarkedListConnector, ClippingsManager, PreferencesConnector, CoralUtils */

/*jslint forin:true, undef:true, widget:true, browser:true */

/**
 * Definition of the ResultList object that defines the methods and attributes relevant
 * to managing the result list page.
 */
function ResultList(ssid, tabCount, refreshMode, searchId, displayMoreResultsDialog, filters)
{
    // Set the instance
    ResultList.instance = this;

    // The globally unique ID for the current search session.
    this.ssid = ssid;

    // The id of the current search
    this.searchId = searchId;

    // The number of tabs that will be displayed
    this.tabCount = tabCount;

    // The index to the currently selected tab
    this.currentTabIndex = 0;

    // Whether to prompt the user to refresh results or not at end of search.
    this.refreshMode = refreshMode;

    // The array of result panes that will be displayed
    this.resultPanes = [];

    // The array of cluster panes that will be displayed
    this.clusterPanes = [];

    // The most current search status object.
    this.resultListStatus = null;

    // The sum of all displayable results from all containers w/o any collection limits.
    this.displayTotal = 0;

    // The list of filters applied to the current view.
    this.filters = filters;

    // The list of the panes that are currently active in each tab.  This
    // is used to display the pane when the tabs are switched.
    this.activeTabPanes = [];

    // The current number of marked results.
    this.markedCount = 0;

    // The currently selected tab
    this.selectedTab = null;

    // The container of the currently selected tab
    this.selectedContainer = null;

    //the YUI search-popup-window element
    this.searchPopup = null;

    // The number of milliseconds to wait for getCollectionStatus requests.
    this.statusTimeout = 2000;

    // True if the more results dialog should be displayed when search is finished
    this.displayMoreResultsDialog = displayMoreResultsDialog;

    // Set when the state of a selected cluster needs to be restored.
    this.clusterInitialState = null;

    // The spell checker object
    this.spellSuggest = new Object();

}

/**
 * This is a reference to the single instance of the resultList object
 */
ResultList.instance = null;

// The text that will be displayed on the search popup window
ResultList.searchPopupButton1Text = "searchPopupButton1Text";
ResultList.searchPopupButton2Text = "searchPopupButton2Text";
ResultList.emailErrorTitle = "emailErrorTitle";
ResultList.emailErrorMessage = "emailErrorMessage";
ResultList.sessionExpiredTitle = "sessionExpiredTitle";
ResultList.sessionExpiredMessage = "sessionExpiredMessage";
ResultList.btnOk = "<<uninitialized>>";

ResultList.pct0 = "zero-percent";
ResultList.pct10 = "ten-percent";
ResultList.pct20 = "twenty-percent";
ResultList.pct30 = "thirty-percent";
ResultList.pct40 = "forty-percent";
ResultList.pct50 = "fifty-percent";
ResultList.pct60 = "sixty-percent";
ResultList.pct70 = "seventy-percent";
ResultList.pct80 = "eighty-percent";
ResultList.pct90 = "ninety-percent";
ResultList.pct100 = "one-hundred-percent";
ResultList.spellFieldSummary = "<<uninitialized>>";
ResultList.spellFieldSnippet = "<<uninitialized>>";
ResultList.spellFieldFullRecord = "<<uninitialized>>";
ResultList.fullRecordLbl = "<<uninitialized>>";

// The module name that will be used in the yahoo ui history manager
ResultList.moduleName = "ResultList";

/**
 * Handles ajax errors that occur on this page
 * @param methodName the name of the method that failed
 * @param message the message from the exception that was thrown
 * @param info furhter information on the exception that was thrown.
 */
ResultList.pageErrorHandler = function(methodName, message, info)
{
    var buttons;

    // if the search session has expired
    if (info.javaClassName && info.javaClassName.indexOf("ExpiredSearchSessionException") != -1)
    {
        buttons = [ { text: ResultList.btnOk,   isDefault:true, handler: function()
        {
            window.location.reload();
        } }];

        MessageBox.displayDialog(ResultList.sessionExpiredTitle, ResultList.sessionExpiredMessage,
                buttons, YAHOO.widget.SimpleDialog.ICON_WARN);
    }
    else
    {
        DWRHelper.displayDWRError(methodName, message, info);
    }
};

/**
 * function called following the update of the session preferences
 * @param prefs the new preferences object
 */
ResultList.onUpdatePreferences = function(prefs)
{
    if (prefs.resultsPerPage != Paginator.resultsPerPage)
    {
        Paginator.resultsPerPage = prefs.resultsPerPage;
        this.refreshMode = prefs.refreshMode;
        ResultList.instance.refreshResults();
    }
};

/**
 * Reloads the currently selected page of results.
 */
ResultList.prototype.refreshResults = function()
{
    this.getActivePane().getPageOfResults(0, false, this.ssid, this.filters);
};

/**
 * Returns the curently active result pane object
 */
ResultList.prototype.getActivePane = function()
{
    return this.activeTabPanes[this.currentTabIndex];
};

ResultList.prototype.getDefaultState = function()
{
    return this.currentTabIndex + "|" +
           "0" + "|" +
           "_" + "|" +
           "RANK" + "|" +
           "_";
};
/**
 * Gets the initial state from the yahoo history
 */
ResultList.prototype.initializeHistory = function()
{
    var initialState = YAHOO.util.History.getBookmarkedState(ResultList.moduleName);

    if (!initialState)
    {
        initialState = this.getDefaultState();
    }

    // Set the inital history state
    YAHOO.util.History.register(ResultList.moduleName, initialState, this.onHistoryChange, this, true);
};

/**
 * Post constructor call to intialize AJAX calls to update the resultListStatus
 * and collectionStatus.
 * Should be called after all other attributes have been initialized and you
 * are ready to start actively polling for status.
 */
ResultList.prototype.init = function()
{
    if (top != window)
    {
        this.initPage();
    }
    else
    {
        this.initializeHistory();

        YAHOO.util.History.onReady(
                function ()
                {
                    ResultList.instance.initPage();
                }
                );

        YAHOO.util.History.initialize("yui-history-field", "yui-history-iframe");
    }
};

/**
 * Setup the Page with onclick handlers for the tabs and select the appropriate
 * tab to display initially based on the resultPane value.
 */
ResultList.prototype.initPage = function()
{
    this.initClusterPanes();

    for (var i = 0; i < this.tabCount; ++i)
    {
        this.activeTabPanes[i] = this.resultPanes[i];
    }

    var state = this.getCurrentState();
    this.switchTabs(state.currentTabIndex, state);

    this.getResultSearchStatus(this.ssid, this.filters, 0);

    this.getSpellingSuggestion();

    PreferencesConnector.getUserPreferences(ResultList.onUpdatePreferences);

    this.createSearchPopup();
};

/**
 * Initializes the clusterPane objects by adding the resultList class as
 * a listener for events fired from the cluster pane.
 */
ResultList.prototype.initClusterPanes = function()
{
    var i;

    for (i = 0; i < this.clusterPanes.length; ++i)
    {
        this.clusterPanes[i].addListener(this);
    }
};

/**
 * Perform any task related to a search status update.  This method will always be
 * called after the resultListStatus object has been updated via AJAX.
 */
ResultList.prototype.updateSearchStatus = function()
{
    // if there aren't any results for the active pane,
    if (this.getActivePane().displayCount === 0 && this.resultListStatus.paneCounts[this.currentTabIndex] > 0)
    {
        // If this is a reloadiing of a cluster, first get the clusters.
        if (this.clusterInitialState)
        {
            // Get the clusters for this pane
            this.clusterPanes[this.currentTabIndex].getClusters(this.ssid, this.isInActiveTab(this.currentTabIndex),
                    this.resultListStatus.paneCounts[this.currentTabIndex], this.isSearchFinished(),
                    this.filters);
        }
        else
        {
            // Get the results for the visible pane
            this.showPage(-1, true);
        }
    }

    this.updateSearchProgress();

    this.updateResultCounts();
};

/**
 * Sets the local resultListStatus attribute to the new value. Calls updateSearchStatus
 * to allow any additional updates to be made.  If the search is not finished, issues
 * the next AJAX request to refresh the resultListStatus.
 * @param resultListStatus the current status of the search
 */
ResultList.prototype.setResultListStatus = function(resultListStatus)
{
    this.resultListStatus = resultListStatus;

    // Update any displayed information about the search
    this.updateSearchStatus();

    // if search is finished...
    if (this.isSearchFinished())
    {
        this.handleSearchFinished();
    }
    else
    {
        this.getResultSearchStatus(this.ssid, this.filters, this.statusTimeout);
    }
};

/**
 * Returns the total filtered results as the sum of the paneCounts for each pane
 */
ResultList.prototype.getTotalFilteredResults = function()
{
    var totalFilteredResults = 0;
    for (var i in this.resultListStatus.paneCounts)
    {
        totalFilteredResults += this.resultListStatus.paneCounts[i];
    }

    return totalFilteredResults;
};

/**
 * Tests whether search is finished.
 * @return true if search is finished
 */
ResultList.prototype.isSearchFinished = function()
{
    if (this.resultListStatus === null)
    {
        return false;
    }
    else
    {
        return this.resultListStatus.searchFinished;
    }
};

/**
 * Retrieve the current state of the search using an AJAX call.
 * @param ssid the session state id
 * @param filters the filters to apply to the results
 * @param timeout the number of seconds to wait before getting the status
 */
ResultList.prototype.getResultSearchStatus = function(ssid, filters, timeout)
{
    var statusFunction = function()
    {
        ResultListConnector.getResultSearchStatus(ssid, filters, ResultList.getResultSearchStatusDWRObject);
    };

    if (timeout === 0)
    {
        statusFunction();
    }
    else
    {
        window.setTimeout(statusFunction, timeout);
    }
};

/**
 * Callback function to the method ResultListConnector.getResultSearchStatus
 * @param resultSearchStatus the resultSearchStatus object
 */
ResultList.getResultSearchStatus_callback = function(resultSearchStatus)
{
    //set the reslutListStatus
    ResultList.instance.setResultListStatus(resultSearchStatus.resultListStatus);

    // if there is a collection status panel configured
    if (document.getElementById("collectionStatusPanel"))
    {
        CollectionStatusPanel.setCollectionStatus(resultSearchStatus.collectionStatus);
    }
};


/**
 * Callback function to the method ResultListConnector.getResultSearchStatus
 * @param data the data received from the AJAX call.
 */
ResultList.getResultSearchStatus_errorHandler = function(message, info)
{
    ResultList.pageErrorHandler("ResultListConnector.getResultSearchStatus", message, info);
};

/**
 * The object used when making calls to ResultListConnector.getResultSearchStatus
 */
ResultList.getResultSearchStatusDWRObject = {
    callback:     ResultList.getResultSearchStatus_callback,
    errorHandler: ResultList.getResultSearchStatus_errorHandler
};

/**
 * Makes an AJAX call to ResultlistConnector.getSpellingSuggestion and passes it
 * the full record setarch string.
 */
ResultList.prototype.getSpellingSuggestion = function()
{
    var expression = "";

    if(ResultList.instance.spellSuggest.multipleFields == "true")
    {
        for(var i = 0; i < ResultList.instance.spellSuggest.fieldList.length; i++)
        {
            //Change this value so we can grab the right value from the URL
            if(ResultList.instance.spellSuggest.fieldList[i] == ResultList.spellFieldSummary)
            {
                ResultList.instance.spellSuggest.fieldList[i] = ResultList.spellFieldSnippet;
            }

            var regexpString = "/"+ResultList.instance.spellSuggest.fieldList[i]+":(.*?)/";
            var regexp = new RegExp(regexpString);
            var myMatch = window.location.href.match(regexp);
            var field = null;

            if (myMatch)
            {
                // We take the SECOND element in the array because it contains what was
                // matched bewtween the parens in the regexp
                field = myMatch[1];

                // Decode the full record match...  Then replace the + with spaces
                field = decodeURIComponent(field);
                field = field.replace(/\+/g, " ");

                if (expression === "")
                {
                    expression = field;
                }
                else
                {
                    expression = expression + "!#!" + field;
                }
            }
            else
            {
                if(expression === "")
                {
                    expression = "NO_FIELD_ENTRY";
                }
                else
                {
                    expression = expression + "!#!" + "NO_FIELD_ENTRY";
                }
            }
        }
    }
    else
    {
        var regexp = /fullRecord:(.*?)\//;
        var myMatch = window.location.href.match(regexp);

        if(myMatch)
        {
            // Decode the full record match...  Then replace the + with spaces
            myMatch[1] = decodeURIComponent(myMatch[1]);
            myMatch[1] = myMatch[1].replace(/\+/g, " ");
            expression = myMatch[1];
        }
    }

    ResultListConnector.getSpellingSuggestion(expression, ResultList.getSpellingSuggestionDWRObject);
};


/**
 * Callback function to the method ResultListConnector.getSpellingSuggestion
 * Displays the spelling suggestion on the view if it is not empty
 * @param data the data received from the AJAX call.
 */
ResultList.getSpellingSuggestion_callback = function(suggestion)
{
    if (suggestion)
    {
        var linkString = "";

        if(ResultList.instance.spellSuggest.multipleFields == "true")
        {
            var suggestionList = [];

            suggestionList = suggestion.split("!#!");

            for(var i = 0; i < ResultList.instance.spellSuggest.fieldList.length; i++)
            {
                //Match the suggestion to the field name
                if(!(suggestionList[i] == "NO_FIELD_ENTRY"))
                {

                    var displayString = ResultList.instance.spellSuggest.fieldList[i];

                    //Change this so it matches the UI
                    if(ResultList.instance.spellSuggest.fieldList[i] == ResultList.spellFieldSnippet)
                    {
                        displayString = ResultList.spellFieldSummary;
                    }

                    //Fix the fullRecord camel case
                    if(displayString == ResultList.spellFieldFullRecord)
                    {
                        displayString  =  ResultList.fullRecordLbl;
                    }

                    //capitalize the first letter of the field
                    var firstLetter = displayString.slice(0,1);
                    firstLetter = firstLetter.toUpperCase();

                    displayString = firstLetter + displayString.slice(1, displayString.length);

                    linkString = linkString + displayString + ": " + suggestionList[i] + " / ";
                }
            }

            //Remove the last slash
            linkString =  linkString.slice(0, -3);

        }
        else
        {
            linkString = suggestion;
        }

        document.getElementById("spellBlock").style.display = "inline";
        document.getElementById("spellingSuggestion").innerHTML = decodeURIComponent(linkString);
    }
    else
    {
        var node = document.getElementById("spellBlock");
        var parent = node.parentNode;
        parent.removeChild(node);
    }
};

/**
 * This method is called when the user clicks on the link to rerun the search.
 * It puts together a URL from the spelling suggestion data.
 */
ResultList.prototype.rerunSearchWithSpellSuggestions = function()
{
    var i, nameValue, suggestString = "";
    var suggestions = document.getElementById('spellingSuggestion').innerHTML;

    if(ResultList.instance.spellSuggest.multipleFields == "true")
    {
        suggestions = suggestions.split("/");

        for (i = 0; i < suggestions.length; i++)
        {
            suggestions[i] = YAHOO.lang.trim(suggestions[i]);
            nameValue = suggestions[i].split(":");
            nameValue[1] = YAHOO.lang.trim(nameValue[1]);

            suggestString += ResultList.instance.spellSuggest.fieldList[i] + ":" + encodeURIComponent(nameValue[1]) + "/";
        }
    }
    else
    {
        suggestString = "fullRecord:" + suggestions;
    }
    window.location.href = Security.contextPath + "/search/get:1/newSearch:true/ssid:" + encodeURIComponent(this.ssid) + "/" + suggestString;
};

/**
 * Error handler from the call to getSpellingSuggestion
 * @param message the error message
 * @param info a stack trace, and futher info
 */
ResultList.getSpellingSuggestion_errorHandler = function(message, info)
{
    if (DWRHelper.debugMode)
    {
        ResultList.pageErrorHandler("ResultListConnector.getSpellingSuggestion", message, info);
    }
};

/**
 * The object used when making calls to getSpellingSuggestion
 */
ResultList.getSpellingSuggestionDWRObject = {
    callback:     ResultList.getSpellingSuggestion_callback,
    timeout:      DWRHelper.ajaxTimeout,
    errorHandler: ResultList.getSpellingSuggestion_errorHandler
};


/**
 * Reload the current page at a new starting result location, and refreshing
 * the result snapshot, if needed
 * @param newStart the start position in the result list to display
 * @param refresh true if a new snapshot should be generated.
 */
ResultList.prototype.showPage = function(newStart, refresh)
{
    if (newStart == -1)
    {
        newStart = this.getActivePane().startPosition;
    }
    this.getActivePane().getPageOfResults(newStart, refresh, this.ssid, this.filters);
};

/**
 * Adds all of the outstanding results to the displayed page by first setting
 * each pane's displayCount to 0 and then refreshing the currently displayed page
 */
ResultList.prototype.addResults = function()
{
    this.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));

    // Loop through all of the top level panes and set their display count to 0
    // This will ensure that they will get reloaded the next time that their tab is clicked.
    for (var i = 0; i < this.tabCount; ++i)
    {
        this.resultPanes[i].displayCount = 0;
        this.resultPanes[i].resultCount = 0;
        this.resultPanes[i].resetSelectedCollection();
        this.updateResultCounter(i);
    }

    // if the selected pane is a cluster pane, select its corresponding top level pane
    // so that the results and clusters can get updated
    if (this.getActivePane().paneId >= this.tabCount)
    {
        this.activeTabPanes[this.currentTabIndex] = this.resultPanes[this.getActivePane().paneId - this.tabCount];
    }

    // Refresh the active pane
    this.showPage(0, true);

    if (this.isSearchFinished())
    {
        // Hide the result count indicator
        document.getElementById("more-results-info").style.visibility = "hidden";
    }
};

/**
 * Returns true if there is a cluster selected
 */
ResultList.prototype.isClusterSelected = function()
{
    return (this.getActivePane().paneId >= this.tabCount);
};

/**
 * Reorders the results on the page by changing the ordering parameter and re-reading
 * the results.
 * @param orderBy the means in which to order the results
 */
ResultList.prototype.reorderResults = function(orderBy)
{
    ResultList.instance.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));

    // Set the ordering in the active pane and then re-get the results
    this.getActivePane().resultSortField = orderBy;
    this.showPage(0, false);
};

/**
 * Emails the current result listing.
 */
ResultList.prototype.emailResults = function()
{
    if (this.getActivePane().displayCount > 0)
    {
        ResultsEmailerUI.showDialogForSearchResults(this.ssid, this.currentTabIndex,
                this.getActivePane().paneId, this.getActivePane().resultSortField,
                this.getActivePane().selectedCollection);
    }
    else
    {
        MessageBox.error(ResultList.emailErrorTitle, ResultList.emailErrorMessage);
    }
};


/**
 * View results in the current snap shot only from the given collectionCode
 * @param collectionCode the collecion to get results for
 */
ResultList.prototype.viewCollection = function(collectionCode)
{
    ResultList.instance.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));

    this.getActivePane().setSelectedCollection(collectionCode);
    this.showPage(0, false);
};

//------------------------------------------------------------------------------
// Functions for paging through the list of results
//------------------------------------------------------------------------------

/**
 * Gets the page of results from the current pane starting at newStart
 */
ResultList.prototype.gotoPage = function(newStart)
{
    this.getActivePane().gotoPage(newStart, this.ssid, this.filters);
};

/**
 * Shows the previous result page
 */
ResultList.prototype.showPrevious = function()
{
    this.getActivePane().showPrevious(this.ssid, this.filters);
};

/**
 * Shows the next result page
 */
ResultList.prototype.showNext = function()
{
    this.getActivePane().showNext(this.ssid, this.filters);
};

/**
 * Shows the first page of results
 */
ResultList.prototype.showFirst = function()
{
    this.getActivePane().showFirst(this.ssid, this.filters);
};

/**
 * Shows the last page of results
 */
ResultList.prototype.showLast = function()
{
    this.getActivePane().showLast(this.ssid, this.filters);
};

/**
 * Updates the result counts when the search status is updated.
 */
ResultList.prototype.updateResultCounts = function()
{
    // Update the count for each tab with the number of results that have been found.
    for (var i = 0; i < this.tabCount; ++i)
    {
        // Update the counts on the tab label
        var resultCountEle = document.getElementById("results_count_" + i);
        if (resultCountEle !== null)
        {
            var tabDisplayedCount = resultCountEle.innerHTML;
            if (this.resultPanes[i].displayCount === 0 || tabDisplayedCount == "0" || tabDisplayedCount === "")
            {
                // Update the result count element with the number of results
                this.updateResultCounter(i);
            }
        }
    }

    this.calculateDisplayTotal();

    var unseenResultCount = this.getTotalFilteredResults() - this.displayTotal;
    DWRUtil.setValue("unseenResultCount", unseenResultCount);

    var el = document.getElementById("more-results-info");

    // If there are unseen results and either we already have displayed some results
    // or the search has finished then show the link.
    if (unseenResultCount > 0 && (this.displayTotal > 0 || this.isSearchFinished()))
    {
        el.style.visibility = "visible";
    }
    else
    {
        el.style.visibility = "hidden";
    }

    this.updateTopResults();
};

/**
 * Updates the ui widgets that show the progress of the search
 */
ResultList.prototype.updateSearchProgress = function()
{
    var bar = document.getElementById("loading-bar");

    //Take the hidden collections out of the total number of collections
    var totalCompleted = this.resultListStatus.completedCollections - CollectionStatusPanel.hiddenCollectionCodes.length;
    var totalCollections = this.resultListStatus.totalNumberCollections - CollectionStatusPanel.hiddenCollectionCodes.length;

    if(totalCompleted < 0)
    {
        totalCompleted = 0;
    }

    DWRUtil.setValue("totalCollections", DWRUtil.toDescriptiveString(totalCollections));
    DWRUtil.setValue("totalCompleted", DWRUtil.toDescriptiveString(totalCompleted));

    var percentComplete = (this.resultListStatus.completedCollections == this.resultListStatus.totalNumberCollections) ?
                          1.0 : this.resultListStatus.completedCollections / this.resultListStatus.totalNumberCollections;

    var image = Math.floor(percentComplete * 10);

    switch (image)
            {
        case 0 :
            bar.className =  ResultList.pct0;
            break;
        case 1:
            bar.className = ResultList.pct10;
            break;
        case 2:
            bar.className = ResultList.pct20;
            break;
        case 3:
            bar.className = ResultList.pct30;
            break;
        case 4:
            bar.className = ResultList.pct40;
            break;
        case 5:
            bar.className = ResultList.pct50;
            break;
        case 6:
            bar.className = ResultList.pct60;
            break;
        case 7:
            bar.className = ResultList.pct70;
            break;
        case 8:
            bar.className = ResultList.pct80;
            break;
        case 9:
            bar.className = ResultList.pct90;
            break;
        case 10:
            bar.className = ResultList.pct100;
            break;
    }

    document.getElementById("search-progress-feedback").style.visibility = "visible";
};

/**
 * Shows and hides the filter panes (top and bottom)
 * @param visibility "hidden" or "visible"
 */
ResultList.prototype.setFilterPaneVisibility = function(visibility)
{
    if (document.getElementById('filter_results_form') !== null)
    {
        document.getElementById('filter_results_form').style.visibility = visibility;
    }

    if (document.getElementById('filter_results_form_bottom') !== null)
    {
        document.getElementById('filter_results_form_bottom').style.visibility = visibility;
    }
};

/**
 * This method updates the Yahoo History manager state
 */
ResultList.prototype.updateHistory = function()
{
    if (top != window) { return; }

    var selCollection = (this.getActivePane().selectedCollection === "") ? "_" : this.getActivePane().selectedCollection;
    var selectedClusterLocation = this.clusterPanes[this.currentTabIndex].getSelectedClusterLocation();
    var clusterLocation = (selectedClusterLocation === "") ? "_" : selectedClusterLocation;
    var hist = this.currentTabIndex +
               "|" + this.getActivePane().startPosition +
               "|" + selCollection +
               "|" + this.getActivePane().resultSortField +
               "|" + clusterLocation;

    if (hist != YAHOO.util.History.getCurrentState(ResultList.moduleName))
    {
        YAHOO.util.History.navigate(ResultList.moduleName, hist);
    }
};

/**
 * Creates a new result state object give the state stored in the yahoo history mananger.
 * @param state the string representation of the current ui state
 */
function ResultListState(state)
{
    var stateArray = state.split("|");

    this.currentTabIndex = parseInt(stateArray[0], 10);
    this.startPosition = parseInt(stateArray[1], 10);
    this.selectedCollection = stateArray[2] == "_" ? "" : stateArray[2];
    this.resultSortField = stateArray[3];
    this.clusterLocation = stateArray[4] == "_" ? "" : stateArray[4];
}

/**
 * Handles the case where the history object has changed its state.
 * @param state the current state of the history object.
 */
ResultList.prototype.onHistoryChange = function(state)
{
    // Get the state object.
    var resultListState = new ResultListState(state);

    // Get the currently selectectd cluster id and the cluster id from the history
    var selectedClusterId = this.clusterPanes[this.currentTabIndex].getSelectedClusterId();
    var historyClusterId = this.clusterPanes[this.currentTabIndex].clusterIdFromClusterLocation(resultListState.clusterLocation);

    // Get the active pane
    var activePane = this.getActivePane();

    if (resultListState.currentTabIndex != this.currentTabIndex)
    {
        this.switchTabs(resultListState.currentTabIndex, resultListState);
    }
    else if (!historyClusterId && selectedClusterId)
    {
        this.clusterPanes[this.currentTabIndex].selectNodeByClusterId("");
        this.onAllResultsClicked(this.currentTabIndex + this.tabCount);
    }
    else if (historyClusterId && historyClusterId != selectedClusterId)
    {
        this.clusterInitialState = resultListState;
        this.selectCluster();
    }
    else
    {
        // if any of the other states have changed, then we will need to reload.
        if (resultListState.startPosition != activePane.startPosition ||
            resultListState.selectedCollection != activePane.selectedCollection ||
            resultListState.resultSortField != activePane.resultSortField)
        {
            activePane.startPosition = resultListState.startPosition;
            activePane.selectedCollection = resultListState.selectedCollection;
            activePane.resultSortField = resultListState.resultSortField;

            activePane.getPageOfResults(activePane.startPosition, false, this.ssid, this.filters);
        }
    }
};

/**
 * Gets the current state of the yahoo history
 */
ResultList.prototype.getCurrentState = function()
{
    var state;
    if (top != window)
    {
        state = this.getDefaultState();
    }
    else
    {
        state = YAHOO.util.History.getCurrentState(ResultList.moduleName);
    }

    return new ResultListState(state);
};

/**
 * Handler for the user action of clicking on one of the displayed tabs in the UI
 * @param which_tab the tab to switch to
 * @parma initialState the initial state of the result pane
 */
ResultList.prototype.switchTabs = function(which_tab, initialState)
{
    // Hide the correct cluster list for the current pane
    if (this.clusterPanes[this.currentTabIndex])
    {
        this.clusterPanes[this.currentTabIndex].hideClusters();
    }

    // Get and temporarily hide the filter panes while we calculate things.
    this.setFilterPaneVisibility("hidden");

    // Set the selected tab
    var selectedTab = document.getElementById("tab_" + which_tab);
    if (selectedTab !== null)
    {
        this.setSelectedTab(selectedTab);
    }

    // Set the selected container
    this.setSelectedContainer(document.getElementById("tab_content_" + this.activeTabPanes[which_tab].paneId));

    this.currentTabIndex = which_tab;

    // Update the description for the current tab
    if (document.getElementById("tab-description") !== null)
    {
        document.getElementById("tab-description").innerHTML = this.getActivePane().description;
    }

    // Get the active pane
    var activePane = this.getActivePane();

    // if the initialState is being set
    if (initialState)
    {
        activePane.startPosition = initialState.startPosition;
        activePane.selectedCollection = initialState.selectedCollection;
        activePane.resultSortField = initialState.resultSortField;
    }

    if (initialState && initialState.clusterLocation && initialState.clusterLocation != "0")
    {
        this.clusterInitialState = initialState;
    }
    else
    {
        // Update the two drop down boxes and pagination
        activePane.setSortByOptions();
        activePane.setLimitToOptions();
        activePane.paginate();

        // If there are no results to display
        if (activePane.resultCount === 0)
        {
            // if there are no results displayed in the pane and there are results to retrieve, get them
            if (activePane.displayCount === 0 && this.resultListStatus !== null && this.resultListStatus.paneCounts[this.currentTabIndex])
            {
                // Set the selected container to the retrieving results... container
                this.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));

                this.showPage(0, true);
            }
            //If there are no results to display in the active pane but there are results displayed in other panes.
            else if (activePane.displayCount === 0 && this.displayTotal !== 0)
            {
                this.setSelectedContainer(document.getElementById("empty-tab-message"));
            }
            //If the search is finished and there are no results displayed in any panes.
            else if (this.isSearchFinished() && this.displayTotal === 0)
            {
                this.setSelectedContainer(document.getElementById("no-results-message"));
            }
            else
            {
                this.setSelectedContainer(document.getElementById("searching-tab-message"));
            }
        }
        else
        {
            // Show the filter panes
            this.setFilterPaneVisibility("visible");
        }
    }

    // Display the cluster list for the selected tab
    if (this.clusterPanes[this.currentTabIndex])
    {
        this.clusterPanes[this.currentTabIndex].showClusters();
    }

    this.updateHistory();
};

/**
 * Sets the currently selected tab element and resets the previously selected
 * tab element
 * @param newSelectedTab the newly selected tab
 */
ResultList.prototype.setSelectedTab = function(newSelectedTab)
{
    if (this.selectedTab !== null)
    {
        this.selectedTab.className = "";
    }

    newSelectedTab.className = "Selected";
    if (newSelectedTab.blur)
    {
        newSelectedTab.blur();
    }

    this.selectedTab = newSelectedTab;
};

/**
 * Sets the currently selected container element and hides the previously selected
 * container element
 * @param newSelectedContainer the newly selected container
 */
ResultList.prototype.setSelectedContainer = function(newSelectedContainer)
{
    if (this.selectedContainer !== null)
    {
        this.selectedContainer.className = "Removed";
    }

    newSelectedContainer.className = "";

    this.selectedContainer = newSelectedContainer;
};

/**
 * Handles condidtions associated with a search status of "finished".
 */
ResultList.prototype.handleSearchFinished = function()
{
    var unseenResultCount = this.getTotalFilteredResults() - this.displayTotal;
    DWRUtil.setValue("unseenResultCount", unseenResultCount);

    // Hide all of the auxiliary panes by setting their class to "Removed"
    var elementIds = ["searching-tab-message", "no-results-message", "empty-tab-message", "retrieving-results-tab-message"];
    for (var i = 0; i < elementIds.length; ++i)
    {
        var elem = document.getElementById(elementIds[i]);
        elem.className = "Removed";
    }

    // Show the search completed pop-up.
    if (unseenResultCount > 0 && this.refreshMode == "PROMPT" && this.displayMoreResultsDialog)
    {
        this.showSearchPopup(unseenResultCount);
    }

    // Handle cases associated with no unseen results.
    if (unseenResultCount <= 0)
    {
        //If there are results for the active pane but they haven't been displayed yet.
        if (this.getActivePane().displayCount === 0 && this.resultListStatus !== null && this.resultListStatus.paneCounts[this.currentTabIndex])
        {
            this.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));
        }
        //If there are results for other panes but they haven't been displayed yet AND the active pane does not have results.
        else if (this.getActivePane().displayCount === 0 && this.displayTotal !== 0)
        {
            this.setSelectedContainer(document.getElementById("empty-tab-message"));
        }
        //If there are no results displayed in any panes.
        else if (this.displayTotal === 0)
        {
            this.setSelectedContainer(document.getElementById("no-results-message"));

            //Since there are no results, we need to hide the animation and show the no cluster message
            document.getElementById("clusterSearchDiv").style.display = "none";
            document.getElementById("noClustersDiv").style.display = "";
        }
    }
    // Handle cases where at least one pane has some results ALREADY displayed.
    else if (this.getActivePane().displayCount === 0)
    {
        this.setSelectedContainer(document.getElementById("empty-tab-message"));
    }
};

/**
 * Creates the search complete popup
 */
ResultList.prototype.createSearchPopup = function()
{
    document.getElementById("search-popup-window").style.display = "";

    this.searchPopup = new YAHOO.widget.Dialog(
            "search-popup-window",
    {
        //width: "450px",
        fixedcenter: true,
        visible: false,
        modal: true,
        underlay: "shadow",
        draggable: true,
        constraintoviewport: true,
        buttons: [
            { text:ResultList.searchPopupButton2Text,
                handler:function()
                {
                    ResultList.instance.handleSearchPopup(false);
                }
            },
            { text:ResultList.searchPopupButton1Text,
                handler:function()
                {
                    ResultList.instance.handleSearchPopup(true);
                },
                isDefault:true }
        ]
    }
            );

    YahooDlgHelper.attachKeyEventHandlers(this.searchPopup, this.searchPopup.cancel);
    YahooDlgHelper.enableFocusOnShow(this.searchPopup);

    this.searchPopup.render();
};


/**
 * Update and display the search popup dialog.
 * We create a "canvas" iframe just beneath so that IE 5.5 and 6 will display
 * the div correctly over select elements.
 */
ResultList.prototype.showSearchPopup = function(resultCount)
{
    var count = document.getElementById("search_popup_resultCount");
    count.innerHTML = CoralUtils.formatInteger(resultCount);

    this.searchPopup.show();
    this.searchPopup.center();
};

/**
 * Method called by the results pop-up to finish the search with a final refresh
 * or not, and to set the refresh mode if needed.
 * @param refresh true if the page should be refreshed
 */
ResultList.prototype.handleSearchPopup = function(refresh)
{
    this.searchPopup.hide();
    var disablePopup = document.getElementById("show_popup").checked;
    PreferencesConnector.setRefreshMode(disablePopup ? "MANUAL" : "PROMPT");
    if (refresh)
    {
        this.addResults();
    }
};

/**
 * Callback method for ResultListConnector.getResults* methods.  The return value contains
 * a resultContainer, which contains the results requested.  It then calls into the
 * appropriate resultPane's update method to display the data.
 * @param resultContainer the object that contains the new values to dsiplay in the
 * resultPane.
 */
ResultList.getResults_callback = function(resultContainer)
{
    ResultList.instance.setResults(resultContainer);
};

/**
 * The error handler method for calls to ResultListConnector.getResults
 * and ResultListConnector.getResultsForIds
 * @param message the error message to be displayed
 */
ResultList.getResults_errorHandler = function(message, info)
{
    ClusterPane.blockCalls = false;
    ResultList.pageErrorHandler("ResultList.getResults", message, info);
};

ResultList.setClippingsCount_errorHandler = function(message, info)
{
    ResultList.pageErrorHandler("ResultList.setClippingsCount", message, info);
};

/**
 * Processes the result container that is received from calls to getResults...
 * @param resultContainer the object that contains the new values to dsiplay in the
 * resultPane.
 */
ResultList.prototype.setResults = function(resultContainer)
{
    ClusterPane.blockCalls = false;

    var paneIndex = resultContainer.paneIndex;

    // Get the result panel where the results will be stored
    var tabContentElement = document.getElementById("tab_content_" + paneIndex);

    // Get the result pane object for this index
    var resultPane = this.resultPanes[paneIndex];

    // Calculate the maximum total counts.  This is the maximum of resultContainer.totalResults and the
    // paneCounts from the resultList status
    var statusIndex = (paneIndex < this.tabCount) ? paneIndex : paneIndex - this.tabCount;
    var maxTotalCounts = Math.max(resultContainer.totalResults, this.resultListStatus.paneCounts[statusIndex]);

    // if there are currently no results displayed for this pane, generate the clusters
    // for the pane.
    if (resultPane.displayCount === 0 && maxTotalCounts > 0 && paneIndex < this.tabCount)
    {
        if (this.clusterPanes[paneIndex])
        {
            this.clusterPanes[paneIndex].getClusters(this.ssid, this.isInActiveTab(paneIndex),
                    maxTotalCounts, this.isSearchFinished(),
                    this.filters);
        }

        this.updateResultCounter(paneIndex, maxTotalCounts);

        // Show the filter panes
        this.setFilterPaneVisibility("visible");
    }

    resultPane.setCollectionCounts(resultContainer.collectionCounts);

    resultPane.resultCount = resultContainer.resultCount;
    resultPane.startPosition = resultContainer.startPosition;
    resultPane.totalResultCount = resultContainer.totalResults;
    resultPane.displayCount = resultPane.displayCount === 0 ? maxTotalCounts : resultPane.displayCount;

    resultPane.update(resultContainer, tabContentElement);

    // if this pane is in the currently selected tab and it is not displayed, then show it
    if (this.isInActiveTab(paneIndex))
    {
        this.setSelectedContainer(tabContentElement);
        this.activeTabPanes[this.currentTabIndex] = resultPane;

        if (resultContainer.totalResults === 0)
        {
            this.setSelectedContainer(document.getElementById("empty-tab-message"));
        }
    }

    this.calculateDisplayTotal();

    // if this is a return from a cluster, select the node in the cluster, and scroll
    // to the top of the result list
    if (resultContainer.clusterId !== null)
    {
        var clusterIndex = paneIndex - this.tabCount;
        this.clusterPanes[clusterIndex].selectNodeByClusterId(resultContainer.clusterId);
        Paginator.scrollIntoView();
    }

    this.updateHistory();

    if (this.clusterInitialState)
    {
        // if any of the other states have changed, then we will need to reload.
        if (this.clusterInitialState.startPosition != this.getActivePane().startPosition ||
            this.clusterInitialState.selectedCollection != this.getActivePane().selectedCollection ||
            this.clusterInitialState.resultSortField != this.getActivePane().resultSortField)
        {
            this.getActivePane().startPosition = this.clusterInitialState.startPosition;
            this.getActivePane().resultSortField = this.clusterInitialState.resultSortField;
            this.getActivePane().setSelectedCollection(this.clusterInitialState.selectedCollection);
            this.getActivePane().getPageOfResults(this.getActivePane().startPosition, false, this.ssid, this.filters);
        }

        this.clusterInitialState = null;
    }
};

/**
 * Updates the result count element for the given tab with the value from the
 * paneCounts array in the resultListStatus
 * @param tabIndex the index of the tab
 */
ResultList.prototype.updateResultCounter = function(index, count)
{
    var resultCountElement = document.getElementById("results_count_" + index);
    if (resultCountElement !== null)
    {
        if (typeof count == "undefined")
        {
            count = this.resultListStatus.paneCounts[index];
        }
        resultCountElement.innerHTML = count;
    }
};

/**
 * This method updates the top results and possible results elements with the apporpriate values.
 */
ResultList.prototype.updateTopResults = function()
{
    if (document.getElementById("topResultCount") !== null)
    {
        document.getElementById("topResultCount").innerHTML = CoralUtils.formatInteger(this.resultListStatus.totalNumberResults);
    }

    if (document.getElementById("possibleResultCount") !== null)
    {
        document.getElementById("possibleResultCount").innerHTML = CoralUtils.formatInteger(this.resultListStatus.totalPossibleResults);
    }
};

/**
 * This method calculates the total number of results that can be displayed by summing
 * the results from each of the resultPanes.  If a resultPane does not have any results
 * displayed, it adds the totalResultCount.  If it does, it means that ResultList is no
 * longer updating its count, so use the displayCount for that pane.
 */
ResultList.prototype.calculateDisplayTotal = function()
{
    this.displayTotal = 0;
    for (var i = 0; i < this.tabCount; ++i)
    {
        if (this.resultPanes[i].displayCount === 0)
        {
            this.displayTotal += this.resultListStatus.paneCounts[i];
        }
        else
        {
            this.displayTotal += this.resultPanes[i].displayCount;
        }
    }
};

/**
 * Detects if the current pane is in the currently selected tab.
 * @param paneIndex the index of the pane to be checked
 * @return true if the pane is in the currently dispalyed tab
 */
ResultList.prototype.isInActiveTab = function(paneIndex)
{
    return (paneIndex == this.currentTabIndex ||
            paneIndex - this.tabCount == this.currentTabIndex);
};

/**
 * This method is called in response to the onlableclick event in the cluster tree.
 * @param cluster the cluster object associated with the clicked cluster label.
 * @param displayPaneIndex the index to the result pane in which the clusters results
 * are to be displayed.
 * @param clusterGroup the name of the group that this cluster is associated with
 */
ResultList.prototype.onClusterClicked = function(cluster, displayPaneIndex, clusterGroup)
{
    this.retrieveClusterResults(cluster.clusterId, displayPaneIndex, clusterGroup, 0);
};

/**
 * Selects a cluster based on the clusterInitialState
 */
ResultList.prototype.selectCluster = function()
{
    var selectedClusterId = this.clusterPanes[this.clusterInitialState.currentTabIndex].clusterIdFromClusterLocation(this.clusterInitialState.clusterLocation);

    // Get the results for the selected cluster.
    this.retrieveClusterResults(selectedClusterId, this.clusterInitialState.currentTabIndex + this.tabCount,
            this.clusterInitialState.currentTabIndex, this.clusterInitialState.startPosition);
};

/**
 * Event fired from the cluster pane following the creation of the cluster tree object.
 * If an initial cluster state is being restored, then the results for the clusters are
 * retrieved here.
 */
ResultList.prototype.onCreateClusterTree = function()
{
    if (this.clusterInitialState)
    {
        this.selectCluster();
    }
};

/**
 * This method is called to retrieve results for a given cluster
 * @param clusterId the id of the cluster to get results for
 * @param displayPaneIndex the index to the result pane in which the clusters results
 * are to be displayed.
 * @param clusterGroup the name of the group that this cluster is associated with
 * @param startPosition the position within the results to retrieve from
 */
ResultList.prototype.retrieveClusterResults = function(clusterId, displayPaneIndex, clusterGroup, startPosition)
{
    ResultList.instance.setSelectedContainer(document.getElementById("retrieving-results-tab-message"));

    // Reset the collectionCounts
    this.resultPanes[displayPaneIndex].setCollectionCounts(null);

    // Reset the selected collection
    this.resultPanes[displayPaneIndex].resetSelectedCollection();

    ResultListConnector.getResultsForCluster(this.ssid, displayPaneIndex, clusterGroup, clusterId,
            startPosition, Paginator.resultsPerPage, this.filters, this.resultPanes[displayPaneIndex].resultSortField,
            ResultList.getResultsDWRObject);
};

/**
 * This method is called in response to the onlableclick event in the cluster tree
 * when the user clicks on the "All Results" node in the tree.
 * @param displayPaneIndex the index to the result pane in which the clusters results
 * are normally displayed.
 */
ResultList.prototype.onAllResultsClicked = function(displayPaneIndex)
{
    var paneIndex = displayPaneIndex - this.tabCount;

    var newResultPane = this.resultPanes[paneIndex];
    this.activeTabPanes[this.currentTabIndex] = newResultPane;

    if (newResultPane.displayCount === 0)
    {
        this.showPage(-1, true);
    }
    else
    {
        this.setSelectedContainer(document.getElementById("tab_content_" + paneIndex));

        // Update the two drop down boxes
        newResultPane.setSortByOptions();
        newResultPane.setLimitToOptions();

        newResultPane.paginate();

        this.updateHistory();

        // If the results per page have changed, refresh the results
        if (Paginator.resultsPerPage != newResultPane.resultCount)
        {
            this.refreshResults();
        }
    }
};

/**
 * The object used when making calls to getResults
 */
ResultList.getResultsDWRObject = {
    callback:     ResultList.getResults_callback,
    timeout:      DWRHelper.ajaxTimeout,
    errorHandler: ResultList.getResults_errorHandler
};

