/*******************************************************************************
 * Copyright © 2004-2006 VHA. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.ui.admin.action;

// Java Classes
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

// Library Classes
import org.acegisecurity.Authentication;
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
import org.acegisecurity.ui.WebAuthenticationDetails;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.commons.lang.Validate;

// Framework Classes
import gov.va.med.fw.security.UserCredentials;
import gov.va.med.fw.security.UserPrincipal;
import gov.va.med.fw.ui.UIConstants;
import gov.va.med.fw.util.StringUtils;

// ESR Classes
import gov.va.med.esr.ui.admin.beans.UserSessionBean;

/**
 * Struts action class that will display and work with HTTP user session information.
 *
 * @author Andrew Pach
 * @version: 3.0
 */
public class UserSessionsAction extends UserSessionsAbstractAction
{
    // Message Keys
    public static final String MSG_NO_PERMISSION_TO_VIEW_ACTIVE_SESSIONS = "error.noPermissionViewActiveSessions";
    public static final String MSG_NO_SESSION_FOUND = "error.noSessionFound";
    public static final String MSG_NO_INVALIDATE_CURRENT_SESSION = "error.noInvalidateCurrentSession";
    public static final String MSG_SESSION_INVALIDATED = "message.sessionInvalidated";
    public static final String MSG_ALL_SESSIONS_INVALIDATED = "message.allSessionsInvalidated";

    // Request and Session Keys
    public static final String SESSION_ID_REQUEST_KEY = "id";
    public static final String USER_SESSIONS_REQUEST_KEY = "userSessionsRequestKey";
    public static final String CURRENT_USER_REQUEST_KEY = "currentUserRequestKey";

    // Other constants
    public static final String ANONYMOUS_USER = "anonymousUser";
    public static final String UNKNOWN_USER = "[UNKNOWN]";

    /**
     * A default constructor
     */
    public UserSessionsAction()
    {
        super();
    }

