package JavaAgent.resource;

import JavaAgent.agent.*;

import java.util.Hashtable;

/**
 * Subclass of Resource which the Agent will attempt to retrieve from other
 * Agents when that resource is not available. Example of this type of
 * Resource are: addresses, languages and interpreters.
 *
 * <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 RetrievalResource extends Resource {

  /**
   * Block retrieval.
   */
  public static final int BLOCK = 1;

  /**
   * Send out message then block.
   */
  public static final int SEND_MSG = 2;

  /**
   * Constructor for the resource.
   *
   * @param agent The Agent which owns the resource.
   */

  public RetrievalResource(Agent agent){
   super(agent);
  }

  /**
   * Called when there is an attempt to retrieve an element
   * associated with a non-existent identifier. If an attempt to 
   * retrieve the given identifier should be taken (determined by the 
   * abstract retrieve() method, then the action taken depends on
   * the specified retrieve_option:
   * <ul>
   * <li> BLOCK: block until either the element is inserted into the 
   * Resource or RETRIEVAL_WAIT msec have expired.
   * <li> SEND_MSG: send a message with receiver and content
   * fields defined by abstract methods then BLOCK.
   * </UL> 
   * Other options can be specified by subclasses of RetrievalResource.
   * If the element is null, the Agent was unable to retrieve the element 
   * and the null object is removed and a ResourceException is thrown.<p>
   *
   * @param identifier Object identifier for the element.
   * @param retrieve_option Specifies action to take if the element is
   * not currently present.
   * @param receiver Agent to whom retrieve requests should be sent, null
   * if default should be used.
   * @return Object to be returned.
   */

  public Object getMissingAction(Object identifier, int retrieve_option,
				 String receiver)
    throws ResourceException {
    
      Object answer = null;
      
      switch(retrieve_option){
      case BLOCK:
	answer = block(identifier);
	break;
      case SEND_MSG:
	send_message(identifier,receiver);
	answer = block(identifier);
	break;
      }
      return answer;
    }

  /**
   * Block until the element is put in the Resource. Each wait is timed and
   * after a set number of notify calls the retrieval attempt is halted and
   * a exception is thrown.
   * @param identifier Identifier for the element.
   */

  protected Object block(Object identifier) throws ResourceException {
    Object answer = null;
    int index = 0;

    while(!storage.containsKey(identifier) && 
	  index < AgentParams.RETRIEVAL_NOTIFIES){
      try{
	wait(AgentParams.RETRIEVAL_WAIT);
	index++;
      } catch (Exception e){
	parent.addSystemMessage("Problem w/ RetrievalResource wait().",e);
      }
    }

    if(storage.containsKey(identifier)){
      answer = getElement(identifier, Resource.NO_RETRIEVAL);
    } else {
      throw new ResourceException("Retrieval timed-out");
    }

    if(answer.getClass().getName().equals("Agent.NullResource")){
      removeElement(identifier);
      throw new ResourceException("Retrieval failed.");
    }
    
    return answer;
  }

  /**
   * Send out a retrieval message.
   * @param identifier Object identifier for the element.
   * @param receiver Agent to whom retrieve requests should be sent, null
   * if default should be used.
   */

  protected void send_message(Object identifier, String receiver){
    
    String msg_str = "(evaluate :sender " + parent.getName() +
      " :language KQML " + ":ontology agent)";
    KQMLmessage msg = new KQMLmessage(msg_str);
	
    String content_str = "(ask-resource :name " + (String)identifier + ")";
    KQMLmessage content = new KQMLmessage(content_str);
    
    if(receiver == null){
      msg.addFieldValuePair("receiver",getReceiver(identifier));
    } else {
      msg.addFieldValuePair("receiver",receiver);
    }
    content.addFieldValuePair("type",getType());
    msg.addFieldValuePair("content", content.getSendString());
    
    parent.sendMessage(msg);  
  }
  
  abstract protected String getReceiver(Object identifier);
  abstract protected String getType();

}


  
