/*****************************************************************
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.proto;

//#CUSTOM_EXCLUDE_FILE

import jade.core.*;
import jade.core.behaviours.*;
import jade.lang.acl.*;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.domain.FIPAAgentManagement.FailureException;
import jade.domain.FIPANames;
import jade.proto.states.*;
import jade.util.leap.Iterator;

/**
* Behaviour class for <code>fipa-propose</code>
* <em>Responder</em> role. This  behaviour implements the
* <code>fipa-propose</code> interaction protocol from the point
* of view of a responder to a propose (<code>propose</code>)
* message.<p>
* The API of this class is similar and homogeneous to the 
* <code>AchieveREResponder</code>. 
* <p>
* When a message arrives
* that matches the message template passed to the constructor,
* the callback method <code>prepareResponse</code> is executed
* that must return the wished response, for instance the 
* <code>ACCEPT_PROPOSAL</code>
* reply message. Any other type of returned communicative act 
* is sent and then closes the
* protocol.
* <p>
* If a message were received, with the same value of this 
* <code>conversation-id</code>, but that does not comply with the FIPA 
* protocol, than the method <code>handleOutOfSequence</code> would be called.
* <p>
* This class can be extended by the programmer by overriding all the needed
* handle methods or, in alternative, appropriate behaviours can be
* registered for each handle via the <code>registerHandle</code>-type
* of methods. This last case is more difficult to use and proper
* care must be taken to properly use the <code>DataStore</code> of the
* <code>Behaviour</code> as a shared memory mechanism with the
* registered behaviour.
* <p>
* Read carefully the section of the 
* <a href="..\..\..\programmersguide.pdf"> JADE programmer's guide </a>
* that describes
* the usage of this class.
* @see jade.proto.ProposeInitiator
* @see jade.proto.AchieveREResponder
* 
* @author Jerome Picault - Motorola Labs
* @version $Date: 2005-09-16 15:54:46 +0200 (ven, 16 set 2005) $ $Revision: 5780 $
*/

public class ProposeResponder extends FSMBehaviour implements FIPANames.InteractionProtocol {
	
  /** 
   * key to retrieve from the DataStore of the behaviour the ACLMessage 
   *	object sent by the initiator.
   **/
  public final String PROPOSE_KEY = "__propose" + hashCode();
  /** 
   * key to retrieve from the DataStore of the behaviour the ACLMessage 
   *	object sent as a response to the initiator.
   **/
  public final String RESPONSE_KEY = "__response" + hashCode();

  // FSM states names
  protected static final String RECEIVE_PROPOSE = "Receive-propose";
  protected static final String PREPARE_RESPONSE = "Prepare-response";
  protected static final String SEND_RESPONSE = "Send-response";

    // Private inner classes for the FSM states
    private static class PrepareResponse extends OneShotBehaviour {

	public PrepareResponse(Agent a) {
	    super(a);
	}

	// For persistence service
	private PrepareResponse() {
	}

        public void action() {
	    ProposeResponder fsm = (ProposeResponder)getParent();
	    DataStore ds = getDataStore();
	    ACLMessage propose = (ACLMessage) ds.get(fsm.PROPOSE_KEY);

	    ACLMessage response = null;
	    try {
		response = fsm.prepareResponse(propose); 
	    }
	    catch (NotUnderstoodException nue) {
		response = nue.getACLMessage();
	    }
	    catch (RefuseException re) {
		response = re.getACLMessage();
	    }
	    ds.put(fsm.RESPONSE_KEY, response);
        }

    } // End of PrepareResponse class


  // The MsgReceiver behaviour used to receive propose messages
  MsgReceiver rec = null;
	
  /**
   * This static method can be used 
   * to set the proper message template (based on the interaction protocol 
   * and the performative)
   * into the constructor of this behaviour.
   * @see FIPANames.InteractionProtocol
   **/
  public static MessageTemplate createMessageTemplate(String iprotocol){
	
    if(CaseInsensitiveString.equalsIgnoreCase(FIPA_PROPOSE,iprotocol))
	    return MessageTemplate.and(MessageTemplate.MatchProtocol(FIPA_PROPOSE),MessageTemplate.MatchPerformative(ACLMessage.PROPOSE));
    else
      return MessageTemplate.MatchProtocol(iprotocol);
  }

