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

//#APIDOC_EXCLUDE_FILE

import jade.util.leap.Map;
import jade.util.leap.HashMap;
import jade.util.leap.Serializable;
import jade.util.Logger;

/**
   This class provides a partial implementation of the
   <code>Node</code> interface. Concrete IMTPs will have to provide a
   full implementation of the <code>Node</code> interface, possibly by
   subclassing this class.

   @author Giovanni Rimassa - FRAMeTech s.r.l.

 */
public abstract class BaseNode implements Node, Serializable {

	private transient Logger myLogger;

	public BaseNode(String name, boolean hasPM) {
		myName = name;
		hasLocalPM = hasPM;
		localSlices = new HashMap(5);
		myLogger = Logger.getMyLogger(getClass().getName());
	}

	public void setName(String name) {
		myName = name;
	}

	public String getName() {
		return myName;
	}

	public boolean hasPlatformManager() {
		return hasLocalPM;
	}

	public void exportSlice(String serviceName, Service.Slice localSlice) {
		localSlices.put(serviceName, localSlice);
	}

	public void unexportSlice(String serviceName) {
		localSlices.remove(serviceName);
	}

	protected Service.Slice getSlice(String serviceName) {
		return (Service.Slice)localSlices.get(serviceName);
	}


	/**
       Provides an IMTP independent implementation for the
       horizontal command serving mechanism. IMTP dependent implementations 
       of the accept() method should invoke this method.
       @param cmd The horizontal command to process.
       @return The object that is the result of processing the command.
       @throws ServiceException If the service the command belongs to
       is not present on this node.
	 */
	public Object serveHorizontalCommand(HorizontalCommand cmd) throws ServiceException {

		String serviceName = cmd.getService();
		String commandName = cmd.getName();
		Object[] commandParams = cmd.getParams();

		if (myLogger.isLoggable(Logger.FINE)) {
			myLogger.log(Logger.FINE, "Node "+myName+" serving incoming H-Command "+commandName+" of Service "+serviceName);
		}

		// Look up in the local slices table and find the slice to dispatch to
		Service.Slice slice = getSlice(serviceName);

		if(slice != null) {
			Object ret = null;
			VerticalCommand vCmd = slice.serve(cmd);

			if(vCmd != null) {
				vCmd.setPrincipal(cmd.getPrincipal());
				vCmd.setCredentials(cmd.getCredentials());
				// Hand it to the command processor
				if (myLogger.isLoggable(Logger.FINE)) {
					myLogger.log(Logger.FINE, "Node "+myName+" issuing incoming V-Command "+vCmd.getName()+" of Service "+vCmd.getService());
				}
				serveVerticalCommand(vCmd);
				ret = vCmd.getReturnValue();
			}
			else {
				ret = cmd.getReturnValue();
			}

			if (ret != null) {
				if (myLogger.isLoggable(Logger.FINE)) {
					myLogger.log(Logger.FINE, "Node "+myName+" return value for incoming H-Command "+commandName+" of Service "+serviceName+" = "+ret);
				}
			}
			return ret;
		}
		else {
			String s = "Node "+myName+": Service "+serviceName+" Unknown. Command = "+commandName;
			throw new ServiceException("-- "+s+" --");
		}
	}

	public void setCommandProcessor(CommandProcessor cp) {
		processor = cp;
	}

	public void setServiceManager(ServiceManager mgr) {
		myServiceManager = mgr;
	}

	public void platformManagerDead(String deadPMAddr, String notifyingPMAddr) throws IMTPException {
		((ServiceManagerImpl) myServiceManager).platformManagerDead(deadPMAddr, notifyingPMAddr);
	}

	/**
       Serves an incoming vertical command, locally. This method is
       invoked if a new <code>VerticalCommand</code> object is
       generated by a slice targetted by a former
       <code>HorizontalCommand</code>, which happens if the
       <code>Slice.serve()</code> yields a non-null result.

       This method makes it so that the newly created vertical command
       is handed to the command processor to first pass through all
       incoming filters and then to be dispatched to its proper
       incoming command sink.
       @param cmd The vertical command to process.
       @return The object that is the result of processing the command.
       @throws ServiceException If some problem occurs.
	 */
	private Object serveVerticalCommand(VerticalCommand cmd) throws ServiceException {
		if(processor == null) {
			throw new ServiceException("No command processor for node <" + getName() + ">");
		}

		return processor.processIncoming(cmd);
	}

	protected transient ServiceManager myServiceManager;
	private transient CommandProcessor processor;

	// The name of this node
	private String myName;

	// True if a local copy of the Platform Manager is deployed at this Node
	private boolean hasLocalPM = false;

	// A map, indexed by service name, of all the local slices of this
	// node. This map is used to dispatch incoming commands to the
	// service they belong to.
	private transient Map localSlices;

	public String toString() {
		return myName;
	}
}
