/*********************************************************************
 * Copyright © 2005-2006 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.ui.valuelist;

// Java Classes
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

// Library Classes
import net.mlw.vlh.ValueList;
import net.mlw.vlh.ValueListInfo;

// Framework Classes
import gov.va.med.fw.service.pagination.SearchQueryInfo;
import gov.va.med.fw.ui.struts.ValueListActionUtils;
import gov.va.med.fw.util.StringUtils;

/**
 * This class extends the search value adapter and adds functionality to maintain whether an entry
 * in the result set is selected or unselected.  Note that this functionality can only work if all
 * the results are cached during the search since we have no place to persist the selected items
 * otherwise if we get one page at a time.
 *
 * @author Andrew Pach
 * @version 3.0
 */
public class SelectableSearchValueListAdapter extends SearchValueListAdapter
{
    private static final long serialVersionUID = -8607848154762326203L;
    
    /**
     * The request attribute key for the "selected" checkbox array
     */
    public static final String SELECTED_REQUEST_ATTRIBUTE_KEY = "selected";

    /**
     * Extends the standard getValueList functionality by updating the selected attribute of the
     * SelectableResult objects with the value that is submitted in the "selected" request attribute
     * for each row on the previous page.
     * <p/>
     * It also updates the "selected" request attribute with the values of the selected flag for the
     * next page.
     *
     * @see net.mlw.vlh.ValueListAdapter#getValueList(String, net.mlw.vlh.ValueListInfo)
     */
    public ValueList getValueList(String name, ValueListInfo info) throws ValueListException,
        MaxRecordsExceededValueListException
    {
        // Get the filter map
        Map filterMap = info.getFilters();

        // Get a handle to the request & session
        HttpServletRequest request = (HttpServletRequest)filterMap.get(HTTP_REQUEST_KEY);
        HttpSession session = request.getSession();

        // Get the user action
        String action = getAction(request);

        // Get the previously cached results.  SelectableResults will only work with
        // cached entries.
        final String RESULTS_SESSION_KEY = getResultsSessionKey(filterMap);
        List searchResults = getResultsFromSession(session, RESULTS_SESSION_KEY);

        if ( SearchQueryInfo.SEARCH_SINGLE_PAGE.equals(getSearchOptimizationType())
             && SearchQueryInfo.ACTION_SELECT.equals(action))
        {
            searchResults = getResultsFromSession(session, getPrevPageDataSessionKey(filterMap));
        }
        // Updated whether each item has been selected for the previous page
        updatePreviousPageSelectedItems(searchResults, action, request,
            getPrevPageSessionKey(filterMap));

        if ( ! (SearchQueryInfo.SEARCH_SINGLE_PAGE.equals(getSearchOptimizationType())
                && SearchQueryInfo.ACTION_SELECT.equals(action)) )
        {
        
            // Perform the standard processing to get the new ValueList
            ValueList valueList = super.getValueList(name, info);
    
            // Get the updated cached search results that would get set in the getValueList method.
            searchResults = getResultsFromSession(session, RESULTS_SESSION_KEY);
    
            // Updated whether each item has been selected for the new destination page
            updatedCurrentPageSelectedItems(info.getPagingPage(), searchResults, request);
    
            // Return the new valueList
            return valueList;
        }
        else
        {
            return null;
            //return (ValueList)searchResults;
        }
    }

