/*****************************************************************
JADE - Java Agent DEvelopment Framework is a framework to develop 
multi-agent systems in compliance with the FIPA specifications.
Copyright (C) 2000 CSELT S.p.A. 

GNU Lesser General Public License

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, 
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA  02111-1307, USA.
*****************************************************************/

package jade.core.behaviours;

//#APIDOC_EXCLUDE_FILE

import jade.util.leap.Iterator;
import jade.util.leap.Serializable;

import jade.core.Agent;

import jade.core.AID;

import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

/**
   Behaviour for receiving an ACL message. This class encapsulates a
   <code>receive()</code> as an atomic operation. This behaviour
   terminates when an ACL message is received. 
   The method <code>getMessage()</code> allows to get the received message.
   @see jade.core.behaviours.SenderBehaviour
   @see jade.core.Agent#receive()
   @see jade.lang.acl.ACLMessage
 
   
   @author Giovanni Rimassa - Universita' di Parma
   @version $Date: 2003-11-25 09:24:45 +0100 (mar, 25 nov 2003) $ $Revision: 4601 $

 */
public final class ReceiverBehaviour extends Behaviour {

  /**
   Exception class for timeouts. This exception is thrown when trying
   to obtain an <code>ACLMessage</code> object from an
   <code>Handle</code>, but no message was received within a specified
   timeout.
   @see jade.core.behaviours.ReceiverBehaviour.Handle#getMessage()
  */
  public static class TimedOut extends Exception {
    TimedOut() {
      super("No message was received before time limit.");
    }
  }

  /**
   Exception class for timeouts. This exception is thrown when trying
   to obtain an <code>ACLMessage</code> from an <code>Handle</code>
   and no message was received so far, but the time limit is not yet
   reached.
   @see jade.core.behaviours.ReceiverBehaviour.Handle#getMessage()
  */
  public static class NotYetReady extends Exception {
    NotYetReady() {
      super("Requested message is not ready yet.");
    }
  }

  /**
   An interface representing ACL messages due to arrive within a time
   limit. This interface is used to create a
   <code>ReceiverBehaviour</code> object to receive an ACL message
   within a user specified time limit. When the user tries to read the
   message represented by the handle, either gets it or gets an
   exception.
   @see jade.core.behaviours.ReceiverBehaviour#newHandle()
   @see jade.core.behaviours.ReceiverBehaviour#ReceiverBehaviour(Agent
   a, ReceiverBehaviour.Handle h, long millis)
   */
  public static interface Handle {

    /**
     Tries to retrieve the <code>ACLMessage</code> object represented
     by this handle.
     @return The ACL message, received by the associated
     <code>ReceiverBehaviour</code>, if any.
     @exception TimedOut If the associated
     <code>ReceiverBehaviour</code> did not receive a suitable ACL
     message within the time limit.
     @exception NotYetReady If the associated
     <code>ReceiverBehaviour</code> is still waiting for a suitable
     ACL message to arrive.
     @see jade.core.behaviours.ReceiverBehaviour#ReceiverBehaviour(Agent
     a, ReceiverBehaviour.Handle h, long millis)
    */
    ACLMessage getMessage() throws TimedOut, NotYetReady;

  }

  private static class MessageFuture implements Handle, Serializable {

    private static final int OK = 0;
    private static final int NOT_YET = 1;
    private static final int TIMED_OUT = 2;

    private int state = NOT_YET;
    private ACLMessage message;

    public void reset() {
      message = null;
      state = NOT_YET;
    }

    public void setMessage(ACLMessage msg) {
      message = msg;
      if(message != null)
	state = OK;
      else
	state = TIMED_OUT;
    }

    public ACLMessage getMessage() throws TimedOut, NotYetReady {
      switch(state) {
      case NOT_YET:
	throw new NotYetReady();
      case TIMED_OUT:
	throw new TimedOut();
      default:
	return message;
      }
    }
  }

  /**
   Factory method for message handles. This method returns a new
   <code>Handle</code> object, which can be used to retrieve an ACL
   message out of a <code>ReceiverBehaviour</code> object.
   @return A new <code>Handle</code> object.
   @see jade.core.behaviours.ReceiverBehaviour.Handle
  */
  public static Handle newHandle() {
    return new MessageFuture();
  }


  // The pattern to match incoming messages against
  /**
  @serial
  */
  private MessageTemplate template;

  // A future for the ACL message, used when a timeout was specified
  /**
  @serial
  */
  private MessageFuture future;

  // A time out value, when present
  /**
  @serial
  */
  private long timeOut;

  // A running counter for calling block(millis) until 'timeOut' milliseconds pass.
  /**
  @serial
  */
  private long timeToWait;

  // Timestamp holder, used when calling block(millis) many times.
  /**
  @serial
  */
  private long blockingTime = 0;
  /**
  @serial
  */
  private boolean finished;

