import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.CometEvent;

public class ChatServlet extends javax.servlet.http.HttpServlet implements org.apache.catalina.CometProcessor  {

    protected ConcurrentHashMap 	connections =  new ConcurrentHashMap();
    protected ConcurrentHashMap	pendingClose = new ConcurrentHashMap();

    protected final MessageSender messageSender = new MessageSender();

    public void init() throws ServletException {
        Thread messageSenderThread = new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]");
        messageSenderThread.setDaemon(true);
        messageSenderThread.start();
    }

    public void destroy() {
        connections.clear();
        messageSender.stop();
    }

    private String getSessionCookie(HttpServletRequest request, HttpServletResponse response) {
        String sessionID = null;
        if ( request.getCookies() != null) {
	        int numCookies = request.getCookies().length;
	        Cookie cookies[] = request.getCookies();
	        for (int i = 0; i < numCookies; i++) {
	        	Cookie cookie = cookies[i];
	        	if (cookie.getName().equals("JSESSIONID")) {
	        		sessionID = cookie.getValue();
	        		break;
	        	}
	        }
        }
        if (sessionID == null ) {
        	Cookie sessionCookie = new Cookie("JSESSIONID", request.getSession(true).getId());
        	sessionID = sessionCookie.getValue();
        	sessionCookie.setMaxAge(1800);
        	sessionCookie.setPath("/chat");
        	response.addCookie(sessionCookie);
        }
        return sessionID;
    }

    public void event(CometEvent event)
        throws IOException, ServletException {
        HttpServletRequest request = event.getHttpServletRequest();
        HttpServletResponse response = event.getHttpServletResponse();

        String sessionID = getSessionCookie(request, response);

        if (event.getEventType() == CometEvent.EventType.BEGIN) {

            log("Begin for session " + sessionID);

            String action = request.getParameter("action");
            if (action.equals("chat")) {
                   String message = request.getParameter("msg");
                   messageSender.send(message);

                PrintWriter writer = response.getWriter();
                writer.println("");
                writer.println("JSP Chat");
                writer.println("Message Sent");
                writer.flush();
                response.flushBuffer();
                event.close();
            }   else {
               	  CometEvent e = connections.get(sessionID);
		    	  if (e != null) {
			    	  synchronized(e) {
			    		  log ("Booting old connection");
			    		  pendingClose.put(e, e);
			    		  e.close();
			    	  }
		    	  }
	    		  event.setTimeout(10*1000);
	    		  connections.put(sessionID, event);
            }
        } else if (event.getEventType() == CometEvent.EventType.ERROR) {
            log("ERROR for session " + sessionID);
            connections.remove(sessionID);
            event.close();

        } else if (event.getEventType() == CometEvent.EventType.END) {
        	log("End for session " + sessionID);
        	if ( pendingClose.containsKey(event) ) {
        		CometEvent pender = pendingClose.get(event);
        		synchronized(pender){
        			pendingClose.remove(event);
        		}
        	} else {
                connections.remove(sessionID);
        	}
            event.close();
        } else if (event.getEventType() == CometEvent.EventType.READ) {
            log("Read for session ");
        }
    }

    private class MessageSender implements Runnable {

        private boolean running = true;
        private final ArrayList messages = new ArrayList();

        public MessageSender() {
        }

        public void stop() {
            running = false;
        }

        public void send(String message) {
            synchronized (messages) {
                messages.add(message);
                messages.notifyAll();
            }
        }
        public void run() {
            while (running) {
                synchronized (messages) {
                    if (messages.size() == 0) {
                        try {
                          messages.wait();
                        } catch (InterruptedException e) {/* Ignore */ }
                    }
                }
                String[] pendingMessages;
                synchronized (messages) {
                    pendingMessages = new String[0];
                    messages.clear();
                    for (CometEvent event : connections.values()){
                        try {
                    		if (event != null ) {
	                    		synchronized(event) {
		                        	HttpServletResponse response = event.getHttpServletResponse();
		                            PrintWriter writer = response.getWriter();
                                    for (int j = 0; j < pendingMessages.length; j++) {
		                                writer.println(pendingMessages[j]);
		                            }
		                            writer.flush();
		                            response.flushBuffer();
		                            event.close();
	                    		}
                    		}
                        } catch (IOException e) {
                            log("IOExeption sending message", e);
                        }
                    }
                }
            }
        }
    }
}