package RemoteService.resource;

import RemoteService.agent.*;
import JavaAgent.resource.*;
import JavaAgent.agent.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;

/**
 * Subclass of Interpreter used for communication between ClientAgents,
 * ServiceAgents and ServiceBrokers. Facilitates the exchange of subclasses
 * of Service between ServiceAgents and ClientAgents, and the registration
 * of Services with the ServiceBroker.<p>
 *
 * Supports the following message types:<p>
 * <ul>
 * <li><i>(ask-available)</i>: Sent from a ClientAgent to the ServiceBroker 
 * asking for a list of currently registered services.
 * <li><i>(tell-available :available &ltservice name&gt 
 * &ltservice agent name&gt [&ltservice name&gt &ltservice agent name&gt 
 * &ltURL for descrip file&gt &ltURL for gif file&gt]*)
 * </i>: Sent from the ServiceBroker to the ClientAgent.
 * <li><i>(register-service :name &ltservice name&gt :descrip 
 * &ltURL for descrip file&gt :gif &ltURL for gif file&gt)</i>: Sent from a 
 * ServiceAgent to the ServiceBroker.
 * <li><i>(unregister-service :name &ltservice name&gt)</i>: Sent from a 
 * ServiceAgent to the 
 * ServiceBroker and ClientAgents and from the ServiceBroker to ClientAgents.
 * <li><i>(request-service :name &ltservice name&gt)</i>: Sent from a 
 * ClientAgent to a ServiceAgent.
 * <li><i>(tell-service :name &ltservice name&gt :location &ltclass name&gt
 * &ltclass URL&gt)</i>: Sent from a 
 * ServiceAgent to a ClientAgent, provides the
 * class name and URL for a subclass of Service.
 * <li><i>(add-service :name &ltservice name&gt :class &ltclass name&gt)</i>: 
 * Sent from the init_file to a ServiceAgent. It is assumed that the 
 * class file, description file and gif file are all in shared classes and
 * have the names &ltservice name&gt.descrip and &ltservice name&gt.gif.
 * <li><i>(update-service :name &ltservice name&gt :location &ltclass name&gt
 * &ltURL&gt)</i>: Sent from a ServiceAgent to a ClientAgent when the 
 * implementation of a service has changed. Not currently used.
 * </ul>
 *
 * <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 ServiceInterpreter extends Interpreter {
  
  /**
   * Handles a received message which references the "service"
   * Interpreter.
   *
   * @param message Message which needs to be interpreted.
   * @param receiver Agent who received the message.
   * @param language Instance of Language which represents the message
   * contents.
   */
  
  
  protected void interpretLanguage(KQMLmessage message, Agent receiver, 
			 Language language) throws InterpretationException{
      
      if(message.getValue("language").equals("KQML")){
	KQMLmessage content = (KQMLmessage)language;
	String performative = content.getValue("performative");
	
	if(performative.equals("ask-available")){
	  askAvailableAction(message,receiver);
	} else if(performative.equals("tell-available")){
	  tellAvailableAction(message,receiver);
	} else if(performative.equals("register-service")){
	  registerServiceAction(message,receiver);
	} else if(performative.equals("unregister-service")){ 
	  unregisterServiceAction(message,receiver);
	} else if(performative.equals("request-service")){ 
	  requestServiceAction(message,receiver);
	} else if(performative.equals("tell-service")){ 
	  tellServiceAction(message,receiver);
	} else if(performative.equals("add-service")){ 
	  addServiceAction(message,receiver);
	}
      } else {
	throw new InterpretationException("Interpreter " + 
					  message.getValue("ontology") +
					  " does not support language " +
					  message.getValue("langauge"));
      } 	
    }
  
  /**
   * Adds the sender to the clients buffer and returns the list of currently
   * registered Services and the ServiceAgents which represent them.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */
  
  void askAvailableAction(KQMLmessage message, Agent parent){ 
    ((ServiceBroker)parent).addClient(message.getValue("sender"));
    Enumeration avail_services = 
      ((ServiceBroker)parent).getResource("available_service").getElements();
    sendAvailableMsg(message.getValue("sender"), parent, 
		     avail_services);
  }
  
 /**
   * Returns the list of currently registered Services and the ServiceAgents
   * which represent them.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   * @param e Enumeration of AvailableService objects.
   */
  
  void sendAvailableMsg(String receiver, Agent parent, Enumeration e){   
    String msg_str = "(evaluate :sender " + parent.getName() + " :receiver " +
      receiver + " :language KQML :ontology service)";
    KQMLmessage reply = new KQMLmessage(msg_str);
    KQMLmessage content = new KQMLmessage("(tell-available)");
    String services = "\"";
    /* send a list of all currently registered Services, and the
       ServiceAgents which represent them, to the sender */
    if(isSuper(parent.getClass(),"RemoteService.agent.ServiceBroker")){
      if(!e.hasMoreElements()){
	services = services.concat("none");
      } else {
	String file_url = parent.getSharedClassesURL().toString();
	while(e.hasMoreElements()){
	  String service_name = (String)e.nextElement();
	  try{
	    AvailableService service = (AvailableService)parent.getResource("available_service").getElement(service_name,Resource.NO_RETRIEVAL);
	    String path = file_url + File.separator + "files" + 
	      File.separator;
	    String descrip = path + service.descrip.getName();
	    String gif = path + service.gif.getName();
	    services = 
	      services.concat(service.name + " " + service.agent + " " + 
			      descrip + " " + gif + " ");
	  } catch (ResourceException ex){
	    parent.addSystemMessage("Failed to tell available services.", ex);
	  }
	}
      }
      services = services.concat("\"");
    } else {
      services = "?";
    }
    content.addFieldValuePair("available", services);
    reply.addFieldValuePair("content",content.getSendString());
    parent.sendMessage(reply);
  }

  /**
   * Received by a ClientAgent, contains a listing of all of the currently
   * registered Services.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void tellAvailableAction(KQMLmessage message, Agent parent){
    if(isSuper(parent.getClass(),"RemoteService.agent.ClientAgent")){
      String content_str = message.getValue("content");
      KQMLmessage content = new KQMLmessage(content_str);
      String available = content.getValue("available");
      if(available.startsWith("\"")){
	available = available.substring(1, available.lastIndexOf("\""));
      }
      if(available.equals("?")){
	parent.addSystemMessage("ask-available message sent to wrong Agent");
      } else {
	if(available.equals("none")){
	  parent.addSystemMessage("No registered servcies.");
	} else {
	  StringTokenizer st = new StringTokenizer(available);
	  while(st.hasMoreTokens()){
    AvailableService service = new AvailableService(st.nextToken());
	    service.agent = st.nextToken();
	    if(parent.localIO()){
	      try{
		String descrip = st.nextToken();
		String gif = st.nextToken();
		String descrip_file_name = 
		  descrip.substring(descrip.lastIndexOf("/")+ 1);
		String gif_file_name = 
		  gif.substring(gif.lastIndexOf("/")+ 1);	
		URL descrip_url = new URL(descrip);
		URL gif_url = new URL(gif);	
		service.descrip = 
		  FileIO.remote_copy(descrip_url,
				     parent.getWorkingDir(),
				     descrip_file_name,
				     RSAgentParams.FILE_EXISTS_ACTION);

		service.gif = 
		  FileIO.remote_copy(gif_url,
				     parent.getWorkingDir(),
				     gif_file_name,
				     RSAgentParams.FILE_EXISTS_ACTION);
		parent.getResource("available_service").addElement(service.name, service, true);
	      } catch (Exception e){
		parent.addSystemMessage("Unable to get descrip and gif " +
					"files for service " + service.name + 
					".", e);
	      }
	    }
	  }
	}
      }
    }
  }

  /**
   * Sent to a ServiceBroker to register a Service.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void registerServiceAction(KQMLmessage message, Agent parent){
    if(isSuper(parent.getClass(),"RemoteService.agent.ServiceBroker")){
      String content_str = message.getValue("content");
      KQMLmessage content = new KQMLmessage(content_str);
      String descrip = content.getValue("descrip");
      String gif = content.getValue("gif");
      AvailableService service = 
	new AvailableService(content.getValue("name"));
      try{
	if(parent.localIO()){
	  service.agent = message.getValue("sender");
	  URL gif_url = new URL(gif);
	  URL descrip_url = new URL(descrip);
	  File local_path = new File(parent.getSharedDir(), 
				   "files" + File.separator);
	  /* Copy the files */
	  String descrip_file_name = 
	    descrip.substring(descrip.lastIndexOf("/")+ 1);
	  String gif_file_name = 
	    gif.substring(gif.lastIndexOf("/")+ 1);
	  service.descrip = FileIO.remote_copy(descrip_url,local_path,
			 descrip_file_name,
			 RSAgentParams.FILE_EXISTS_ACTION);
	  service.gif = FileIO.remote_copy(gif_url,local_path,
			 gif_file_name,
			 RSAgentParams.FILE_EXISTS_ACTION);
	  
	  /* Add to the AvailableServices Resource */
	  parent.getResource("available_service").addElement(service.name,
							     service,true);
	} else {
	 parent.addSystemMessage("Agent unable to perform local IO");
       }
      } catch(Exception e){
	parent.addSystemMessage("Problem getting descrip and gif files.",e);
      }
      
      /* Send out tell-available messages */
      Enumeration e = ((ServiceBroker)parent).getClients();
      Vector v = new Vector();
      v.addElement(service.name);
      while(e.hasMoreElements()){
	sendAvailableMsg((String)e.nextElement(), parent, v.elements());
      }
    }
  }

  /**
   * Indicates that the specified Service has been unregistered. Received by
   * either a ClientAgent or the ServiceBroker.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void unregisterServiceAction(KQMLmessage message, Agent parent){
    String content_str = message.getValue("content");
    KQMLmessage content = new KQMLmessage(content_str);
    String name = content.getValue("name");
    try{
      AvailableService service =
	(AvailableService)parent.getResource("available_service").getElement(name,Resource.NO_RETRIEVAL);
      if(service != null){
	if(isSuper(parent.getClass(),"RemoteService.agent.ServiceBroker")){
	  ((ServiceBroker)parent).SendUnregisterMessage(name,service.agent);
	  parent.getResource("available_service").removeElement(name);
	} else if(isSuper(parent.getClass(),
			  "RemoteService.agent.ClientAgent")){
	  parent.getResource("available_service").removeElement(name);
	  Services services = (Services)parent.getResource("service");
	  if(services.hasElement(name)){
	    Service s = 
	      (Service)services.getElement(name,Resource.NO_RETRIEVAL);
	    s.stop();
	    services.removeElement(s);
	  }
	}
      } else {
	parent.addSystemMessage("Unregistered service not present");
      }
    } catch (ResourceException e){
      parent.addSystemMessage("Unable to unregister service.",e);
    }
  }

  /**
   * Received by a ServiceAgent.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void requestServiceAction(KQMLmessage message, Agent parent){
    String msg_str = "(evaluate :sender " + parent.getName() + " :receiver " +
      message.getValue("sender") + " :language KQML :ontology service)";
    String content_str = message.getValue("content");
    KQMLmessage content = new KQMLmessage(content_str);
    KQMLmessage reply = new KQMLmessage(msg_str);
    String reply_content_str = "(tell-service :name " + 
      content.getValue("name") + ")";
    KQMLmessage reply_content = new KQMLmessage(reply_content_str);
    
    if(isSuper(parent.getClass(),"RemoteService.agent.ServiceAgent")){
      String service_name = content.getValue("name");
      try{
	AvailableService service = 
	  (AvailableService)((ServiceAgent)parent).getResource("available_service").getElement(service_name,Resource.NO_RETRIEVAL);
	if(service != null){
	  reply_content.addFieldValuePair("location", "\"" + 
					  service.class_name + " " + parent.getSharedClassesURL().toString() + 
					  "classes" + File.separator + "\"");
	}
      } catch (ResourceException e){
	reply_content.addFieldValuePair("location","?");
      }
    } else {
      reply_content.addFieldValuePair("location","?");
    }  
    reply.addFieldValuePair("content",reply_content.getSendString());
    parent.sendMessage(reply);    
  }

  /**
   * Received by a ClientAgent, provides the location of the class file for
   * a specific Service. The ClientAgent will load the class, instantiate it
   * and run it.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void tellServiceAction(KQMLmessage message, Agent parent){
   if(isSuper(parent.getClass(),"RemoteService.agent.ClientAgent")){
      String content_str = message.getValue("content");
      KQMLmessage content = new KQMLmessage(content_str);
      String name = content.getValue("name");
      String location = content.getValue("location");
      parent.addSystemMessage("Loading service " + name + ", at location " +
			      location);
      if(location.equals("?")){
	parent.addSystemMessage("Service " + name + " not found.");
      } else {
	try{
	  String class_name = location.substring(1,location.indexOf(" "));
	  String url = location.substring(location.indexOf(" ")+1, location.lastIndexOf("\""));
	  URL code_base = new URL(url);
	  FileLocation cl = new FileLocation(code_base, class_name);
	  Class c = parent.loadClass(code_base, class_name);
	  if(c != null){
	    Service s = (Service)c.newInstance();
	    s.setParams(name,(ClientAgent)parent);
	    ((ClientAgent)parent).getResource("service").addElement(name,s,
								    true);
	  }else {
	    parent.addSystemMessage("Loaded class was null, class = " +
				    class_name + ", url = " +
				    url);
	  }
	} catch (Exception e){
	  parent.addSystemMessage("Failed to load Service " + name +
				  ".", e);
	}
      }
    }
 }

  /**
   * Received by a ServiceAgent, adds a new service.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */
  
  void addServiceAction(KQMLmessage message, Agent parent){

    if(isSuper(parent.getClass(),"RemoteService.agent.ServiceAgent")){
      String content_str = message.getValue("content");
      KQMLmessage content = new KQMLmessage(content_str);
      AvailableService service = 
	new AvailableService(content.getValue("name"));
      service.class_name = content.getValue("class");
      service.gif = new File(parent.getSharedDir(), "files" +
			     File.separator + service.name + ".gif");
      service.descrip = new File(parent.getSharedDir(), "files" +
			     File.separator + service.name + ".descrip");
      AvailableServices as = 
	(AvailableServices)parent.getResource("available_service");
      as.addElement(service.name,service,true);
      /* register the service with the ANS(Service Broker) */
      String msg_str = "(evaluate :sender " + parent.getName() + 
	" :receiver ANS :language KQML :ontology service)";
      KQMLmessage reply = new KQMLmessage(msg_str);
      String files_url = parent.getSharedClassesURL().toString() + "files" +
	File.separator;
      reply.addFieldValuePair("content","(register-service :name " + 
			      service.name + " :descrip " + files_url + 
			      service.name + ".descrip :gif " + files_url +
			      service.name + ".gif)");
      parent.sendMessage(reply);
    } 
  }
    
}




