/*
 * 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.venice.frame;

import java.io.*;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.log4j.Logger;
import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.servlet.ServletBase;
import com.silverwrist.dynamo.util.*;
import com.silverwrist.dynamo.velocity.VelocityRenderable;
import com.silverwrist.venice.VeniceNamespaces;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.iface.*;
import com.silverwrist.venice.session.SessionInfoParams;

public class StyleSheetServlet extends ServletBase
    implements ComponentShutdown, TextRenderer, DynamicUpdateListener
{
  /*--------------------------------------------------------------------------------
   * Inner class implementing the template for rendering.
   *--------------------------------------------------------------------------------
   */

  private class StyleSheetTemplate implements VelocityRenderable
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private String m_template_name;

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

    StyleSheetTemplate(String template_name)
    {
      m_template_name = template_name;

    } // end constructor

    /*====================================================================
     * Implementations from interface VelocityParamSupplier
     *====================================================================
     */

    public Object getParameter(String key)
    {
      return null;

    } // end getParameter
    
    public Collection getParameterNames()
    {
      return Collections.EMPTY_SET;

    } // end getParameterNames

    /*====================================================================
     * Implementations from interface VelocityRenderable
     *====================================================================
     */

    public String getMimeType()
    {
      return "text/css";

    } // end getMimeType

    public String getTemplateName()
    {
      return m_template_name;

    } // end getTemplateName

  } // end class StyleSheetTemplate

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

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

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

  private ObjectProvider m_props;
  private ReferenceMap m_cache = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
  private Object m_cache_sync = new Object();

  /*--------------------------------------------------------------------------------
   * Abstract implementations from class ServletBase
   *--------------------------------------------------------------------------------
   */

  protected Object process(Request r, Application app) throws Exception
  {
    // Get the first half of the sheet property name, based on the request servlet name.
    StylesheetMapper mapper = (StylesheetMapper)(app.queryService(StylesheetMapper.class));
    String prop1 = mapper.getStylesheetPrefix(r.getRequestPath());
    if (prop1==null)
    { // no configuration - throw this
      logger.error("stylesheet prefix for " + r.getRequestPath() + " not configured");
      StyleSheetException sse = new StyleSheetException(StyleSheetServlet.class,"FrameMessages","ss.noPrefix");
      sse.setParameter(0,r.getRequestPath());
      throw sse;

    } // end if

    // Get the second half of the sheet property name - the stylesheet type as noted
    // in the session.
    RequestHelper rh = new RequestHelper(r);
    SessionInfo session = rh.getSession();
    String prop2 = null;
    try
    { // get the property name
      prop2 = (String)(session.getObject(SessionInfoParams.NAMESPACE,
					 SessionInfoParams.ATTR_STYLESHEET_SELECTOR));

    } // end try
    catch (NoSuchObjectException e)
    { // property not found
      logger.warn("session stylesheet selector not found, defaulting to 'normal'");
      prop2 = "normal";  // default value

    } // end catch

    String propname = "sheet." + prop1 + "." + prop2;  // this is the full property name
    if (logger.isDebugEnabled())
      logger.debug("Stylesheet property name: " + propname);

    StyleSheetData rc = null;
    synchronized (m_cache_sync)
    { // See if the stylesheet is cached.
      rc = (StyleSheetData)(m_cache.get(propname));
      if (rc==null)
      { // generate the style sheet data and cache it
	String template_name = null;
	try
	{ // get the template name
	  template_name = (String)(m_props.getObject(VeniceNamespaces.STYLESHEET_NAMESPACE,propname));
	  if (logger.isDebugEnabled())
	    logger.debug("Rendering template name: " + template_name);

	} // end try
	catch (NoSuchObjectException e)
	{ // property not found - throw an exception
	  logger.error("did not find stylesheet property with name " + propname,e);
	  StyleSheetException sse = new StyleSheetException(StyleSheetServlet.class,"FrameMessages",
							    "ss.noTemplate",e);
	  sse.setParameter(0,propname);
	  throw sse;

	} // end catch

	// create and render the template
	StyleSheetTemplate templ = new StyleSheetTemplate(template_name);
	RenderImmediate rimm = (RenderImmediate)(r.queryService(RenderImmediate.class));
	try
	{ // render the stylesheet template and create the StyleSheetData object
	  rc = new StyleSheetData(rimm.renderTextObject(templ));

	} // end try
	catch (IOException e)
	{ // throw this as an exception
	  logger.error("RenderImmediate on stylesheet " + template_name + " threw IOException",e);
	  StyleSheetException sse = new StyleSheetException(StyleSheetServlet.class,"FrameMessages",
							    "ss.renderFail",e);
	  sse.setParameter(0,template_name);
	  sse.setParameter(1,e.getMessage());
	  throw sse;

	} // end catch
	catch (RenderingException e)
	{ // throw this as an exception
	  logger.error("RenderImmediate on stylesheet " + template_name + " threw RenderingException",e);
	  StyleSheetException sse = new StyleSheetException(StyleSheetServlet.class,"FrameMessages",
							    "ss.renderFail",e);
	  sse.setParameter(0,template_name);
	  sse.setParameter(1,e.getMessage());
	  throw sse;

	} // end catch

	m_cache.put(propname,rc);  // cache the new data

      } // end if

    } // end synchronized block

    return rc;  // return the style sheet data

  } // end process

  /*--------------------------------------------------------------------------------
   * Overrides from class ServletBase
   *--------------------------------------------------------------------------------
   */

  protected void initServlet(ServiceProvider services) throws DynamoException
  {
    logger.info("Starting up StyleSheetServlet");

    // Get the global properties provider.
    ServiceProvider gprop_sp =
        (ServiceProvider)(GetObjectUtils.getDynamoComponent(services,ServiceProvider.class,"globals"));
    m_props = (ObjectProvider)(gprop_sp.queryService(ObjectProvider.class,"properties"));

    // Register this class as being a renderer for StyleSheetData.
    RendererRegistration rreg = (RendererRegistration)(services.queryService(RendererRegistration.class));
    addShutdownHook(rreg.registerRenderer(StyleSheetData.class,this));

    // Register to receive global property update events.
    EventListenerRegistration reg =
        (EventListenerRegistration)(services.queryService(EventListenerRegistration.class));
    addShutdownHook(reg.registerDynamicUpdateListener(GlobalPropertyUpdateEvent.class,this));

    logger.info("StyleSheetServlet initialized");

  } // end initServlet

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  public void shutdown()
  {
    m_props = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface TextRenderer
   *--------------------------------------------------------------------------------
   */

  public void render(Object obj, TextRenderControl control) throws IOException, RenderingException
  {
    ((StyleSheetData)obj).render(control);

  } // end render

  /*--------------------------------------------------------------------------------
   * Implementations from interface DynamicUpdateListener
   *--------------------------------------------------------------------------------
   */

  public void updateReceived(DynamicUpdateEvent evt)
  {
    GlobalPropertyUpdateEvent event = (GlobalPropertyUpdateEvent)evt;
    if (VeniceNamespaces.STYLESHEET_NAMESPACE.equals(event.getPropertyNamespace()))
    { // a stylesheet template name has changed...
      synchronized (m_cache_sync)
      { // un-cache the corresponding stylesheet data
	m_cache.remove(event.getPropertyName());

      } // end synchronized block

    } // end if

  } // end updateReceived

} // end class StyleSheetServlet
