package j3deditor.bin.hierarchy;


import j3deditor.bin.hierarchy.util.HNodeType;


/**
 * Base class for all hierarchy classes.
 *
 * @author Risto Seene
 */
public abstract class HierarchyNode extends Object{


	/** reference to the parent of this node */
	private HierarchyNode parent = null;


	/** the name of this node */
	private String name;

	/** the type of this node */
	private HNodeType type;

	/** the index of this node */
	private long index;


	/**
	 * Creates an instance of <code>HierarchyNode</code>
	 * with the specified name, type and index.
	 *
	 * @param name
	 *	the name of the new node
	 * @param type
	 *	the type of the new node
	 * @param index
	 *	the index of the new node
	 */
	public HierarchyNode(String name, HNodeType type, long index){
		this.name = name;
		this.type = type;
		this.index = index;
	}


	/**
	 * Removes the specified child node.
	 * Should be overrided by subclasses
	 *
	 * @param child
	 *	child node to be removed
	 */
	protected void removeChild(HierarchyNode child){
	}


	/**
	 * Clones this node.
	 * Should only be called by subclasses
	 *
	 * @param node
	 *	node sent by subclass
	 * @return the same node that was inserted
	 */
	protected HierarchyNode clone(HierarchyNode node){
		node.setName(this.toString());
		return node;
	}


	/**
	 * Returns the child at the specified index.
	 * Should be overrided by subclasses
	 *
	 * @param index
	 *	index of child
	 * @return Returns the child with the specified index
	 */
	public HierarchyNode getChild(int index){
		return null;
	}


	/**
	 * Returns the number of children.
	 * Should be overrided by subclasses
	 *
	 * @return Returns the number of children as integer
	 */
	public int getNumberOfChildren(){
		return 0;
	}


	/**
	 * Returns the name of this node.
	 *
	 * @return Returns the name of this node
	 */
	public String toString(){
		return name;
	}


	/**
	 * Renames this node.
	 *
	 * @param name
	 *	new name
	 */
	public void setName(String name){
		this.name = name;
	}


	/**
	 * Returns the index of specified child.
	 * Should be overrided by subclasses
	 *
	 * @param child
	 *	child node which index will be returned
	 * @return Returns the index of specified child
	 */
	public int getIndexOfChild(HierarchyNode child){
		return 0;
	}


	/**
	 * Returns the type of this node.
	 *
	 * @return Returns the type of this node
	 */
	public HNodeType getType(){
		return type;
	}


	/**
	 * Returns the index of this node.
	 *
	 * @return Returns the index of this node
	 */
	public long getIndex(){
		return index;
	}


	/**
	 * Tests if this node equals to the given node.
	 *
	 * @param node
	 *	node that will be compared to this node
	 * @return Returns <code>true</code> if the given node
	 *	equals to this node,
	 *	otherwise <code>false</code>
	 */
	public boolean equals(HierarchyNode node){
		if(type == node.getType() && index == node.getIndex()){
			return true;
		}
		return false;
	}


	/**
	 * Sets the parent of this node.
	 *
	 * @param parent
	 *	new parent
	 */
	public void setParent(HierarchyNode parent){
		this.parent = parent;
	}


	/**
	 * Returns the parent of this node.
	 *
	 * @return Returns the parent of this node
	 */
	public HierarchyNode getParent(){
		return parent;
	}
	
	
	/**
	 * Returns the first anchestor of the given type.
	 * 
	 * @param type
	 * 	type of the requested anchestor
	 * @return
	 * 	returns the requested anchestor
	 */
	public HierarchyNode getParent(Class type){
		return this.getParent(this, type);
	}
	
	private HierarchyNode getParent(HierarchyNode current, Class type){
		HierarchyNode parent = current.getParent();
		if(parent != null){
			if(parent.getClass().getCanonicalName().equals(type.getCanonicalName())){
				return parent;
			}
			parent = getParent(parent, type);
		}
		return parent;
	}


	/**
	 * Tests if this node has child which name
	 * equals to the name of the given node.
	 * Should be overrided by subclasses
	 *
	 * @param object
	 *	node which name will be tested
	 * @return Returns <code>true</code> if the name of the given
	 *	node equals to the name of this node,
	 *	otherwise <code>false</code>
	 */
	protected boolean hasNamedChild(HierarchyNode object){
		return false;
	}


	/**
	 * Changes the name of this node if the parent
	 * of this node has a child with the same name.
	 */
	public void updateName(){
		HierarchyNode parent = this.getParent();
		if(parent != null && parent.hasNamedChild(this)){
			String name = this.toString();
			if(name.contains("[") && name.contains("]")){
				name = name.substring(0, name.indexOf("[") + 1) + getIndex() + name.substring(name.indexOf("]"));
			} else {
				name = name + "[" + getIndex() + "]";
			}
			setName(name);
		}
	}

}
