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

import java.util.*;
import org.apache.commons.collections.*;
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.venice.SearchMode;
import com.silverwrist.venice.iface.VeniceCategory;

public class CategoryManager implements NamedObject, ComponentInitialize, ComponentShutdown, CategoryService
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

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

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

  private String m_name;                       // this object's name
  private CategoryOps m_ops;                   // database operations
  private ReferenceMap m_categories;           // cached category values

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

  public CategoryManager()
  {
    m_categories = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);

  } // end constructor

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

  private final List convertList(List base) throws DatabaseException
  {
    if (base.isEmpty())
      return Collections.EMPTY_LIST;

    ArrayList rc = new ArrayList(base.size());
    synchronized (this)
    { // convert the tuples list ont a full category list
      Iterator it = base.iterator();
      while (it.hasNext())
      { // get the tuple from the list and convert it
	CategoryTuple tuple = (CategoryTuple)(it.next());
	Integer key = new Integer(tuple.getCategoryID());
	VeniceCategory cat = (VeniceCategory)(m_categories.get(key));
	if (cat==null)
	{ // need to create a new category object and add it
	  if (tuple.getParentCategoryID()<0)
	    cat = new CategoryImpl(this,tuple.getCategoryID(),tuple.getSymlink(),tuple.getName());
	  else
	    cat = new CategoryImpl(lookup(tuple.getParentCategoryID()),tuple.getCategoryID(),tuple.getSymlink(),
				   tuple.getName());
	  m_categories.put(key,cat);

        } // end if

	rc.add(cat);

      } // end while

    } // end synchronized block

    return Collections.unmodifiableList(rc);

  } // end convertList

  /*--------------------------------------------------------------------------------
   * 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("CategoryManager initializing");

    XMLLoader loader = XMLLoader.get();
    String name_pool = 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 database pool
      DOMElementHelper config_root_h = new DOMElementHelper(config_root);
      Element foo = loader.getSubElement(config_root_h,"database");
      name_pool = loader.getAttribute(foo,"connection");

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

    } // end catch

    // Get the database connection pool.
    DBConnectionPool pool = GetObjectUtils.getDatabaseConnection(services,name_pool);

    // Get the operations object.
    m_ops = CategoryOps.get(pool);

  } // end initialize

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

  public void shutdown()
  {
    m_categories.clear();
    m_ops.dispose();
    m_ops = null;

  } // end shutdown

  /*--------------------------------------------------------------------------------
   * Implementations from interface CategoryService
   *--------------------------------------------------------------------------------
   */

  public boolean isValidCategoryID(int catid)
  {
    if (catid==-1)
      return true;  // valid by definition; it means "Top"
    if (m_categories.containsKey(new Integer(catid)))
      return true;  // already present in our category map, it must be OK
    try
    { // call down to the database
      return m_ops.verifyCategoryID(catid);

    } // end try
    catch (DatabaseException e)
    { // don't throw this exception!
      logger.warn("CategoryManager.isValidCategoryID(): database failure",e);

    } // end catch

    return false;  // default return

  } // end isValidCategoryID

  public List getRootCategoryList() throws DatabaseException
  {
    return getCategorySublist(-1);

  } // end getRootCategoryList

  public VeniceCategory getCategory(int catid) throws DatabaseException
  {
    return (VeniceCategory)lookup(catid);

  } // end getCategory

  public List searchForCategories(SearchMode mode, String term, int offset, int count) throws DatabaseException
  {
    return convertList(m_ops.search(mode,term,offset,count));

  } // end searchForCategories

  public int getSearchCategoryCount(SearchMode mode, String term) throws DatabaseException
  {
    return m_ops.getSearchCount(mode,term);

  } // end getSearchCategoryCount

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

  synchronized CategoryImpl lookup(int catid) throws DatabaseException
  {
    Integer key = new Integer(catid);
    CategoryImpl rc = (CategoryImpl)(m_categories.get(key));
    if (rc!=null)
      return rc;

    if (catid<0)
    { // for "TOP" category, handle specially
      rc = new CategoryImpl(this);
      m_categories.put(key,rc);
      return rc;

    } // end if

    // look up the tuple for this category
    CategoryTuple tuple = m_ops.getCategory(catid);
    if (tuple==null)
    { // category not found!
      DatabaseException de = new DatabaseException(CategoryManager.class,"CommunityMessages","cat.notfound");
      de.setParameter(0,String.valueOf(catid));
      throw de;

    } // end if

    if (tuple.getParentCategoryID()<0)
      rc = new CategoryImpl(this,tuple.getCategoryID(),tuple.getSymlink(),tuple.getName());
    else  // recurse to look up the parent, and so on
      rc = new CategoryImpl(lookup(tuple.getParentCategoryID()),tuple.getCategoryID(),tuple.getSymlink(),
			    tuple.getName());
    m_categories.put(key,rc);
    return rc;

  } // end lookup

  List getCategorySublist(int parentid) throws DatabaseException
  {
    return convertList(m_ops.getCategoriesList(parentid));

  } // end getCategorySublist

  synchronized VeniceCategory canonicalize(CategoryImpl obj)
  {
    Integer key = new Integer(obj.getCategoryID());
    VeniceCategory rc = (VeniceCategory)(m_categories.get(key));
    if (rc==null)
    { // it wasn't in our refmap - it is now!
      m_categories.put(key,obj);
      rc = obj;

    } // end if
    else  // dispose of the unneeded second copy
      obj.dispose();
    return rc;

  } // end canonicalize

} // end class CategoryManager
