package FDATI;

import 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 a ClientAgent and
 * the FDAT ServiceAgent.<p>
 *
 * Supports the following message types:<p>
 * <ul>
 * <li><i>(ask-models)</i>: Sent from a ClientAgent to the FDATAgent asking 
 * for the list of models.
 * <li><i>(tell-models :models ([&ltname&gt &ltfile URL&gt]*))</i>: 
 * Sent from the FDATAgent to a ClientAgent.
 * <li><i>(add-models :models ([&ltmodel name&gt &ltfile name&gt]*))
 * </i>: Sent from the init_file to the FDATAgent. 
 * <li><i>(submit-fixels :name &ltfixture plan name&gt :fixels 
 * (&ltdimension&gt &ltdirection &gt [&ltdir-i&gt &ltdir-j&gt 
 * &ltdir-k&gt] [&ltx&gt &lty&gt &ltz&gt &lti&gt &ltj&gt &ltk&gt]*))
 * Sent from a ClientAgent to the FDATAgent.
 * <li><i>(analysis-results :name &ltfixture plan name&gt :results 
 * (&ltstability&gt &ltminimum&gt [&ltredundant&gt]*))</i>: 
 * Sent from FDATAgent to ClientAgent.
 * 
 * <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 FDATInterpreter extends JavaAgent.resource.Interpreter {
  
  /**
   * Handles a received message which references the "FDAT"
   * Interpreter.<p>
   *
   * @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("add-models")){
	  addModelsAction(message,receiver);
	} else if(performative.equals("ask-models")){
	  askModelsAction(message,receiver);
	} else if(performative.equals("tell-models")){
	  tellModelsAction(message,receiver);
	} else if(performative.equals("submit-fixels")){
	  submitAction(message,receiver);
	} else if(performative.equals("analysis-results")){
	  analysisResultsAction(message,receiver);
	}
	
      } else {
	throw new InterpretationException("Interpreter " + 
					  message.getValue("ontology") +
					  " does not support language " +
					  message.getValue("langauge"));
      } 
    }

  /**
   * Received by the FDATAgent, adds a set of models.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */
  
  protected void addModelsAction(KQMLmessage message, Agent parent){
    if(isSuper(parent.getClass(),"FDATI.FDATAgent")){
      String content_str = message.getValue("content");
      KQMLmessage content = new KQMLmessage(content_str);
      String models = content.getValue("models");
      models = models.substring(1,models.lastIndexOf(")"));
      StringTokenizer st = new StringTokenizer(models);
      while(st.hasMoreTokens()){
	String name = st.nextToken();
	String file_name = st.nextToken();
	parent.addSystemMessage("adding a model: " + name);
	((FDATAgent)parent).getResource("model").addElement(name,file_name,
							    true);
      }
    } 
  }
  
  /**
   * Returns a list of example models.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */
  
  protected void askModelsAction(KQMLmessage message, Agent parent){    
    String msg_str = "(evaluate :sender " + parent.getName() + " :receiver " +
      message.getValue("sender") + " :language KQML :ontology FDAT)";
    KQMLmessage reply = new KQMLmessage(msg_str);
    KQMLmessage content = new KQMLmessage("(tell-models)");

    String model_listing = "(";
    if(isSuper(parent.getClass(),"FDATI.FDATAgent")){
      try{
	Models models = (Models)((FDATAgent)parent).getResource("model");
        Enumeration model_names = models.getElements();
	if(!model_names.hasMoreElements()){
	  model_listing = model_listing + "none";
	} else {
	  while(model_names.hasMoreElements()){
	    String name = (String)model_names.nextElement();
	    model_listing = 
	      model_listing + " " + name + " " +
			    parent.getSharedClassesURL().toString() + "files" + 
			    File.separator + 
			      (String)models.getElement(name, 
						Resource.NO_RETRIEVAL);
	  }
	}
      } catch (ResourceException e){
	parent.addSystemMessage("Problem getting model information.",e);
      	model_listing = model_listing + "none";
      }
    } else {
      model_listing = model_listing + "?";
    }
    model_listing = model_listing + ")";
    content.addFieldValuePair("models", model_listing);
    reply.addFieldValuePair("content",content.getSendString());
    parent.sendMessage(reply);
  }

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

  protected void tellModelsAction(KQMLmessage message, Agent parent){
    Hashtable Models = new Hashtable();
    String content_str = message.getValue("content");
    KQMLmessage content = new KQMLmessage(content_str);
    String models = 
      content.getValue("models").substring(1,content.getValue("models").lastIndexOf(")"));

    if(!models.equals("?") && !models.equals("none")){
      StringTokenizer st = new StringTokenizer(models);
      while(st.hasMoreTokens()){
	String name = st.nextToken();
	String file_url = st.nextToken();
	if(parent.localIO()){
	  try{
	    File model_file = 
	      FileIO.remote_copy(new URL(file_url),parent.getWorkingDir(),
				 name, AgentParams.OVERWRITE);
	    Models.put(name,model_file);
	  } catch (IOException e){
	    parent.addSystemMessage("Failed to read-in model " + name + 
				    ".", e);	    
	  }
	}
      }
    } else {
      if(models.equals("?")){
	parent.addSystemMessage("Sender does not know model set!");
      } else if(models.equals("none")){
	parent.addSystemMessage("Model set is empty!");
      }
    }
    
    /* Received by a ClientAgent */
    if(isSuper(parent.getClass(),"RemoteService.agent.ClientAgent")){
      if(((ClientAgent)parent).getResource("service").hasElement("FDAT")){
	try{
	  FDATService service = 
	    (FDATService)((ClientAgent)parent).getResource("service").getElement("FDAT", Resource.NO_RETRIEVAL);
	  service.received_models(Models);
	} catch (ResourceException e){
	  parent.addSystemMessage("Unable to add models to service.",e);
	}
      } else {
	parent.addSystemMessage("Unable to process message, FDATService " +
				"not loaded!");
      }
    }
  }
 

  /**
   * A ClientAgent is submitting a set of fixels to the FDATAgent for
   * analysis.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  protected void submitAction(KQMLmessage message, Agent parent){
    File local_file = null;

    String content_str = message.getValue("content");
    KQMLmessage content = new KQMLmessage(content_str);
    String plan_name = content.getValue("name");
    String fixels = content.getValue("fixels");

    if(isSuper(parent.getClass(),"FDATI.FDATAgent")){ 
      ((FDATAgent)parent).submitFixels(message.getValue("sender"),
				       plan_name,fixels);
    }
  }

  /**
   * Received by a ClientAgent after analysis results have been 
   * obtained by the FDATAgent.
   * @param message Original KQMLmessage received.
   * @param parent Agent which received the message.
   */

  void analysisResultsAction(KQMLmessage message, Agent parent){
    String content_str = message.getValue("content");
    KQMLmessage content = new KQMLmessage(content_str);
    String plan_name = content.getValue("name");
    String results = content.getValue("results");
    if(isSuper(parent.getClass(),"RemoteService.agent.ClientAgent")){
      if(((ClientAgent)parent).getResource("service").hasElement("FDAT")){
	try{
	  StringTokenizer st = 
	    new StringTokenizer(results.substring(1,results.lastIndexOf(")")));
	  boolean stable = ((new Integer(st.nextToken())).intValue() > 0);
	  int minimum = (new Integer(st.nextToken())).intValue();
	  Vector redundant = new Vector();
	  while(st.hasMoreTokens()){
	    redundant.addElement(new Integer(st.nextToken()));
	  }
	  FDATService service = 
	    (FDATService)((ClientAgent)parent).getResource("service").getElement("FDAT", Resource.NO_RETRIEVAL);
	  service.submissionResults(plan_name,stable,minimum,redundant);
	} catch (ResourceException e){
	  parent.addSystemMessage("Unable to pass results to service.",e);
	}
      } else {
	parent.addSystemMessage("Unable to process message, FDATService " +
				"not loaded!");
      }
    }
  }
}