    /**
     * Updates whether each item has been selected for the previous page.
     *
     * @param results The list of results
     * @param action The action being performed
     * @param request The HttpServletRequest
     * @param prevPageSessionKey The previous page session key
     */
    protected void updatePreviousPageSelectedItems(List results, String action,
        HttpServletRequest request, String prevPageSessionKey)
    {
        // Get the session
        HttpSession session = request.getSession();

        // Only process the selected entries if previous results were cached and
        // the user is sorting, paging, or selecting items.
        
		String[] selectedItems =
			request.getParameterValues(SELECTED_REQUEST_ATTRIBUTE_KEY);
		
		if (selectedItems != null && results != null && !StringUtils.isEmpty(action))
		{
			if ((action.equals(SearchQueryInfo.ACTION_SORT)) ||
					(action.equals(SearchQueryInfo.ACTION_PAGE)))
			{
				// Get GUI checkbox values from the parameter
				String checkedList = (String)selectedItems[0]; 
				//Always one, Like: "false,true,false", see javascript SelectedPaging
				StringTokenizer strtok = new StringTokenizer(checkedList, ","); 
				
				// Get the previous (updated) page
				int prevPage = getPrevPageFromSession(session, prevPageSessionKey).intValue();
				int maxLoop = (prevPage * getNumberPerPage()) > results.size() ?
						(results.size() % getNumberPerPage()) : getNumberPerPage();
						
						for (int i = 0; i < maxLoop && strtok.hasMoreTokens(); i++)
						{
							SelectableResult selectableResult =
								(SelectableResult)results.get(((prevPage - 1) * getNumberPerPage()) + i);
							
							String isSelected = strtok.nextToken();
							
							selectableResult.setSelected(isSelected != null && isSelected.equals("true"));
						}
						
			}
			else if  (action.equals(SearchQueryInfo.ACTION_SELECT))
			{        		
				// Create a map of the selected items
				Map selectedItemMap = new HashMap();
				for (int i = 0; i < selectedItems.length; i++) //selectedItems as: "0, 1, ..."
				{
					String selectedItemIndex = (String)selectedItems[i];
					selectedItemMap.put(new Integer(selectedItemIndex), "true");
				}
				
				
                // Get the previous page
				int prevPage = getPrevPageFromSession(session, prevPageSessionKey).intValue();
				
                // if this is signle page search and on action select we only have the prev page in 
                // session . so no need to loop through the complete results
                if ( SearchQueryInfo.SEARCH_SINGLE_PAGE.equals(getSearchOptimizationType())
                        && SearchQueryInfo.ACTION_SELECT.equals(action))
                {
                    prevPage = 1 ;
                }
                
				// Loop through the number of records on the page and mark the appropriate
				// ones as selected
				int maxLoop = (prevPage * getNumberPerPage()) > results.size() ?
						(results.size() % getNumberPerPage()) : getNumberPerPage();
						for (int i = 0; i < maxLoop; i++)
						{
							SelectableResult selectableResult =
								(SelectableResult)results.get(((prevPage - 1) * getNumberPerPage()) + i);
							selectableResult.setSelected(selectedItemMap.get(new Integer(i)) != null);
						}
			}
		}
        
    }

    /**
     * Updates whether each item has been selected for the new destination page.
     *
     * @param currentPage The new page
     * @param results The list of results
     * @param request The HttpServletRequest
     */
    protected void updatedCurrentPageSelectedItems(int currentPage, List results,
        HttpServletRequest request)
    {
        // Only st the selected items if the results are cached
        if (results != null)
        {
            // Create a new array of selected items for the new page
            List newlySelectedItems = new ArrayList();

            // Itereate through one page's worth of results
            int maxLoop = (currentPage * getNumberPerPage()) > results.size() ?
                (results.size() % getNumberPerPage()) : getNumberPerPage();
            for (int i = 0; i < maxLoop; i++)
            {
                // Get the selectable result for the current page at the specified (ith) offset
                SelectableResult selectableResult =
                    (SelectableResult)results.get(((currentPage - 1) * getNumberPerPage()) + i);

                // Set whether this selectable result is selected
                newlySelectedItems.add(selectableResult.isSelected() ? "checked" : "");
            }

            // Set the newly selected items in the request
            request.setAttribute(SELECTED_REQUEST_ATTRIBUTE_KEY, newlySelectedItems);
        }
    }

    /**
     * Returns a list of SelectableResult objects for each result passed in.  The selected attribute
     * of each SelectableResult will initially be false.
     * <p/>
     * NOTE: All child classes that overwrite this method should perform their post query processing
     * on the results before this method is called.  Otherwise they will need to process the
     * modified result list of SelectableResult objects.
     *
     * @param results The initial results
     * @param searchQueryInfo The search query information
     *
     * @return The list of SearchableResults
     * @throws ValueListException if any errors were encountered.
     */
    protected List performPostQueryProcessing(List results, SearchQueryInfo searchQueryInfo, ValueListInfo info)
        throws ValueListException
    {
        ArrayList selectableResults = new ArrayList();
        for (Iterator iterator = results.iterator(); iterator.hasNext();)
        {
            Object result = iterator.next();
            SelectableResult selectableResult = new DefaultSelectableResult();
            selectableResult.setResult(result);
            selectableResult.setSelected(false);
            selectableResults.add(selectableResult);
        }
        return selectableResults;
    }
}