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

//#MIDP_EXCLUDE_FILE
//#APIDOC_EXCLUDE_FILE

import jade.core.behaviours.*;

import jade.util.leap.List;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.Collection;

import jade.content.Concept;

/**
 
 This class represents an unique identifier referring to a specific
 agent behaviour.
 
 @author Giovanni Rimassa - Universita' di Parma
 @version $Date: 2006-10-25 18:45:54 +0200 (mer, 25 ott 2006) $ $Revision: 5902 $
 
 */
public class BehaviourID implements Concept {
	
	private int code;
	private String name;
	private String className;
	private String kind; 
	private List children = new ArrayList();
	
	/**
	 Default constructor. Builds an unspecified behaviour ID.
	 */
	public BehaviourID () {
	}
	
	/**
	 This constructor builds a new behaviour ID, describing the
	 given behaviour object. The various attributes of the behaviour
	 ID (behaviour name, behaviour class, etc.) are set accordingly.
	 
	 @param b The <code>Behaviour</code> object that is to be
	 described with this ID.
	 */
	public BehaviourID (Behaviour b) {
		code = b.hashCode();
		name = b.getBehaviourName();
		className = b.getClass().getName();      
		kind = getClassKind(b.getClass());      
		
		// If we have a composite behaviour, add the
		// children to this behaviour id.
		if (b instanceof CompositeBehaviour) {
			CompositeBehaviour c = (CompositeBehaviour)b;
			Iterator iter = c.getChildren().iterator();
			while (iter.hasNext()) {
				addChildren(new BehaviourID((Behaviour)iter.next()));
			}
		}
	}
	
	private String getClassKind(Class c) {
		if (c == null) {
			return null;
		}
		
		String className = c.getName();
		// Remove the class name and the '$' characters from
		// the class name for readability.
		int dotIndex = className.lastIndexOf('.');
		int dollarIndex = className.lastIndexOf('$');
		int lastIndex = (dotIndex > dollarIndex ? dotIndex : dollarIndex);
		if (lastIndex == -1) {
			return className;
		}
		else if (lastIndex == dotIndex) {
			return className.substring(lastIndex+1);
		}
		else {
			// This is an anonymous inner class (the name is not meaningful) --> 
			// Use the extended class 
			return getClassKind(c.getSuperclass());
		}
	}
	
	/**
	 Set the name of this behaviour ID
	 @param n The name to give to this behaviour ID.
	 */
	public void setName(String n) {
		name = n;
	}
	
	/**
	 Retrieve the name of this behaviour ID.
	 @return The given name, or <code>null</code> if no name was set.
	 */
	public String getName() {
		return name;
	}
	
	/**
	 Set the code of this behaviour ID
	 @param code The code to give to this behaviour ID.
	 */
	public void setCode(int code) {
		this.code = code;
	}
	
	/**
	 Retrieve the code of this behaviour ID.
	 */
	public int getCode() {
		return code;
	}
	
	/**
	 Set the class name for this behaviour ID. This is the name of
	 the Java class implementing the described agent behaviour.
	 @param n The class name of the described behaviour.
	 */
	public void setClassName(String n) {
		className = n;
	}
	
	/**
	 Retrieve the class name implementing the agent behaviour
	 described by this ID.
	 @return The class name, or <code>null</code> if no class name
	 was set.
	 */
	public String getClassName() {
		return className;
	}
	
	/**
	 Set the kind of behaviour described by this behaviour ID.
	 @param k A string specifying the kind of the described
	 behaviour.
	 */
	public void setKind(String k) {
		kind = k;
	}
	
	/**
	 Retrieve the kind of behaviour described by this behaviour ID.
	 @return A string describing the kind of behaviour, or
	 <code>null</code> if no kind was set.
	 */
	public String getKind() {
		return kind;
	}
	
	/**
	 Adds a new behaviour ID as a child of this one. The
	 parent-child relationship between behaviour IDs reflects the
	 one between their described behaviours.
	 
	 @param bid The behaviour ID object to add.
	 */
	public void addChildren(BehaviourID bid) {
		children.add(bid);
	}
	
	/**
	 Retrieve the list of all the children behaviour IDs, as an
	 iterator object.
	 @return An iterator over the children collection of this
	 behaviour ID.
	 */
	public Iterator getAllChildren() {
		return children.iterator();
	}
		
	/**
	 Tells whether this behaviour ID has children.
	 @return If the children collection is empty, <code>true</code>
	 is returned, and <code>false</code> otherwise.
	 */
	public boolean isSimple() {
		return (children.size() == 0);
	}
	
	/**
	 Equality test on two behaviour IDs. They are considered to be
	 equal if and only if their <i>name</i>, <i>className</i> and
	 <i>kind</i> attributed are the same.
	 
	 @param o The right hand side of the equality test (the left
	 hand one being the current object).
	 @return If the <code>o</code> parameter is a behaviour ID with
	 the same name, class name and kind of the current object,
	 <code>true</code>. Otherwise, <code>false</code> is returned.
	 */
	public boolean equals(Object o) {
		if (o != null && o instanceof BehaviourID) {
			BehaviourID b = (BehaviourID)o;
			return (checkEquals(name, b.name) && checkEquals(className, b.className) && checkEquals(kind, b.kind));
		}
		else {
			return false;
		}
	}
	
	
	
	private boolean checkEquals(String s1, String s2) {
		if (s1 != null) {
			return (s1.equals(s2));
		}
		else {
			return s2 == null;
		}
	}

	/**
	 Hash code operation, compliant with identity-by-name. This
	 method returns an hash code for a behaviour ID, so that two
	 behaviour IDs with the same name, class name and kind have the
	 same hash code.
	 */
	public int hashCode() {
		int result = 0;
		if(name != null) {
			result = name.hashCode();
		}
		if(className != null) {
			result = result*2 + className.hashCode();
		}
		if(kind != null) {
			result = result*2 + kind.hashCode();
		}
		
		return result;
	}
	
	/**
	 Create a string representation for this behaviour ID. The
	 actual representation is simply the name of the behaviour ID.
	 
	 @return The name of this behaviour ID.
	 */
	public String toString() {
		return name;
	}
	
}