  /**
   * Constructor of the behaviour that creates a new empty DataStore
   * @see #ProposeResponder(Agent a, MessageTemplate mt, DataStore store) 
   **/
  public ProposeResponder(Agent a, MessageTemplate mt){
    this(a,mt, new DataStore());
  }

  /**
   * Constructor.
   * @param a is the reference to the Agent object
   * @param mt is the MessageTemplate that must be used to match
   * the initiator message. Take care that 
   * if mt is null every message is consumed by this protocol.
   * @param store the DataStore for this protocol
   **/
  public ProposeResponder(Agent a, MessageTemplate mt, DataStore store) {
    super(a);
		
    setDataStore(store);
		
    // Register the FSM transitions
    registerDefaultTransition(RECEIVE_PROPOSE, PREPARE_RESPONSE);
    registerDefaultTransition(PREPARE_RESPONSE, SEND_RESPONSE);
    registerDefaultTransition(SEND_RESPONSE, RECEIVE_PROPOSE);
	
    // Create and register the states that make up the FSM
    Behaviour b = null;

    // RECEIVE_PROPOSE
    rec = new MsgReceiver(myAgent, mt, -1, getDataStore(), PROPOSE_KEY);
    registerFirstState(rec, RECEIVE_PROPOSE);

    // PREPARE_RESPONSE
    b = new PrepareResponse(myAgent);
    b.setDataStore(getDataStore());		
    registerState(b, PREPARE_RESPONSE);

    // SEND_RESPONSE
    b = new ReplySender(myAgent, RESPONSE_KEY, PROPOSE_KEY);
    b.setDataStore(getDataStore());		
    registerState(b, SEND_RESPONSE);
  }

  /**
   * Reset this behaviour.
   */
  public void reset() {
    super.reset();
    DataStore ds = getDataStore();
    ds.remove(PROPOSE_KEY);
    ds.remove(RESPONSE_KEY);
  }
    
  /**
   * This method allows to change the <code>MessageTemplate</code>
   * that defines what messages this ProposeResponder will react to
   * and reset the protocol.
   */
  public void reset(MessageTemplate mt) {
    this.reset();
    rec.reset(mt, -1, getDataStore(), PROPOSE_KEY);
  }
    

  /**   
   * This method is called when the initiator's
   * message is received that matches the message template
   * passed in the constructor. 
   * This default implementation return null which has
   * the effect of sending no reponse. Programmers should
   * override the method in case they need to react to this event.
   * @param propose the received message
   * @return the ACLMessage to be sent as a response (i.e. one of
   * <code>accept_proposal, reject_proposal, not-understood</code>. 
   * <b>Remind</b> to use the method createReply of the class ACLMessage 
   * in order to create a good reply message
   * @see jade.lang.acl.ACLMessage#createReply()
   **/
  protected ACLMessage prepareResponse(ACLMessage propose) throws NotUnderstoodException, RefuseException {
    return null;
  }
    
  /**
   * This method allows to register a user defined <code>Behaviour</code>
   * in the PREPARE_RESPONSE state.
   * This behaviour would override the homonymous method.
   * This method also set the 
   * data store of the registered <code>Behaviour</code> to the
   * DataStore of this current behaviour.
   * It is responsibility of the registered behaviour to put the
   * response to be sent into the datastore at the <code>RESPONSE_KEY</code>
   * key.
   * @param b the Behaviour that will handle this state
   */
  public void registerPrepareResponse(Behaviour b) {
    registerState(b, PREPARE_RESPONSE);
    b.setDataStore(getDataStore());
  }

    //#MIDP_EXCLUDE_BEGIN

    // For persistence service
    private ProposeResponder() {
    }
    //#MIDP_EXCLUDE_END

}
