/*
 * 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.dynamo.dialog;

import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import com.silverwrist.util.*;
import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

public class DialogManager
    implements NamedObject, ComponentInitialize, ComponentShutdown, DialogFactoryConfig, DialogLoader
{
  /*--------------------------------------------------------------------------------
   * Internal class implementing the dialog item factory
   *--------------------------------------------------------------------------------
   */

  private class Factory implements DialogItemFactory
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

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

    /*====================================================================
     * Implementations from interface DialogItemFactory
     *====================================================================
     */

    public DialogItem createDialogItem(Element elt, DialogPLAF plaf) throws DialogException
    {
      DialogItemFactory fact = (DialogItemFactory)(m_factory_data.get(elt.getTagName()));
      if (fact!=null)
	return fact.createDialogItem(elt,plaf);

      throw new UnknownDialogFieldException(elt.getTagName(),elt);

    } // end createDialogItem

  } // end class Factory

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

  private static final int DEFAULT_HARD_LIMIT = 5;
  private static final int DEFAULT_SOFT_LIMIT = 20;

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

  private String m_name;
  private String m_resource_prefix;
  private ResourceProvider m_prov;
  private DialogPLAF m_plaf;
  private HardSoftCache m_resource_cache;
  private Hashtable m_factory_data = new Hashtable();
  private ComponentShutdown m_shut_init;
  private ComponentShutdown m_shut_runtime;

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

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

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

  public String getName()
  {
    return m_name;

  } // end getName

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

  public void initialize(Element config_root, ServiceProvider services) throws ConfigException
  {
    XMLLoader loader = XMLLoader.get();
    int hard_limit = DEFAULT_HARD_LIMIT;
    int soft_limit = DEFAULT_SOFT_LIMIT;
    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 resource prefix
      DOMElementHelper config_h = new DOMElementHelper(config_root);
      m_resource_prefix = config_h.getSubElementText("resource-prefix");
      if (m_resource_prefix==null)
      { // set up default prefix
	m_resource_prefix = "/";

      } // end if
      else
      { // make sure the prefix is properly set
	if (!(m_resource_prefix.startsWith("/")))
	  m_resource_prefix = "/" + m_resource_prefix;
	if (!(m_resource_prefix.endsWith("/")))
	  m_resource_prefix += "/";

      } // end else

      // get the resource dialog cache information
      Element elt = config_h.getSubElement("resource-dialog-cache");
      if (elt!=null)
      { // get the cache limits
	hard_limit = loader.getAttributeInt(elt,"hardlimit",DEFAULT_HARD_LIMIT);
	soft_limit = loader.getAttributeInt(elt,"softlimit",DEFAULT_SOFT_LIMIT);

      } // end if

    } // end try
    catch (XMLLoadException e)
    { // error loading XML config data
      throw new ConfigException(e);

    } // end catch

    // Get the standard resource provider.
    m_prov = (ResourceProvider)(services.queryService(ResourceProvider.class));

    // Set up the default look-and-feel provider.
    m_plaf = new DefaultDialogLAF();

    // Create the resource cache.
    m_resource_cache = new HardSoftCache(hard_limit,soft_limit);

    // Set up the standard factory elements.
    DialogItemFactory fact = new StdItemFactory();
    m_factory_data.put("button",fact);
    m_factory_data.put("checkbox",fact);
    m_factory_data.put("countrylist",fact);
    m_factory_data.put("dynamo-id",fact);
    m_factory_data.put("email",fact);
    m_factory_data.put("header",fact);
    m_factory_data.put("hidden",fact);
    m_factory_data.put("int",fact);
    m_factory_data.put("localelist",fact);
    m_factory_data.put("password",fact);
    m_factory_data.put("text",fact);
    m_factory_data.put("tzlist",fact);

    // Get the service provider hooks interface.
    HookServiceProviders hooker = (HookServiceProviders)(services.queryService(HookServiceProviders.class));

    // Hook the initialization services with our initialization interface.
    SingletonServiceProvider ssp = new SingletonServiceProvider("DialogManager",DialogFactoryConfig.class,
								(DialogFactoryConfig)this);
    m_shut_init = hooker.hookInitServiceProvider(ssp);

    // Hook the runtime services with the DialogLoader interface.
    ssp = new SingletonServiceProvider("DialogManager",DialogLoader.class,(DialogLoader)this);
    m_shut_runtime = hooker.hookRuntimeServiceProvider(ssp);

  } // end initialize

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

  public void shutdown()
  {
    m_shut_runtime.shutdown();
    m_shut_runtime = null;
    m_shut_init.shutdown();
    m_shut_init = null;
    m_resource_cache.clear();
    m_factory_data.clear();
    m_plaf = null;
    m_prov = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface DialogFactoryConfig
   *--------------------------------------------------------------------------------
   */

  public synchronized void setDialogPLAF(DialogPLAF plaf)
  {
    m_plaf = ((plaf==null) ? new DefaultDialogLAF() : plaf);

  } // end setDialogPLAF

  public ComponentShutdown registerDialogItem(String item_name, DialogItemFactory factory)
      throws ConfigException
  {
    if (m_factory_data.containsKey(item_name))
    { // already registered - can't register again
      ConfigException ce = new ConfigException(DialogManager.class,"DialogMessages","control.registered");
      ce.setParameter(0,item_name);
      throw ce;

    } // end if

    m_factory_data.put(item_name,factory);
    return new ShutdownHashtableRemove(m_factory_data,item_name);

  } // end registerDialogItem

  /*--------------------------------------------------------------------------------
   * Implementations from interface DialogLoader
   *--------------------------------------------------------------------------------
   */

  public Dialog loadDialogResource(String resource_name) throws DialogException
  {
    Dialog dlg = (Dialog)(m_resource_cache.get(resource_name));
    if (dlg==null)
    { // OK, try loading the resource for the dialog...
      InputStream stm = null;
      try
      { // get the resource as a stream
	stm = m_prov.getResource(m_resource_prefix + resource_name);

	// parse it into an XML document
	XMLLoader loader = XMLLoader.get();
	Document doc = loader.load(stm,false);

	// get the root element, which should be <dialog/>
	Element elt = loader.getRootElement(doc,"dialog");

	// now create the dialog!  (and cache it)
	dlg = new DialogImpl(elt,new Factory(),m_plaf);
	m_resource_cache.put(resource_name,dlg);

      } // end try
      catch (IOException e)
      { // translate the exception and throw it
	DialogException de = new DialogException(DialogManager.class,"DialogMessages","dlg.loadErr",e);
	de.setParameter(0,resource_name);
	de.setParameter(1,e.getMessage());
	throw de;

      } // end catch
      catch (NoSuchResourceException e)
      { // translate this exception and throw it as well
	DialogException de = new DialogException(DialogManager.class,"DialogMessages","dlg.notFound",e);
	de.setParameter(0,resource_name);
	throw de;

      } // end catch
      catch (XMLLoadException e)
      { // this translates straight into a DialogException
	throw new DialogException(e);

      } // end catch
      finally
      { // make sure and close the stream before we go
	IOUtils.shutdown(stm);

      } // end finally

    } // end if

    // Clone the dialog before we return it, so as not to mess up the one in the cache.
    return (Dialog)(dlg.clone());

  } // end loadDialogResource

} // end class DialogManager
