/*
 * 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.core.impl;

import java.lang.ref.*;
import java.util.*;
import org.apache.log4j.*;
import org.w3c.dom.*;
import com.silverwrist.util.*;
import com.silverwrist.venice.core.ServiceGroup;
import com.silverwrist.venice.core.ServiceToken;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.security.*;
import com.silverwrist.venice.svc.*;
import com.silverwrist.venice.util.XMLLoader;

class ServiceControlManager implements ServiceControl
{
  /*--------------------------------------------------------------------------------
   * Internal class implementing ServiceTokens that allows them to be constructed
   *--------------------------------------------------------------------------------
   */

  static class MyServiceToken extends ServiceToken
  {
    public MyServiceToken(int group, String symbol, int index)
    {
      super(group,symbol,index);

    } // end constructor

  } // end class MyServiceToken

  /*--------------------------------------------------------------------------------
   * Internal "Community Service Site" class
   *--------------------------------------------------------------------------------
   */

  class MyCommServiceSite implements CommServiceSite
  {
    MyCommServiceSite()
    { // do nothing
    } // end constructor

  } // end class MyCommServiceSite

  /*--------------------------------------------------------------------------------
   * Internal "Service Predicate" class
   *--------------------------------------------------------------------------------
   */

  abstract class ServicePredicate
  {
    public abstract boolean test(Service svc);

  } // end class ServicePredicate

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

  private static Category logger = Category.getInstance(ServiceControlManager.class);

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

  private Map[] symbol_to_service = new Map[NUM_SVCGRPS];
  private Map[] index_to_service = new Map[NUM_SVCGRPS];
  private SoftReference[] list_all_cache = new SoftReference[NUM_SVCGRPS];
  private SoftReference[] list_default_cache = new SoftReference[NUM_SVCGRPS];

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  ServiceControlManager(Document cfg, SecurityMonitorEnvironment sm_env)
      throws ConfigException
  {
    XMLLoader loader = XMLLoader.get();
    Element root = loader.configGetRootElement(cfg,"services-config");
    DOMElementHelper root_h = new DOMElementHelper(root);
    NodeList nl;
    int i;

    symbol_to_service[SVCGRP_GLOBAL] = Collections.EMPTY_MAP;
    index_to_service[SVCGRP_GLOBAL] = Collections.EMPTY_MAP;

    // Get the "community" section
    Element sect = root_h.getSubElement("community");
    if (sect!=null)
    { // load the services for the section
      HashMap tmp_symbol_to_service = new HashMap();
      HashMap tmp_index_to_service = new HashMap();
      SecurityMonitor sm_comm = sm_env.getMonitor("Community");
      nl = sect.getChildNodes();
      for (i=0; i<nl.getLength(); i++)
      { // get each element node...
	Node n = nl.item(i);
	if ((n.getNodeType()==Node.ELEMENT_NODE) && (n.getNodeName().equals("service")))
	{ // create the appropriate service settings
	  CommService svc = initCommunityService((Element)n,sm_comm);
	  tmp_symbol_to_service.put(svc.getToken().getSymbol(),svc);
	  tmp_index_to_service.put(new Integer(svc.getToken().getIndex()),svc);

	} // end if

      } // end for

      if (tmp_symbol_to_service.size()>0)
	symbol_to_service[SVCGRP_COMMUNITY] = Collections.unmodifiableMap(tmp_symbol_to_service);
      else
	symbol_to_service[SVCGRP_COMMUNITY] = Collections.EMPTY_MAP;
      if (tmp_index_to_service.size()>0)
	index_to_service[SVCGRP_COMMUNITY] = Collections.unmodifiableMap(tmp_index_to_service);
      else
	index_to_service[SVCGRP_COMMUNITY] = Collections.EMPTY_MAP;

    } // end if
    else
    { // no community services - undo this
      symbol_to_service[SVCGRP_COMMUNITY] = Collections.EMPTY_MAP;
      index_to_service[SVCGRP_COMMUNITY] = Collections.EMPTY_MAP;

    } // end else

    symbol_to_service[SVCGRP_USER] = Collections.EMPTY_MAP;
    index_to_service[SVCGRP_USER] = Collections.EMPTY_MAP;

    for (i=0; i<NUM_SVCGRPS; i++)
    { // blank out all our cache arrays
      list_all_cache[i] = null;
      list_default_cache[i] = null;

    } // end for

  } // end constructor

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

  private static final ServiceToken newToken(int group, Element service) throws ConfigException
  {
    XMLLoader loader = XMLLoader.get();

    // Load the symbol and index from the root element.
    String symbol = loader.configGetAttribute(service,"id");
    int index = loader.configGetAttributeInt(service,"index");

    return new MyServiceToken(group,symbol,index);

  } // end newToken

  private final CommService initCommunityService(Element root, SecurityMonitor sm) throws ConfigException
  {
    ServiceToken token = newToken(SVCGRP_COMMUNITY,root);
    CommService svc;
    // FUTURE: do a create of a specific class
    svc = new StaticCommService();
    svc.initialize(token,root,sm,new MyCommServiceSite());
    return svc;

  } // end initCommunityService

  private final synchronized Set listServices(int group, ServicePredicate predicate)
  {
    HashSet tmp = new HashSet();
    Iterator it = symbol_to_service[group].values().iterator();
    while (it.hasNext())
    { // get each service in turn
      Service svc = (Service)(it.next());
      if (predicate.test(svc))
	tmp.add(svc.getToken());

    } // end while

    if (tmp.isEmpty())
      return Collections.EMPTY_SET;
    else
      return Collections.unmodifiableSet(tmp);
    
  } // end listCommunities

  /*--------------------------------------------------------------------------------
   * Implementations from interface ServiceControl
   *--------------------------------------------------------------------------------
   */

  public Set getAllServices(int group)
  {
    if ((group<0) || (group>=NUM_SVCGRPS))
      throw new IndexOutOfBoundsException("group index not valid");
    synchronized (this)
    { // extract the reference
      Set rc = null;
      if (list_all_cache[group]!=null)
	rc = (Set)(list_all_cache[group].get());
      if (rc==null)
      { // need to regenerate the set
	rc = listServices(group,new ServicePredicate(){
	    public final boolean test(Service svc) { return true; }
	  });

	// cache the result
	list_all_cache[group] = new SoftReference(rc);

      } // end if

      return rc;

    } // end synchronized block

  } // end getAllServices

  public Set getDefaultServices(int group)
  {
    if ((group<0) || (group>=NUM_SVCGRPS))
      throw new IndexOutOfBoundsException("group index not valid");
    synchronized (this)
    { // extract the reference
      Set rc = null;
      if (list_default_cache[group]!=null)
	rc = (Set)(list_default_cache[group].get());
      if (rc==null)
      { // need to regenerate the set
	rc = listServices(group,new ServicePredicate(){
	    public final boolean test(Service svc) { return svc.isDefault(); }
	  });

	// cache the result
	list_default_cache[group] = new SoftReference(rc);

      } // end if

      return rc;

    } // end synchronized block

  } // end getDefaultServices

  public Service getServiceForSymbol(int group, String sym)
  {
    if ((group<0) || (group>=NUM_SVCGRPS))
      throw new IndexOutOfBoundsException("group index not valid");
    return (Service)(symbol_to_service[group].get(sym));

  } // end getServiceForSymbol

  public Service getServiceForToken(ServiceToken token)
  {
    return this.getServiceForSymbol(token.getGroup(),token.getSymbol());

  } // end getServiceForToken

  public ServiceToken getTokenForIndex(int group, int ndx)
  {
    if ((group<0) || (group>=NUM_SVCGRPS))
      throw new IndexOutOfBoundsException("group index not valid");
    Service svc = (Service)(index_to_service[group].get(new Integer(ndx)));
    return ((svc==null) ? null : svc.getToken());

  } // end getTokenForIndex

  public ServiceToken getTokenForSymbol(int group, String sym)
  {
    Service svc = this.getServiceForSymbol(group,sym);
    return ((svc==null) ? null : svc.getToken());

  } // end getTokenForSymbol

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

  public final synchronized void shutdown()
  {
    for (int i=0; i<NUM_SVCGRPS; i++)
    { // shutdown all three lists of services
      Iterator it = symbol_to_service[i].values().iterator();
      while (it.hasNext())
      { // shut down each service in turn
	Service svc = (Service)(it.next());
	svc.shutdown();

      } // end while

      symbol_to_service[i] = Collections.EMPTY_MAP;

    } // end for

  } // end shutdown

} // end class ServiceControlManager


