package org.jboss.resteasy.skeleton.key.as7;

import org.apache.catalina.Session;
import org.apache.catalina.SessionEvent;
import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal;
import org.jboss.resteasy.skeleton.key.as7.i18n.LogMessages;
import org.jboss.resteasy.skeleton.key.as7.i18n.Messages;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Manages relationship to users and sessions so that forced admin logout can be implemented
 *
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class UserSessionManagement implements SessionListener
{
   protected ConcurrentHashMap<String, Map<String, Session>> userSessionMap = new ConcurrentHashMap<String, Map<String, Session>>();

   protected void login(Session session, String username)
   {
      Map<String, Session> map = userSessionMap.get(username);
      if (map == null)
      {
         final Map<String, Session> value = new HashMap<String, Session>();
         map = userSessionMap.putIfAbsent(username, value);
         if (map == null)
         {
            map = value;
         }
      }
      synchronized (map)
      {
         map.put(session.getId(), session);
      }
      session.addSessionListener(this);
   }

   public void logoutAll()
   {
      List<String> users = new ArrayList<String>();
      users.addAll(userSessionMap.keySet());
      for (String user : users) logout(user);
   }

   public void logoutAllBut(String but)
   {
      List<String> users = new ArrayList<String>();
      users.addAll(userSessionMap.keySet());
      for (String user : users)
      {
         if (!but.equals(user)) logout(user);
      }
   }


   public void logout(String user)
   {
      LogMessages.LOGGER.debug(Messages.MESSAGES.logoutUser(user));
      Map<String, Session> map = userSessionMap.remove(user);
      if (map == null)
      {
         LogMessages.LOGGER.debug(Messages.MESSAGES.noSessionForUser(user));
         return;
      }
      LogMessages.LOGGER.debug(Messages.MESSAGES.foundSessionForUser());
      synchronized (map)
      {
         for (Session session : map.values())
         {
            LogMessages.LOGGER.debug(Messages.MESSAGES.invalidatingSessionForUser(user));
            session.setPrincipal(null);
            session.setAuthType(null);
            session.getSession().invalidate();
         }
      }

   }

   public void sessionEvent(SessionEvent event)
   {
      // We only care about session destroyed events
      if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())
            && (!Session.SESSION_PASSIVATED_EVENT.equals(event.getType())))
         return;

      // Look up the single session id associated with this session (if any)
      Session session = event.getSession();
      GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
      if (principal == null) return;
      session.setPrincipal(null);
      session.setAuthType(null);

      String username = principal.getUserPrincipal().getName();
      Map<String, Session> map = userSessionMap.get(username);
      if (map == null) return;
      synchronized (map)
      {
         map.remove(session.getId());
         if (map.isEmpty()) userSessionMap.remove(username);
      }


   }
}
