package ac.essex.gp.params;

import ac.essex.gp.tree.Node;
import ac.essex.gp.tree.Terminal;

/**
 * NodeParams is a metadata object which allows nodes to be stored. It also
 * allows us to instantiate new nodes using this classes getInstance() method.
 *
 * @author Olly Oechsle, University of Essex, Date: 15-Jan-2007
 * @version 1.0
 */
public class NodeParams {

    public static final int VOID = 1;
    public static final int NUMBER = 2;
    public static final int BOOLEAN = 3;
    public static final int SUBSTATEMENT = 4;

    // meta data
    protected String classname;
    protected int returntype;
    protected int rangeType;
    protected int childCount;

    public static final long UNINITIALISED = -1;
    long uniqueID = UNINITIALISED;

    /**
     * It may be that you want to specify a minimum number of nodes of each type to be added.
     */
    protected int minimumNodesRequired = 0;

    /**
     * Initialises the Node Params object.
     * @param classname The classname of the node (allows it to be instantiated programmatically)
     * @param returntype The type this node returns (for strong typing)
     * @param rangeType The range type this node returns (terminals only)
     * @param childCount How many children this node should have.
     */
    public NodeParams(String classname, int returntype, int rangeType, int childCount) {
        this.classname = classname;
        this.returntype = returntype;
        this.childCount = childCount;
        this.rangeType = rangeType;
    }

    /**
     * Sets a unique ID for this Node Params object which is passed on to the nodes
     * that it creates. This allows each node to be able to refer back to the node params
     * object, which is useful for some operations, for instance where you want to count
     * how many times a node params object is used in any particular individual.
     * @param uniqueID The uniqueId to be assigned to this node params object.
     */
    public void registerUniqueID(long uniqueID) {
        this.uniqueID = uniqueID;
    }

    public long getID() {
        if (uniqueID == UNINITIALISED) throw new RuntimeException("ID is not initialised for node params");
        return uniqueID;
    }

    public Node getInstance() {
        try {
            Node n = (Node) Class.forName(classname).newInstance();
            if (n instanceof Terminal) {
                ((Terminal) n).setRangeID(rangeType);
            }
            n.setID(uniqueID);
            return n;
        } catch (ClassNotFoundException e) {
            System.out.println("Class not found: " + classname);
        } catch (Exception e) {
            System.out.println("Could not instantiate class: " + classname);
        }
        return null;
    }

    /**
     * Converts return types into english
     */
    public static String returnTypeToString(int nodeType) {
        switch (nodeType) {
            case VOID: return "void";
            case NUMBER: return "number";
            case BOOLEAN: return "boolean";
            case SUBSTATEMENT: return "substatement";
        }
        return "Unknown node type (" + nodeType + ")";
    }

}
