/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.servlet;

import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.log4j.Logger;
import com.silverwrist.dynamo.WebConstants;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;

class ServletCookieControl implements CookieControl, WebConstants
{
  /*--------------------------------------------------------------------------------
   * Internal class which holds cookies until the response is prepared
   *--------------------------------------------------------------------------------
   */

  private static class NewCookieHolder
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private HashMap m_cookies = new HashMap();

    /*====================================================================
     * Constructor
     *====================================================================
     */

    NewCookieHolder()
    { // do nothing
    } // end constructor

    /*====================================================================
     * External operations
     *====================================================================
     */

    synchronized void putCookie(Cookie c)
    {
      m_cookies.put(c.getName(),c);

    } // end putCookie

    synchronized void flushCookies(HttpServletResponse resp)
    {
      if (logger.isDebugEnabled())
	logger.debug("flushCookies(): " + m_cookies.size() + " cookie(s) to flush to response");
      if (resp.isCommitted())
	logger.warn("flushCookies() being called on committed response, this won't work!!!");
      Iterator it = m_cookies.values().iterator();
      while (it.hasNext())
	resp.addCookie((Cookie)(it.next()));
      m_cookies.clear();

    } // end flushCookies

  } // end NewCookieHolder

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static Logger logger = Logger.getLogger(ServletCookieControl.class);

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private HttpServletRequest m_req;
  private HttpServletResponse m_resp;
  private HashMap m_cookie_set;
  private NewCookieHolder m_holder = null;

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  ServletCookieControl(HttpServletRequest req)
  {
    m_req = req;
    m_resp = null;
    m_cookie_set = (HashMap)(m_req.getAttribute(REQUEST_COOKIESET_ATTR));

  } // end constructor

  ServletCookieControl(HttpServletRequest req, HttpServletResponse resp)
  {
    m_req = req;
    m_resp = resp;
    m_cookie_set = (HashMap)(m_req.getAttribute(REQUEST_COOKIESET_ATTR));

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final synchronized void initCookieSet()
  {
    if (m_cookie_set==null)
    { // initialize the cookie set with the request cookies
      Cookie[] cookies = m_req.getCookies();
      m_cookie_set = new HashMap();
      if (cookies!=null)
      { // if there are cookies, save their values
	for (int i=0; i<cookies.length; i++)
	  m_cookie_set.put(cookies[i].getName(),cookies[i]);

      } // end if

      m_req.setAttribute(REQUEST_COOKIESET_ATTR,m_cookie_set);

    } // end if

  } // end initCookieSet

  private synchronized Cookie saveOffCookie(String name, String value, int age)
  {
    Cookie c = new Cookie(name,value);
    c.setMaxAge(age);
    c.setPath(m_req.getContextPath());
    if (m_resp!=null)
    { // add the cookie straight to the response
      if (logger.isDebugEnabled())
	logger.debug("cookie going straight to the HttpServletResponse");
      m_resp.addCookie(c);

    } // end if
    else
    { // needs to be put in the cookie holder
      if (logger.isDebugEnabled())
	logger.debug("cookie going to the NewCookieHolder holding area");
      if (m_holder==null)
      { // first look in the request attributes for the NewCookieHolder
	m_holder = (NewCookieHolder)(m_req.getAttribute(REQUEST_NEWCOOKIEHOLDER_ATTR));
	if (m_holder==null)
	{ // create a new NewCookieHolder and get it into the attributes
	  m_holder = new NewCookieHolder();
	  m_req.setAttribute(REQUEST_NEWCOOKIEHOLDER_ATTR,m_holder);

	} // end if

      } // end if

      m_holder.putCookie(c);

    } // end else

    return c;

  } // end saveOffCookie

  /*--------------------------------------------------------------------------------
   * Implementations from interface CookieControl
   *--------------------------------------------------------------------------------
   */

  public synchronized boolean isCookiePresent(String name)
  {
    initCookieSet();
    return m_cookie_set.containsKey(name);

  } // end isCookiePresent

  public synchronized String getCookie(String name)
  {
    initCookieSet();
    Cookie c = (Cookie)(m_cookie_set.get(name));
    return ((c==null) ? null : c.getValue());

  } // end getCookie

  public synchronized Collection getCookieNames()
  {
    initCookieSet();
    return Collections.unmodifiableSet(new HashSet(m_cookie_set.keySet()));

  } // end getCookieNames

  public synchronized void putPersistentCookie(String name, String value, int max_age)
  {
    initCookieSet();
    if (logger.isDebugEnabled())
      logger.debug("saving persistent cookie \"" + name + "\" to browser (max_age = " + max_age + ")");
    m_cookie_set.put(name,saveOffCookie(name,value,Math.max(max_age,1)));

  } // end putPersistentCookie

  public synchronized void putTemporaryCookie(String name, String value)
  {
    initCookieSet();
    if (logger.isDebugEnabled())
      logger.debug("saving temporary cookie \"" + name + "\" to browser");
    m_cookie_set.put(name,saveOffCookie(name,value,-1));

  } // end putTemporaryCookie

  public synchronized void deleteCookie(String name)
  {
    initCookieSet();
    if (logger.isDebugEnabled())
      logger.debug("deleting cookie \"" + name + "\" from browser");
    saveOffCookie(name,"",0);
    m_cookie_set.remove(name);

  } // end deleteCookie

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  static void flushCookies(HttpServletRequest req, HttpServletResponse resp)
  {
    NewCookieHolder holder = (NewCookieHolder)(req.getAttribute(REQUEST_NEWCOOKIEHOLDER_ATTR));
    if (holder!=null)
    { // if it's present, take it out and flush all cookies
      req.removeAttribute(REQUEST_NEWCOOKIEHOLDER_ATTR);
      holder.flushCookies(resp);

    } // end if

  } // end flushCookies

} // end class ServletCookieControl
