package JavaAgent.context;

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

/**
 * Abstract class which implements the ContextInterface and provides 
 * an execution environment for an Agent. 
 * This class also creates a CommInterface and optionally
 * an AgentFrame GUI. 
 * Execution can take place as either a standalone process or via
 * instantiation within an existing Java VM (e.g 
 * Applet execution). For standalone or applet execution, either a main()
 * method or the applet init() method must instantiate and initialize the
 * context object. Execution parameters which are bound at runtime are 
 * contained in a RuntimeParams object. Internal system messages are funneled
 * back to the context via the addSystemMessage() method, if the message
 * begins with the string "System stat:" then the message is being used to
 * report some period system performance statistic and thus will not be added
 * to the log file.<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 abstract class AgentContext implements ContextInterface {

  /**
   * Object which contains all parameters set at execution-time as either
   * commandline args or applet parameters.
   */
  protected RuntimeParams params;

  /**
   * Agent which this context represents.
   */
  protected Agent A;
  
  /**
   * Object which provides messaging functionality for the Agent.
   */
  protected CommInterface comm;

  /**
   * Agent GUI.
   */
  protected AgentFrame frame;

  /**
   * Name of the local log file for recording system messages.
   */
  protected String log_file = null;
  
  /**
   * Is the CommInterface ready to send messages?
   */
  protected boolean comm_ready = false;

  /**
   * Constructor for AgentContext. Sets the parameters and creates the
   * CommInterface, represented Agent and possibly the AgentFrame.
   * @param params Runtime parameters for the AgentContext.
   */
  
  public AgentContext(RuntimeParams params){
    this.params = params;
    createGUI();
    if(addAgent()){
      createCommInterface();
      A.sendInitMessages();
    } else {
      System.out.println(((params.name != null)?params.name:"Agent")
			 + " not added successfully");
    }
  }

  /**
   * If the gui flag is set, creates an instance of AgentFrame to 
   * provide a interface to the user.
   */

  protected void createGUI(){
    if(params.gui){
      frame = new AgentFrame(this,params.name);
      frame.resize(ContextParams.AF_WIDTH, ContextParams.AF_HEIGHT);
      frame.show();
    } 
  }
  
  /**
   * Creates the Agent object. Must be overridden.
   * @return Success of creation.
   */  
  
  protected boolean addAgent(){
    System.out.println("In AgentContext's addAgent()");
    return false;
  }

  /**
   * Creates a CommInterface for the Agent. Must be overridden.
   */

  abstract protected void createCommInterface();

  /**
   * @return Agent contained by context.
   */

  protected Agent getAgent(){
    return A;
  }

  /**
   * @return Is the CommInterface functioning?
   */

  public boolean commReady(){
    return comm_ready;
  }

  /**
   * Generic method for sending an Object message to some context-dependent
   * non-agent destination (e.g. a legacy software application, database,
   * etc.) Where this message is sent and how it is 
   * interpreted depends on the specific ContextInterface implementation.
   * If no destination exists or some other problem occurs,
   * a NonAgentMsgException will be thrown. Must be overridden.
   * @message Object representing the message.
   */

  public void NonAgentMsg(Object message) throws NonAgentMsgException{
    throw new NonAgentMsgException("NonAgent messages not supported, " +
				   message.toString() + " not sent.");
  }

  /**
   * @return Does this context support a GUI?
   */

  public boolean hasGUI(){
    return params.gui;
  }

  /**
   * Called to begin the termination process. Kills the gui then terminates the
   * Agent.
   */

  public void initiateTermination(){
    if(params.gui){
      frame.terminate();
    }
    if(comm_ready){ /* send out remove-address message */
      A.initiateTermination();
    } else { /* haven't registered an address so just quit */
      terminate();
    }
  }

  /**
   * Called by the Agent to terminate the AgentContext. This method is
   * invoked after the agent has sent out its remove-address message.
   * If it is a stand alone, calls the System.exit method, otherwise just
   * disposes of the AgentFrame.
   */

  public void terminate(){
    if(!params.applet){
      System.exit(0);
    }     
  }
  
  /**
   * Called when the Agent has changed its name.
   * If stand alone, change the frame title.
   * @param name The new name for the Agent.
   */
  
  public void setName(String name){
    if(!params.applet){
      params.name = name;
      frame.setTitle(name);
    }
  }

  /**
   * Called when some Agent resource has changed.
   * @param type String identifying the type of the resource.
   */

  public void resourceChanged(String type){
    if(params.gui){
      frame.resourceChanged(type);
    }
  }
 
  /**
   * Takes the array of command line arguments and creates a Hashtable of
   * flag/token pairs.
   * @param arg Command line arguments
   * @return Hashtable of flag/token pairs.
   */

  static protected Hashtable parseArgs(String[] arg){
    Hashtable args = new Hashtable();
    for(int i = 0;i<arg.length;i++){
      while(!arg[i].startsWith("-") && i < arg.length){
	i++;
      }
      if(arg[i].equals("-gui")){
	args.put(arg[i],"true");
      } else if(i + 1 < arg.length){
	args.put(arg[i++],arg[i]);
      }
    }  
    return args;
  }
  
  /**
   * Adds a system message w/ optional exception.
   * @param message The message to output.
   * @param e Exception which generated the message, may be null.
   */

  public void addSystemMessage(String message, Exception e){
    if(ContextParams.STACK_TRACE && e != null){
      ByteArrayOutputStream out = new ByteArrayOutputStream(100);
      e.printStackTrace(new PrintStream(out));
      message = message +  "\n" + out.toString();
    } else {
      message = message + " " + e.toString();
    }
    addSystemMessage(message);
  }
    
  /**
   * Adds a system message.
   * If the Agent can do file IO, appends the message to a log file whose
   * name is based on the Agent's name and creation time stamp.
   * @param message The message to output.
   */

  public void addSystemMessage(String message){
    if(A != null && !message.startsWith("System stat:")){
      if(ContextParams.LOG_FILE){
	if(log_file == null && A.localIO()){
	  String date = (new Date()).toLocaleString();
	  date = date.replace(' ','_');
	  date = date.replace(':','_');
	  log_file = params.name + "_" + date;
	}
	if(A.localIO()){
	  String temp_msg = message.concat("\n");
	  byte[] data = new byte[temp_msg.length()];
	  temp_msg.getBytes(0,temp_msg.length(),data,0);
	  try{
	    FileIO.write_to_file(data,A.getWorkingDir(),log_file,true);
	  } catch (IOException e){
	    message = message.concat("\nError writing to log file, " +
				     e.toString());
	  }
	}
      }
    }
    if(params.gui){
      frame.addSystemMessage(message);
    }
  }
  
  /**
   * Takes a panel created by the Agent and passed through the MessageOutput
   * object and displays it in a new top-level window.
   */
  
  public void addPanel(Panel p){
    if(params.gui){
      frame.addPanel(p);
    }
  }

}



















