/*
 * 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) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.servlets;

import java.io.*;
import java.lang.ref.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.log4j.*;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.servlets.format.*;
import com.silverwrist.venice.servlets.format.menus.LeftMenu;

public class Variables
{
  /*--------------------------------------------------------------------------------
   * Static data values
   *--------------------------------------------------------------------------------
   */

  // ServletContext ("application") attributes
  protected static final String ENGINE_ATTRIBUTE = "com.silverwrist.venice.core.Engine";
  protected static final String STYLESHEET_ATTRIBUTE = "com.silverwrist.venice.rendering.StyleSheet";

  // HttpSession ("session") attributes
  protected static final String USERCTXT_ATTRIBUTE = "user.context";
  protected static final String MENU_ATTRIBUTE = "current.menu";

  // ServletRequest ("request" attributes)
  protected static final String COOKIEJAR_ATTRIBUTE = "com.silverwrist.venice.servlets.CookieJar";

  // Servlet initialization parameters
  protected static final String ENGINE_INIT_PARAM = "venice.config";

  // Cookie name
  public static final String LOGIN_COOKIE = "VeniceAuth";

  private static Category logger = Category.getInstance(Variables.class);
  private static Integer engine_gate = new Integer(0);

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

  public static VeniceEngine getVeniceEngine(ServletContext ctxt) throws ServletException
  {
    synchronized (engine_gate)
    { // we must synchronize efforts to access the engine
      Object foo = ctxt.getAttribute(ENGINE_ATTRIBUTE);
      if (foo!=null)
	return (VeniceEngine)foo;

      String root_file_path = ctxt.getRealPath("/");
      if (!(root_file_path.endsWith("/")))
	root_file_path += "/";

      String cfgfile = ctxt.getInitParameter(ENGINE_INIT_PARAM);  // get the config file name
      if (!(cfgfile.startsWith("/")))
	cfgfile = root_file_path + cfgfile;
      logger.info("Initializing Venice engine using config file: " + cfgfile);

      try
      { // extract the configuration file name and create the engine
	VeniceEngine engine = Startup.createEngine(cfgfile,root_file_path);
	ctxt.setAttribute(ENGINE_ATTRIBUTE,engine);
	return engine;

      } // end try
      catch (ConfigException e)
      { // configuration failed!  post an error message
	logger.fatal("Engine configuration failed: " + e.getMessage(),e);
	throw new ServletException("Venice engine configuration failed: " + e.getMessage(),e);

      } // end catch
      catch (DataException e2)
      { // configuration failed!  post an error message
	logger.fatal("Engine data load failed: " + e2.getMessage(),e2);
	throw new ServletException("Venice engine data load failed: " + e2.getMessage(),e2);

      } // end catch

    } // end synchronized block

  } // end getVeniceEngine

  public static UserContext getUserContext(ServletContext ctxt, HttpServletRequest request,
					   HttpSession session)
      throws ServletException
  {
    Object uctmp = session.getAttribute(USERCTXT_ATTRIBUTE);
    if (uctmp!=null)
      return (UserContext)uctmp;

    try
    { // use the Venice engine to create a new user context and save it off
      VeniceEngine engine = getVeniceEngine(ctxt);
      UserContext user = engine.createUserContext(request.getRemoteAddr());

      // Did the user send a Venice authentication cookie?  If so, try to use it.
      Cookie[] cookies = request.getCookies();
      Cookie venice_cookie = null;
      for (int i=0; (venice_cookie==null) && (i<cookies.length); i++)
      { // look for a Venice authentication cookie
	if (LOGIN_COOKIE.equals(cookies[i].getName()))
	  venice_cookie = cookies[i];

      } // end for

      if (venice_cookie!=null)
      { // we've found the cookie - now attempt to authenticate with it
	if (!(user.authenticateWithToken(venice_cookie.getValue())))
	{ // the authentication failed - this cookie MUST be bogus, delete it!
	  Cookie zapper = new Cookie(LOGIN_COOKIE,"");
	  zapper.setMaxAge(0);
	  zapper.setPath(request.getContextPath());
	  saveCookie(request,zapper);
	  
	} // end if
	// else we're authenticated - let the cookie stay!

      } // end if
      // else don't bother trying to authenticate

      // save the user context off as usual and return it
      session.setAttribute(USERCTXT_ATTRIBUTE,user);
      return user;

    } // end try
    catch (DataException e)
    { // context creation failed - remap the exception
      logger.error("Session creation failed: " + e.getMessage(),e);
      throw new ServletException("Session creation failed: " + e.getMessage(),e);

    } // end catch

  } // end getUserContext

  public static void putUserContext(HttpSession session, UserContext user)
  {
    session.setAttribute(USERCTXT_ATTRIBUTE,user);
    session.removeAttribute(MENU_ATTRIBUTE);
    
  } // end putUserContext

  public static void clearUserContext(HttpSession session)
  {
    session.removeAttribute(USERCTXT_ATTRIBUTE);
    session.removeAttribute(MENU_ATTRIBUTE);

  } // end clearUserContext

  public static ComponentRender getMenu(HttpSession session)
  {
    return (ComponentRender)(session.getAttribute(MENU_ATTRIBUTE));

  } // end getMenu

  public static void setMenuTop(ServletContext ctxt, HttpSession session)
  {
    Object obj = session.getAttribute(MENU_ATTRIBUTE);
    boolean do_change;
    if ((obj==null) || !(obj instanceof LeftMenu))
      do_change = true;
    else
    { // look to see if this is the "top" menu
      LeftMenu mnu = (LeftMenu)obj;
      do_change = !(mnu.getIdentifier().equals("top"));

    } // end else

    if (do_change)
    { // get the new menu from the RenderConfig object and save it
      try
      { // but note that getting the rendering configuration can throw a ServletException
	RenderConfig cfg = RenderConfig.getRenderConfig(ctxt);
	LeftMenu new_mnu = cfg.getLeftMenu("top");
	session.setAttribute(MENU_ATTRIBUTE,new_mnu);

      } // end try
      catch (ServletException e)
      { // if we fail, just clear it out
	logger.warn("caught ServletException in setMenuTop",e);
	session.removeAttribute(MENU_ATTRIBUTE);

      } // end catch

    } // end if

  } // end setMenuTop

  public static void setMenuCommunity(HttpSession session, CommunityContext comm)
  {
    Object obj = session.getAttribute(MENU_ATTRIBUTE);
    boolean do_change;
    if ((obj==null) || !(obj instanceof MenuCommunity))
      do_change = true;
    else
    { // look at the actual community IDs underlying the MenuCommunity
      MenuCommunity tmp = (MenuCommunity)obj;
      do_change = (tmp.getID()!=comm.getCommunityID());

    } // end else

    if (do_change)
    { // switch to the appropriate MenuCommunity
      MenuCommunity mc = new MenuCommunity(comm);
      session.setAttribute(MENU_ATTRIBUTE,mc);
      
    } // end if

  } // end setMenuCommunity

  public static void clearMenu(HttpSession session)
  {
    session.removeAttribute(MENU_ATTRIBUTE);

  } // end clearMenu

  public static void failIfNull(Object obj) throws ServletException
  {
    if (obj==null)
      throw new ServletException("improper context - data not set");

  } // end failIfNull

  public static void saveCookie(ServletRequest request, Cookie cookie)
  {
    ArrayList cookiejar = null;
    Object o = request.getAttribute(COOKIEJAR_ATTRIBUTE);
    if ((o==null) || !(o instanceof ArrayList))
    { // create a new cookie jar and save it
      cookiejar = new ArrayList();
      request.setAttribute(COOKIEJAR_ATTRIBUTE,cookiejar);

    } // end if
    else  // found our cookie jar
      cookiejar = (ArrayList)o;

    // save the cookie in the cookie jar (to be flushed later)
    cookiejar.add(cookie);

  } // end saveCookie

  public static void flushCookies(ServletRequest request, HttpServletResponse response)
  {
    Object o = request.getAttribute(COOKIEJAR_ATTRIBUTE);
    request.removeAttribute(COOKIEJAR_ATTRIBUTE);
    if (o instanceof ArrayList)
    { // found the cookie jar - now take the cookies out and add them to the response
      ArrayList cookiejar = (ArrayList)o;
      Iterator it = cookiejar.iterator();
      while (it.hasNext())
      { // add each cookie in turn
	Cookie cookie = (Cookie)(it.next());
	response.addCookie(cookie);

      } // end while

    } // end if

  } // end flushCookies

  public static String getStyleSheetData(ServletContext ctxt, RenderData rdat) throws IOException
  {
    if (!(rdat.useStyleSheet()))
      return null;
    String data = null;
    SoftReference r = (SoftReference)(ctxt.getAttribute(STYLESHEET_ATTRIBUTE));
    if (r!=null)
      data = (String)(r.get());
    if ((data==null) || rdat.hasStyleSheetChanged())
    { // construct or reconstruct the stylesheet data, save a soft reference to it
      data = rdat.loadStyleSheetData();
      r = new SoftReference(data);
      ctxt.setAttribute(STYLESHEET_ATTRIBUTE,r);

    } // end if

    return data;

  } // end getStyleSheetData

} // end class Variables
