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

import java.io.IOException;
import org.apache.log4j.Logger;
import org.w3c.dom.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;
import com.silverwrist.dynamo.velocity.VelocityRendererConfig;
import com.silverwrist.venice.VeniceNamespaces;
import com.silverwrist.venice.iface.*;
import com.silverwrist.venice.util.*;

public class StandardContentSupplier
    implements NamedObject, ComponentInitialize, ComponentShutdown, ServiceProvider, ContentBlockProvider,
               TextRenderer
{
  /*--------------------------------------------------------------------------------
   * Internal object implementing Velocity operations
   *--------------------------------------------------------------------------------
   */

  public class ContentOps
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

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

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

    public Object header1(String title) throws DatabaseException
    {
      ContentBlock rc = getContentBlock("content.header");
      rc.setContentParameter("title",title);
      return rc;

    } // end header1

    public Object header2(String title, String subtitle) throws DatabaseException
    {
      ContentBlock rc = getContentBlock("content.header");
      rc.setContentParameter("title",title);
      rc.setContentParameter("subtitle",subtitle);
      return rc;

    } // end header1

  } // end class ContentOps

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

  private Logger logger = Logger.getLogger(StandardContentSupplier.class);

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

  private String m_name = null;           // name of the object
  private ObjectProvider m_blocks;        // global blocks provider
  private ComponentShutdown m_shut_1;     // shut down renderer
  private ComponentShutdown m_shut_2;     // shut down Velocity object

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public StandardContentSupplier()
  { // do nothing
  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface NamedObject
   *--------------------------------------------------------------------------------
   */

  public String getName()
  {
    return m_name;

  } // end getName

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentInitialize
   *--------------------------------------------------------------------------------
   */

  /**
   * Initialize the component.
   *
   * @param config_root Pointer to the section of the Dynamo XML configuration file that configures this
   *                    particular component.  This is to be considered "read-only" by the component.
   * @param services An implementation of {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider}
   *                 which provides initialization services to the component.  This will include an implementation
   *                 of {@link com.silverwrist.dynamo.iface.ObjectProvider ObjectProvider} which may be used to
   *                 get information about other objects previously initialized by the application.
   * @exception com.silverwrist.dynamo.except.ConfigException If an error is encountered in the component
   *            configuration.
   */
  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    logger.info("StandardContentSupplier initializing");

    XMLLoader loader = XMLLoader.get();
    String gprops = null;
    try
    { // verify the right node name
      loader.verifyNodeName(config_root,"object");

      // get the object's name
      m_name = loader.getAttribute(config_root,"name");

      // get the name of the global properties object
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element foo = loader.getSubElement(config_root_h,"global-properties");
      gprops = loader.getAttribute(foo,"object");

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      logger.fatal("XML loader exception in StandardContentSupplier",e);
      throw new ConfigException(e);

    } // end catch

    // Get the standard block provider.
    ServiceProvider gdm_sp =
        (ServiceProvider)(GetObjectUtils.getDynamoComponent(services,ServiceProvider.class,gprops));
    try
    { // get the "blocks" service from that object
      m_blocks = (ObjectProvider)(gdm_sp.queryService(ObjectProvider.class,"blocks"));

    } // end try
    catch (NoSuchServiceException e)
    { // this shouldn't happen, but...
      logger.fatal("Unable to find global properties object \"" + gprops + "\"",e);
      throw new ConfigException(StandardContentSupplier.class,"ContentMessages","init.noBlock",e);

    } // end catch

    // Register this object as a renderer for some objects.
    RendererRegistration rr = (RendererRegistration)(services.queryService(RendererRegistration.class));
    m_shut_1 = rr.registerRenderer(ErrorBox.class,this);

    // Add an object to the standard objects available to Velocity.
    VelocityRendererConfig vrcon =
        (VelocityRendererConfig)(services.queryService(VelocityRendererConfig.class));
    m_shut_2 = vrcon.addStandardComponentInstance("content",new ContentOps());

  } // end initialize

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

  public void shutdown()
  {
    m_shut_2.shutdown();
    m_shut_2 = null;
    m_shut_1.shutdown();
    m_shut_1 = null;
    m_blocks = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface ServiceProvider
   *--------------------------------------------------------------------------------
   */

  /**
   * Queries this object for a specified service.
   *
   * @param klass The class of the object that should be returned as a service.
   * @return A service object.  The service object is guaranteed to be of the class
   *         specified by <CODE>klass</CODE>; that is, if <CODE>queryService(klass)</CODE>
   *         yields some object <CODE>x</CODE>, then the expression <CODE>klass.isInstance(x)</CODE>
   *         is true.
   * @exception com.silverwrist.dynamo.except.NoSuchServiceException If no service is available in
   *            the specified class.
   */
  public Object queryService(Class klass)
  {
    if (klass==ContentBlockProvider.class)
      return (ContentBlockProvider)this;
    throw new NoSuchServiceException(getName(),klass);

  } // end queryService

  /**
   * Queries this object for a specified service.
   *
   * @param klass The class of the object that should be returned as a service.
   * @param serviceid ID for the service to be requested, to further discriminate between requests.
   * @return A service object.  The service object is guaranteed to be of the class
   *         specified by <CODE>klass</CODE>; that is, if <CODE>queryService(klass)</CODE>
   *         yields some object <CODE>x</CODE>, then the expression <CODE>klass.isInstance(x)</CODE>
   *         is true.
   * @exception com.silverwrist.dynamo.except.NoSuchServiceException If no service is available in
   *            the specified class.
   */
  public Object queryService(Class klass, String serviceid)
  {
    if (klass==ContentBlockProvider.class)
      return (ContentBlockProvider)this;
    throw new NoSuchServiceException(getName(),klass,serviceid);

  } // end queryService

  /*--------------------------------------------------------------------------------
   * Implementations from interface ContentBlockProvider
   *--------------------------------------------------------------------------------
   */

  public ContentBlock getContentBlock(String name) throws DatabaseException
  {
    try
    { // get the content block and wrap it in an object
      String block = m_blocks.getObject(VeniceNamespaces.CONTENT_LAF_NAMESPACE,name).toString();
      return new StringTemplateContentBlock(block,name);

    } // end try
    catch (NoSuchObjectException e)
    { // unwrap the DatabaseException if there's one in there
      if (e.getCause() instanceof DatabaseException)
	throw (DatabaseException)(e.getCause());
      else
	throw e;

    } // end catch

  } // end getContentBlock

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

  public void render(Object obj, TextRenderControl control) throws IOException, RenderingException
  {
    if (obj instanceof ErrorBox)
    { // render an ErrorBox
      ErrorBox ebox = (ErrorBox)obj;
      try
      { // get the error box template out of the database
	ContentBlock blk = this.getContentBlock("error.box");
	blk.setContentParameter("title",ebox.getTitle());
	blk.setContentParameter("message",ebox.getMessage());
	if (ebox.getException()!=null)
	  blk.setContentParameter("except",ebox.getException());
	if (ebox.getBackLink()!=null)
	{ // set the "back" and "backtype" parameters
	  blk.setContentParameter("backtype",ebox.getBackType());
	  blk.setContentParameter("back",ebox.getBackLink());

	} // end if

	control.renderSubObject(blk);  // render the block

      } // end try
      catch (DatabaseException e)
      { // translate this into a rendering exception
	RenderingException re = new RenderingException(StandardContentSupplier.class,"ContentMessages",
						       "render.errorBox",e);
	re.setParameter(0,e.getMessage());
	re.setParameter(1,ebox.getTitle());
	re.setParameter(2,ebox.getMessage());
	throw re;

      } // end catch

    } // end if

  } // end render

} // end class StandardContentSupplier
