/*
  Copyright (C) 2006 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.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/*
 * WOPackageLinker
 *
 * This class is used by WOApplication to load required packages for the
 * application.
 * 
 * THREAD: this class is not threadsafe.
 */
public class WOPackageLinker {
  
  protected final Log log = LogFactory.getLog("WOPackageLinker");
  
  /* used to protect against recursive loads */
  protected Set<String> packagesInLoad;
  
  protected List<WOResourceManager> resourceManagers; 
  
  protected boolean enableCaching;
  
  public WOPackageLinker(boolean _enableCaching) {
    this.enableCaching    = _enableCaching;
    this.resourceManagers = new ArrayList<WOResourceManager>(8);
    this.packagesInLoad   = new HashSet<String>(16);
  }
  
  /* accessors */
  
  public WOResourceManager resourceManager() {
    if (this.resourceManagers == null)
      return null;
    if (this.resourceManagers.size() == 0)
      return null;
    if (this.resourceManagers.size() == 1)
      return this.resourceManagers.get(0);
    
    return new WOCompoundResourceManager
      (this.resourceManagers, this.enableCaching);
  }
  
  /* main entry points */
  
  public boolean linkWithSpecification(URL _url) {
    if (_url == null)
      return false;
    
    if (this.log.isDebugEnabled())
      this.log.debug("link specification: " + _url);
    
    InputStream in = null;
    try {
      in = _url.openStream();
    }
    catch (IOException e) {
      this.log.error("could not open link specification: " + _url, e);
      return false;
    }
    return in != null ? this.linkWithSpecification(in) : false;
  }
  
  public boolean linkWithSpecification(InputStream _in) {
    if (_in == null)
      return false;
    
    BufferedReader r = new BufferedReader(new InputStreamReader(_in));
    try {
      String s;
      while ((s = r.readLine()) != null) {
        int cidx = s.indexOf("#");
        if (cidx != -1) s = s.substring(0, cidx);
        cidx = s.indexOf("//");
        if (cidx != -1) s = s.substring(0, cidx);
        s = s.trim();
        if (s.length() == 0) continue;
        
        this.linkFramework(s);
      }
      return true;
    }
    catch (IOException e) {
      this.log.error("failed to read JOPE link file ...", e);
      return false;
    }    
  }
  
  /* linking packages */
  
  public boolean linkFramework(String _pkg) {
    if (_pkg == null)
      return false;
    if (_pkg.length() == 0)
      return false;
    
    /* avoid load cycles */
    if (this.packagesInLoad.contains(_pkg))
      return true;
    this.packagesInLoad.add(_pkg);
    
    this.log.debug("  link framework: " + _pkg);
    
    /* first lookup base class for package (used as the hook) */
    
    Class pkgbase; 
    try {
      pkgbase = Class.forName(_pkg + "." + "WOFramework");
    }
    catch (ClassNotFoundException e) {
      this.log.debug("    did not find package base class", null /* e */);
      pkgbase = null;
    }
    
    if (pkgbase == null) {
      this.log.warn("    could not link package: " + _pkg);
      return false;
    }
    this.log.debug("    using base class:" + pkgbase);
    
    /* link */
    
    WOPackageLinker linker = new WOPackageLinker(this.enableCaching);
    linker.linkClass(pkgbase);
    
    /* next check whether the package wants to link something */
    
    // TODO: check whether this works properly
    URL deplink = pkgbase.getResource("jopelink.txt");
    if (deplink != null) {
      this.log.debug("    linking framework dependencies ...");
      linker.linkWithSpecification(deplink);
    }
    
    /* link system frameworks */
    // TODO: we get an runaway when calling this, find out why
    // String sysbase = WOApplication.class.getPackage().getName();
    // linker.linkFramework(sysbase + ".elements");
    // linker.linkFramework(sysbase);
    
    /* retrieve the resulting resource manager */
    WOResourceManager rm = linker.resourceManager();
    if (rm != null)
      this.resourceManagers.add(rm);
    
    return true;
  }
  
  public boolean linkClass(Class _cls) {
    WOResourceManager rm = new WOClassResourceManager(_cls, this.enableCaching);
    if (rm != null) {
      this.resourceManagers.add(rm);
      return true;
    }
    return false;
  }
}
