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

//#APIDOC_EXCLUDE_FILE
 
import jade.core.Agent;
import jade.core.ServiceException;
import jade.core.event.ContainerAdapter;
import jade.core.event.ContainerEvent;
import jade.core.event.ContainerListener;
import jade.core.event.NotificationHelper;
import jade.core.event.NotificationService;

import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.*;
import jade.domain.JADEAgentManagement.*;
import jade.domain.introspection.*;

import jade.lang.acl.ACLMessage;
import jade.util.Logger;

import jade.content.lang.sl.SLCodec;

/**
 This abstract class is the common ancestor of all JADE tools (RMA,
 Sniffer, Introspector, etc.). It provides suitable behaviours to
 interact with the AMS, registering for interesting events and
 requesting actions when needed.
 
 @author Giovanni Rimassa - Universita' di Parma
 @version $Date: 2009-05-18 17:12:10 +0200 (lun, 18 mag 2009) $ $Revision: 6124 $
 */
public abstract class ToolAgent extends Agent {
	
	private ACLMessage AMSSubscription = new ACLMessage(ACLMessage.SUBSCRIBE);
	private ACLMessage AMSCancellation = new ACLMessage(ACLMessage.CANCEL);
	
	private transient ContainerListener myContainerListener = null;
	protected transient Logger logger;
	
	
	// This is left here for backward compatibility
	public static interface EventHandler extends AMSSubscriber.EventHandler {
	}
	
	/**
	 This abstract behaviour is used to receive notifications from
	 the AMS. 
	 */
	protected abstract class AMSListenerBehaviour extends AMSSubscriber {
		// Redefine the onStart() method not to automatically subscribe.
		public void onStart() {
		}
	} // End of AMSListenerBehaviour class
	
	/**
	 * Default constructor.
	 */
	public ToolAgent() {
	}
	
	/**
	 This method is invoked just after the generic agent
	 setup. Subclasses must use this method the same way ordinary
	 agents use their <code>setup()</code> method.
	 */
	protected void toolSetup() {
		
	}
	
	/**
	 This method is invoked just before the generic agent
	 takedown. Subclasses must use this method the same way ordinary
	 agents use their <code>takeDown()</code> method.
	 */
	protected void toolTakeDown() {
		
	}
	
	/**
	 Retrieve the <code>subscribe</code> ACL message with which this
	 tool agent subscribed with the AMS.
	 @return The subscription ACL message.
	 */
	protected ACLMessage getSubscribe() {
		return AMSSubscription;
	}
	
	/**
	 Retrieve the <code>cancel</code> ACL message with which this
	 tool agent removes its subscription with the AMS.
	 @return The cancellation ACL message.
	 */
	protected ACLMessage getCancel() {
		return AMSCancellation;
	}
	
	/**
	 Retrieve the <code>request</code> ACL message with which this
	 tool agent requests the AMS tool-specific actions.
	 @return The request ACL message.
	 */
	protected ACLMessage getRequest() {
		ACLMessage AMSRequest = new ACLMessage(ACLMessage.REQUEST);
		AMSRequest.setSender(getAID());
		AMSRequest.addReceiver(getAMS());
		AMSRequest.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);
		AMSRequest.setLanguage(FIPANames.ContentLanguage.FIPA_SL0);
		return AMSRequest;
	}
	
	public final void setup() {	
		init();
		
		// Fill ACL messages fields
		AMSSubscription.setSender(getAID());
		AMSSubscription.clearAllReceiver();
		AMSSubscription.addReceiver(getAMS());
		AMSSubscription.setLanguage(FIPANames.ContentLanguage.FIPA_SL0);
		AMSSubscription.setOntology(IntrospectionOntology.NAME);
		AMSSubscription.setReplyWith(AMSSubscriber.AMS_SUBSCRIPTION);
		AMSSubscription.setConversationId(getLocalName());
		AMSSubscription.setContent(AMSSubscriber.PLATFORM_EVENTS);
		
		AMSCancellation.setSender(getAID());
		AMSCancellation.clearAllReceiver();
		AMSCancellation.addReceiver(getAMS());
		AMSCancellation.setLanguage(FIPANames.ContentLanguage.FIPA_SL0);
		AMSCancellation.setOntology(IntrospectionOntology.NAME);
		AMSCancellation.setReplyWith(AMSSubscriber.AMS_CANCELLATION);
		AMSCancellation.setConversationId(getLocalName());
		// No content is needed (cfr. FIPA 97 Part 2 page 26)
		
		// Call tool-specific setup
		toolSetup();
	}
	
	protected final void takeDown() {
		clean();
		
		// Call tool-specific takedown
		toolTakeDown();
	}
	
	protected void afterClone() {
		init();
	}
	
	protected void beforeMove() {
		clean();
	}
	
	protected void afterMove() {
		init();
	}
	
	protected void afterLoad() {
		init();
	}
	
	protected void afterThaw() {
		init();
	}
	
	protected void afterReload() {
		init();
	}
	
	protected void beforeSave() {
	}
	
	protected void beforeFreeze() {
		clean();
	}
	
	protected void beforeReload() {
		clean();
	}
	
	private void init() {
		logger = Logger.getMyLogger(getName());
		
		// Register the supported ontologies
		getContentManager().registerOntology(JADEManagementOntology.getInstance());
		getContentManager().registerOntology(IntrospectionOntology.getInstance());
		getContentManager().registerOntology(FIPAManagementOntology.getInstance());
		
		// Register the supported languages
		SLCodec codec = new SLCodec();
		getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL0);
		getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL1);
		getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL2);
		getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL);
		
		// Register to be notified about the REATTACHED event in order to handle Main Container faults
		try {
			NotificationHelper helper = (NotificationHelper) getHelper(NotificationService.NAME);
			myContainerListener = new ContainerAdapter() {
				public void reattached(ContainerEvent ev) {
					// The Main Container lost my subscription --> Subscribe again
					send(getSubscribe());
				}
			};
			helper.registerContainerListener(myContainerListener);
		}
		catch (ServiceException se) {
			// Just print a warning since this does not affect the normal operation of a ToolAgent
			logger.log(Logger.WARNING, "NotificationService not installed. Some tool may not work properly.");
		}
	}
	
	private void clean() {
		if (myContainerListener != null) {
			try {
				NotificationHelper helper = (NotificationHelper) getHelper(NotificationService.NAME);
				helper.deregisterContainerListener(myContainerListener);
			}
			catch (ServiceException se) {
				// Just do nothing since this does not affect the normal operation of a ToolAgent
			}
		}
	}
}
