/*
 * Copyright (C) 2007 Helge Hess
 *
 * This file is part of JOPE.
 *
 * JOPE is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later version.
 *
 * JOPE is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JOPE; see the file COPYING. If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package org.opengroupware.jope.appserver.publisher;

import java.util.HashMap;
import java.util.Map;

import org.opengroupware.jope.appserver.core.WOApplication;
import org.opengroupware.jope.appserver.core.WOComponentDefinition;
import org.opengroupware.jope.appserver.core.WOContext;
import org.opengroupware.jope.appserver.core.WOResourceManager;
import org.opengroupware.jope.foundation.NSKeyValueCoding;

/**
 * JoContainerResourceManager
 * <p>
 * This resource manager looks for JoComponents in the given JoObject using
 * lookupName. Its a wrapper around another JoObject which it uses as the
 * lookup base.
 * <p>
 * JoContainerResourceManagers need to be hooked up in a queue. They do not call
 * lookupName with 'acquire'.
 */
public class JoContainerResourceManager extends WOResourceManager {
  
  protected WOResourceManager parentRM;
  protected IJoContext        context; /* note required internally, but passed on */
  protected Object            container;
  protected Map<String, IJoComponentDefinition> joComponents;

  public JoContainerResourceManager
    (Object _container, WOResourceManager _parent, IJoContext _ctx)
  {
    super(true /* caching, just lives for one transaction */);
    this.container = _container;
    this.parentRM  = _parent;
    this.context   = _ctx;
    this.joComponents = new HashMap<String, IJoComponentDefinition>(64);
  }
  
  
  /* lookup */
  
  /**
   * Lookup a resource manager for the objectby traversing the objects
   * containment hierarchy (TBD: context hierarchy would be better ...).
   *
   * @param _base - the object to start the lookup at
   * @param _ctx  - the WOContext of the transaction
   * @return a WOResourceManager instance, or null if none was found
   */
  public static WOResourceManager lookupResourceManager
    (Object _base, IJoContext _ctx)
  {
    // TBD: we could construct the RM in here. If we detect an IJoFolderish or
    //      JSJoFolder
    WOResourceManager rm = (WOResourceManager)IJoLocation.Utility
      .locateValueForKeyPath(_base, "resourceManager");
    if (rm != null)
      return rm;
    
    /* fallback to WOApplication resource manager (eg OK for root folder!) */
    //System.err.println("fallback to app-rm: " + _ctx);
    
    WOContext wctx = (WOContext)_ctx; // TBD: cast exception
    if (wctx == null)
      wctx = (WOContext)NSKeyValueCoding.Utility.valueForKey(_base, "context");
    if (wctx == null)
      return null;
    
    WOApplication app = wctx.application();
    if (app == null)
      return null;
    
    return app.resourceManager();
  }

  
  /* cache */
  
  protected IJoComponentDefinition findJoComponentWithName(String _name) {
    if (_name == null)
      return null;
    
    /* check cache */
  
    IJoComponentDefinition f = this.joComponents.get(_name);
    if (f != null)
      return f;
    if (this.joComponents.containsKey(_name)) /* can contain null */
      return null;
    
    /* Lookup object. Note that we do NOT acquire. Component acquisition is done
     * in the resource manager hierarchy!
     */
    Object o = IJoSecuredObject.Utility.lookupName
        (this.container, _name, this.context, false /* do not acquire */);
    
    /* check and cache result */
    
    if (o instanceof IJoComponentDefinition)
      f = (IJoComponentDefinition)o;
    else if (o != null) {
      log.warn("found a JoObject with name '" + _name + "', but its not a " +
          "component provider: " + o);
    }
    
    this.joComponents.put(_name, f /* can be null */);
    //System.err.println("findJSComponentWithName(\"" + _name +"\"): " + f);
    return f;
  }

  /**
   * Note: the path contains the pagename. Hence we need to lookup till
   * length-1.
   * 
   * @param _path - the array of names to traverse
   * @return the WOResourceManager responsible for the path
   */
  protected WOResourceManager findResourceManagerForPath(String[] _path) {
    int len = _path != null ? _path.length : 0;
    if (len < 2) return this;
    
    Object o = this.container;
    len--; // last name is the pagename
    for (int i = 0; i < len && o != null; i++) {
      o = IJoSecuredObject.Utility.lookupName
        (o, _path[i], this.context, false /* do not acquire */);
      if (o instanceof Exception) {
        log.error("could not locate resource manager!"); // TBD
        return null;
      }
    }
    if (o == null)
      return null;
    
    WOResourceManager rm = (WOResourceManager)NSKeyValueCoding.Utility
      .valueForKey(o, "resourceManager");
    if (rm != null)
      return rm;
    
    return lookupResourceManager(o, this.context);
  }
  
  /* class lookup */

  @Override
  public Class lookupComponentClass(String _name) {
    /* Note: this is only called for components */
    
    if (_name.indexOf('.') > 0) {
      String[] path = _name.split("\\.");
      WOResourceManager rm = this.findResourceManagerForPath(path);
      if (rm != null) return rm.lookupComponentClass(path[path.length - 1]);
    }
    
    IJoComponentDefinition joComp = this.findJoComponentWithName(_name);
    return (joComp != null)
      ? joComp.lookupComponentClass(_name, this)
      : this.parentRM.lookupComponentClass(_name);
  }

  @Override
  public Class lookupDynamicElementClass(String _name) {
    /* Only invoked to find dynamic elements, hence we do not need to check the
     * filesystem.
     */
    return this.parentRM.lookupDynamicElementClass(_name);
  }
  
  @Override
  public Class lookupDirectActionClass(String _name) {
    // TBD: JS direct actions are DIFFERENT. They have no WOComponentDefinition
    log.info("JS direct actions are not yet supported");
    return this.parentRM.lookupDirectActionClass(_name);
  }
  
  
  /* component lookup */
  
  @Override
  public WOComponentDefinition definitionForComponent
    (String _name, String[] _langs, WOResourceManager _clsctx)
  {
    if (_name.indexOf('.') > 0) {
      String[] path = _name.split("\\.");
      WOResourceManager rm = this.findResourceManagerForPath(path);
      if (rm != null)
        return rm.definitionForComponent(path[path.length-1], _langs, _clsctx);
    }

    IJoComponentDefinition joComponent = this.findJoComponentWithName(_name);
    if (joComponent != null) {
      /* Note: we pass in OUR RM, per default the method receives the top-level
       * RM! If we don't the lookup context is the application RM which does
       * not consider our RM-traversal list.
       */
      return joComponent.definitionForComponent(_name, _langs, this /* US */);
    }
    
    return this.parentRM.definitionForComponent(_name, _langs, _clsctx);
  }
    
  /* description */

  @Override
  public void appendAttributesToDescription(StringBuilder _d) {
    super.appendAttributesToDescription(_d);
    
    if (this.container != null) {
      _d.append(" folder=");
      _d.append(this.container);
    }
    
    if (this.context != null)
      _d.append(" ctx");
    if (this.parentRM != null)
      _d.append(" parent");
  }
  
}