    /**
     * Retrieves and displays the session information.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward display(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        // Initialize the session bean list
        ArrayList sessionBeanList = new ArrayList();

        // Default the current user
        String currentUser = UNKNOWN_USER;

        // Ensure only administrators can view the data
        if (isAnyPermissionGranted(administratorPermissions))
        {
            // Get the open list of sessions
            Hashtable sessionTable = getSessions(request);

            // Iterate through the sessions
            for (Iterator iterator = sessionTable.values().iterator(); iterator.hasNext();)
            {
                // Get a session
                HttpSession session = (HttpSession)iterator.next();

                // Create a user session bean
                UserSessionBean sessionBean = new UserSessionBean();

                sessionBean.setCreationTime(new Date(session.getCreationTime()));
                sessionBean.setLastAccessedTime(new Date(session.getLastAccessedTime()));
                sessionBean.setMaxInactiveInterval(session.getMaxInactiveInterval());
                sessionBean.setId(session.getId());
                sessionBean.setCurrentSession(isCurrentSession(session.getId(), request));

                // Get the authentication information
                Object authObject = session.getAttribute(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY);
                org.acegisecurity.context.SecurityContext secureContext =
                	(org.acegisecurity.context.SecurityContext)authObject;
                if (secureContext != null)
                {
                    Authentication auth = secureContext.getAuthentication();

                    // Get and store the logged in username
                    Object principalObject = auth.getPrincipal();
                    String username = principalObject == null ? "" : principalObject.toString();
                    if (UserPrincipal.class.isAssignableFrom(principalObject.getClass()))
                    {
                        UserPrincipal userPrincipal = (UserPrincipal)principalObject;
                        UserCredentials userCredentials = userPrincipal.getUserCredentials();
                        username = userCredentials.getUserID();
                    }
                    sessionBean.setUsername(username);

                    WebAuthenticationDetails authDetails = (WebAuthenticationDetails)auth.getDetails();
                    sessionBean.setRemoteIpAddress(authDetails == null ? "" : authDetails.getRemoteAddress());
                }
                else
                {
                    sessionBean.setUsername("");
                    sessionBean.setRemoteIpAddress("");
                }

                if (sessionBean.isCurrentSession())
                {
                    currentUser = sessionBean.getUsername();
                }

                // Filter out sessions where the user is not logged in
                if ((StringUtils.isNotBlank(sessionBean.getUsername())) &&
                    (!sessionBean.getUsername().equalsIgnoreCase(ANONYMOUS_USER)))
                {
                    // Add the bean to the list
                    sessionBeanList.add(sessionBean);
                }
            }

            // Sort the list
            Collections.sort(sessionBeanList);
        }
        else
        {
            // Inform the user that they can't view active session information
            addActionMessage(request, MSG_NO_PERMISSION_TO_VIEW_ACTIVE_SESSIONS);
        }

        // Store the user sessions on the request
        request.setAttribute(USER_SESSIONS_REQUEST_KEY, sessionBeanList); 
        // Store the current user on the request
        request.setAttribute(CURRENT_USER_REQUEST_KEY, currentUser);

        // Forward to the display
        return mapping.findForward(UIConstants.SUCCESS);
    }

    /**
     * Refreshes the screen.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward refresh(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        return display(mapping, form, request, response);
    }

    /**
     * Invalidates a session.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward invalidate(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        // Ensure only administrators can perform this operation
        if (isAnyPermissionGranted(administratorPermissions))
        {
            // Get the session Id to invalidate
            String id = (String)request.getParameter(SESSION_ID_REQUEST_KEY);

            // Ensure a session Id was found
            Validate.notEmpty(id, "A session Id wasn't specified.");

            // Get the currently logged in user's session Id
            String currentSessionId = getCurrentSessionId(request);

            // Ensure the user doesn't invalidate their own session
            if (currentSessionId.equals(id))
            {
                // Inform the user that they can't invalidate their own session
                addActionMessage(request, MSG_NO_INVALIDATE_CURRENT_SESSION);
            }
            else
            {
                // Get the session for the passed in Id
                HttpSession session = getSessionById(request, id);

                // Check if the session was found
                if (session == null)
                {
                    // Inform the user that the session wasn't found
                    addActionMessage(request, MSG_NO_SESSION_FOUND);
                }
                else
                {
                    // Invalidate the session
                    session.invalidate();

                    // Add a success message
                    addInformationMessage(request, MSG_SESSION_INVALIDATED);
                }
            }
        }

        // Forward to the display
        return display(mapping, form, request, response);
    }

    /**
     * Invalidates all sessions except the currently logged in user.
     *
     * @param mapping An action mapping
     * @param form A form bean
     * @param request A http request
     * @param response A http response to stream data to a next page
     *
     * @return An action forward class encapsulating information about a next page
     * @throws Exception if any errors occurred while trying to select the person.
     */
    public ActionForward invalidateAll(ActionMapping mapping, ActionForm form,
        HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        // Ensure only administrators can perform this operation
        if (isAnyPermissionGranted(administratorPermissions))
        {
            // Get the open list of sessions
            Hashtable sessionTable = getSessions(request);

            // Create a list of session to invalidate
            List sessionsToInvalidate = new ArrayList();

            // Iterate through the sessions
            for (Iterator iterator = sessionTable.values().iterator(); iterator.hasNext();)
            {
                // Get a session
                HttpSession session = (HttpSession)iterator.next();

                // Get this session Id
                String sessionId = session.getId();

                // Invalidate the session if the session Id isn't the same as the currently logged in user
                if (!isCurrentSession(sessionId, request))
                {
                    // Add this session to the list of sessions to invalidate
                    sessionsToInvalidate.add(session);
                }
            }

            // Invalidate all the sessions
            for (Iterator iterator = sessionsToInvalidate.iterator(); iterator.hasNext();)
            {
                HttpSession session = (HttpSession)iterator.next();
                session.invalidate();
            }

            // Add a success message
            addInformationMessage(request, MSG_ALL_SESSIONS_INVALIDATED);
        }

        // Forward to the display
        return display(mapping, form, request, response);
    }

    // Lookup dispatch action methods
    protected Map getKeyMethodMap()
    {
        Map map = new HashMap();
        map.put("button.display", "display");
        map.put("button.refresh", "refresh");
        map.put("button.invalidate", "invalidate");
        map.put("button.invalidateAll", "invalidateAll");
        return map;
    }
}