package JavaAgent.agent;

import JavaAgent.resource.*;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.io.*;
import java.net.URL;
import java.awt.Panel;

/**
 * Manages all of the Resource objects for a specific Agent.<p>
 *
 * <hr>
 * Copyright (c) 1995, H. Robert Frost, Stanford University.
 * All rights reserved.<p>
 * Copyright (c) 1996, H. Robert Frost, Enterprise Integration Technologies,
 * Inc. All rights reserved.<p>
 *
 * RESTRICTED RIGHTS LEGEND: Use, duplication or disclosure by the 
 * Government is subject to restrictions as set forth in 
 * subparagraph(c)(1)(ii) of the Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 and in similar clauses in the
 * FAR and NASA FAR supplement.<p>
 * 
 * This software is bound by the terms and conditions listed in the
 * attached <a href="../LICENSE">LICENSE file</A>.
 * <hr>
 * @author <A HREF="http://cdr.stanford.edu/html/people/frost-bio.html"> Rob Frost</A>
 * @version 0.3 5/21/96 for Java(tm) Language Version 1.0.2
 */

public class ResourceManager {

  /**
   * Agent whose Resources are managed.
   */
  protected Agent parent;

  /**
   * Storage for NetworkClassLoaders used to load remote classes into the
   * current runtime. Type NetworkClassLoader.
   */
  protected Hashtable ClassLoaders;
  
  /**
   * Local storage for agent addresses. Identifiers are Agent names,
   * type String, elements are addresses, type AgentAddress.
   */
  protected Addresses addresses;

  /**
   * Storage for all Classes loaded into the runtime. Identifiers are
   * class names, type String, elements are the classes, type Class.
   */
  protected Classes classes;

  /**
   * Storage for all local Files.
   */
  protected Files files;

  /**
   * Storage for the subclasses of Interpreter which the agent can understand.
   * Identifiers are Interpreter names, type String, elements are instances of
   * Interpreter, type Interpreter.
   */
  protected Interpreters interpreters;

  /**
   * Storage for the subclasses of Language which the agent can understand.
   * Identifiers are language names, type String, elements are subclasses of
   * Language, type Class.
   */
  protected Languages languages;

  /**
   * Initializes the memory and contents of all Agent resources.
   */

  public ResourceManager(Agent parent){
    this.parent = parent;
    addresses = new Addresses(parent);
    classes = new Classes(parent);
    files = new Files(parent);
    interpreters = new Interpreters(parent);
    languages = new Languages(parent);
    ClassLoaders = new Hashtable();
    
    interpreters.addElement("agent", new AgentInterpreter(), true);
    languages.addElement("KQML", new KQMLmessage().getClass(), true);
  }

  /**
   * Sends a system message to the agent.
   * @param message The system message to output.
   */
  
  public void addSystemMessage(String message){
    parent.addSystemMessage(message);
  }

  /**
   * Sends a system message to the agent w/ exception.
   * @param message The system message to output.
   * @param e Exception which generated the message, may be null.
   */
  
  public void addSystemMessage(String message, Exception e){
    parent.addSystemMessage(message,e);
  }

  /**
   * Loads a given resource. If the resource currently exists does not
   * overwrite.
   * @param type Type of resource, either language, Interpreter or class
   * @param name The identifier for the resource.
   * @param object_name The class or file name.
   * @param url The code base for the class.
   */
  
  public void loadResource(String type, String name, String object_name,
			   String url){
    try{
      Resource resource = getResource(type);
      URL code_base = new URL(url);
      URL object_url = new URL(code_base,object_name);
      if(type.equals("file") && parent.localIO()){
	File f = FileIO.remote_copy(object_url,parent.getWorkingDir(),
				    object_name,
				    AgentParams.FILE_EXISTS_ACTION);
	if(f != null){
	  resource.addElement(name,f,true);
	  resource.addLocation(name, new FileLocation(code_base,object_name),
			       true);
	} else {
	 addSystemMessage("Error copying file " +
				  name + " to the local disk.");
	}
      } else {
	Class c = loadClass(code_base, object_name);
	if(type.equals("interpreter")){
	  Interpreter ont = (Interpreter)c.newInstance();
	  resource.addElement(name,ont,false);
	} else {
	  resource.addElement(name,c,false);
	}
      }
      resource.addLocation(name,new FileLocation(code_base,object_name),false);
    } catch (Exception e){
	addSystemMessage("Unable to load resource " + name + " of type " +
			 type + " from " + url + ".", e);
    }
  }

  /**
   * Get the resource object for the specified type.
   * @param type Resource type to get, one of: "address", "language",
   * "interpreter" or "class".
   * @return Resource object or null if type does not exist.
   */

  public Resource getResource(String type){
    Resource resource = null;

    if(type.equals("address")){
      resource = addresses;
    } else if(type.equals("language")){
      resource = languages;
    } else if(type.equals("interpreter")){
      resource = interpreters;
    } else if(type.equals("class")){
      resource = classes;
    } else if(type.equals("file")){
      resource = files;
    } else {
      addSystemMessage("Agent does not have a resource of type " + type);
    }
    
    return resource;
  }

  /**
   * Called to load a remote class into the current runtime environment.
   * First check to see if a loader for the URL already exists, only create
   * a new loader if one doesn't currently exist.
   *
   * Returns null if no code is found.
   * 
   * @param code_url code base URL
   * @param name Class name.
   * @return The loaded Class.
   */

  public Class loadClass(URL code_url, String name){
    Class new_class = null;
    String file = code_url.getFile();
    if(!file.endsWith("/")){
      file = file + "/";
      try {
	code_url = new URL(code_url.getProtocol(),code_url.getHost(),file);
      } catch (Exception e){
	addSystemMessage("Code base URL was incomplete, attempt to add " +
			 "trailing / failed.",  e);
	return new_class;
      }
    }
    try{
      NetworkClassLoader loader = 
	(NetworkClassLoader)ClassLoaders.get(code_url.toString());
      if(loader == null){
	loader = new NetworkClassLoader(code_url,parent);
	ClassLoaders.put(code_url.toString(),loader);
      }
      new_class = loader.loadClass(name);
    } catch (Exception e) {
      addSystemMessage("Unable to load class " + name + " from " +
		       code_url + ".",e);
    }
    return new_class;
  }
 
}