  /**
     This constructor creates a
     <code>ReceiverBehaviour</code> object that ends as soon as an ACL
     message matching a given <code>MessageTemplate</code> arrives or
     the passed <code>millis<code> timeout expires.
     The received message can then be got via the method 
     <code>getMessage</code>.
     @param a The agent this behaviour belongs to, and that will
     <code>receive()</code> the message.
     @param millis The timeout expressed in milliseconds, an infinite timeout
     can be expressed by a value < 0.
     @param mt A Message template to match incoming messages against, null to
     indicate no template and receive any message that arrives.
  */
  public ReceiverBehaviour(Agent a, long millis, MessageTemplate mt) {
      this(a, newHandle(), millis, mt);
  }



  /**
     Receive any ACL message, waiting at most <code>millis</code>
     milliseconds (infinite time if <code>millis < 1</code>).
     When calling this constructor, a suitable <code>Handle</code>
     must be created and passed to it. When this behaviour ends, some
     other behaviour will try to get the ACL message out of the
     handle, and an exception will be thrown in case of a time out.
     The following example code explains this:

     <code><pre>
       // ReceiverBehaviour creation, e.g. in agent setup() method
       h = ReceiverBehaviour.newHandle(); // h is an agent instance variable
       addBehaviour(new ReceiverBehaviour(this, h, 10000); // Wait 10 seconds

       ...

       // Some other behaviour, later, tries to read the ACL message
       // in its action() method
       try {
         ACLMessage msg = h.getMessage();
	 // OK. Message received within timeout.
       }
       catch(ReceiverBehaviour.TimedOut rbte) {
         // Receive timed out
       }
       catch(ReceiverBehaviour.NotYetReady rbnyr) {
         // Message not yet ready, but timeout still active
       }
     </pre></code>
     @param a The agent this behaviour belongs to.
     @param h An <em>Handle</em> representing the message to receive.
     @param millis The maximum amount of time to wait for the message,
     in milliseconds.
     @see jade.core.behaviours.ReceiverBehaviour.Handle
     @see jade.core.behaviours.ReceiverBehaviour#newHandle()
   */
  public ReceiverBehaviour(Agent a, Handle h, long millis) {
    this(a, h, millis, null);
  }

  /**
     Receive any ACL message matching the given template, witing at
     most <code>millis</code> milliseconds (infinite time if
     <code>millis < 1</code>. When calling this constructor, a
     suitable <code>Handle</code> must be created and passed to it.
     @param a The agent this behaviour belongs to.
     @param h An <em>Handle</em> representing the message to receive.
     @param millis The maximum amount of time to wait for the message,
     in milliseconds.
     @param mt A Message template to match incoming messages against, null to
     indicate no template and receive any message that arrives.
     @see jade.core.behaviours.ReceiverBehaviour#ReceiverBehaviour(Agent a, Handle h, long millis)
  */
  public ReceiverBehaviour(Agent a, Handle h, long millis, MessageTemplate mt) {
    super(a);
    future = (MessageFuture)h;
    timeOut = millis;
    timeToWait = timeOut;
    template = mt;
  }

  /**
     Actual behaviour implementation. This method receives a suitable
     ACL message and copies it into the message provided by the
     behaviour creator. It blocks the current behaviour if no suitable
     message is available.
  */
  public void action() {
    ACLMessage msg = null;
    if(template == null)
      msg = myAgent.receive();
    else
      msg = myAgent.receive(template);

    if(msg == null) {
      if(timeOut < 0) {
	block();
	finished = false;
	return;
      }
      else {
	long elapsedTime = 0;
	if(blockingTime != 0)
	  elapsedTime = System.currentTimeMillis() - blockingTime;
	else
	  elapsedTime = 0;
	timeToWait -= elapsedTime;
	if(timeToWait > 0) {
	  blockingTime  = System.currentTimeMillis();
	  // System.out.println("Waiting for " + timeToWait + " ms.");
	  block(timeToWait);
	  return;
	}
	else {
	  future.setMessage(msg);
	  finished = true;
	}
      }
    }
    else {
	future.setMessage(msg);
	finished = true;
    }
  }

  /**
     Checks whether this behaviour ended.
     @return <code>true</code> when an ACL message has been received.
  */
  public boolean done() {
    return finished;
  }

  /**
     Resets this behaviour. This method allows to receive another
     <code>ACLMessage</code> with the same
     <code>ReceiverBehaviour</code> without creating a new object.
  */
  public void reset() {
    finished = false;
    future.reset();
    timeToWait = timeOut;
    blockingTime = 0;
    super.reset();
  }




    /**
     * This method allows the caller to get the received message.
     * @return the received message
     * @exception TimedOut if the timeout passed in the constructor of this
     * class expired before any message (that eventually matched the passed
     * message template) arrived
     * @exception NotYetReady if the message is not yet arrived and the
     * timeout is not yet expired.
     **/
    public ACLMessage getMessage() throws TimedOut, NotYetReady {
	return future.getMessage();
    }
} 
