package ac.essex.gp.params;

import ac.essex.gp.tree.Node;
import ac.essex.gp.util.DeepCopy;
import ac.essex.gp.nodes.ADFNode;
import ac.essex.gp.problems.coevolve.TestResults;

/**
 * A originalNode params object that represents a tree of nodes as opposed to an individual originalNode.
 * Functions in the same way as a terminal - it has no children (as far as the originalNode builder is concerned
 *
 * ADF Node params may have a fitness which is reflectant in some way upon the fitness
 * of the individuals that it has contributed to.
 *
 * @author Olly Oechsle, University of Essex, Date: 28-Feb-2007
 * @version 1.0
 */
public class ADFNodeParams extends NodeParams implements Comparable {

    protected double fitness;
    protected ADFNode originalNode;

    public ADFNodeParams(ADFNode node, int returntype) {
        super("", returntype, RangeTypes.DONT_CARE, 0);
        // keep a reference to this node, we'll use clones of it to make new instances.
        this.originalNode = node;
        resetFitness();
    }

    /**
     * Gets the fitness of this ADF Node. This value is affected by the performance
     * of individuals to which the ADF originalNode is contributing.
     * @return
     */
    public double getFitness() {
        return fitness;
    }

    /**
     * Sets the fitness of the originalNode param, which is usually the fitness of the
     * individual using this originalNode param. As the same originalNode param will probably be
     * used many time different strategies may be used to discover a fair average
     * measure of this originalNode's contribution to the overall fitness.
     * @param fitness
     */
    public void setFitness(double fitness) {
        // if this fitness is better (lower) than the existing fitness, replace
        if (fitness < this.fitness) this.fitness = fitness;
    }

    /**
     * Resets the fitness of the ADFNodeParams object.
     */
    public void resetFitness() {
        // assign fitness the worst value
        fitness = Double.MAX_VALUE;
    }

    public int compareTo(Object o) {
        if (o instanceof ADFNodeParams)  {
            double otherFitness = ((ADFNodeParams) o).fitness;
            if (otherFitness > fitness) return -1;
            if (otherFitness < fitness) return +1;
            return 0;
        } throw new RuntimeException("Non ADFNodeParam object in classifier population");
    }

    /**
     * Instantiates an ADF node. Since the logic in an ADF node is created at runtime and not
     * compile time it cannot be instantiated by reflecting an existing class, rather it makes
     * a (deep) clone of the original node. The node is given a reference to this NodeParams class
     * so that it can be identified later if it is present in a successful individual.
     * @return A new node, or null if the copying operation fails.
     */
    public ADFNode getInstance() {
        try {
            return (ADFNode) new DeepCopy().copy(originalNode);
        } catch (Exception e) {
            System.err.println("Could not copy ADF Node: " + originalNode);
        }
        return null;
    }

    /**
     * Test Results class holds the results of the classifier tested against the training data.
     * It provides an equals method that allows us to determine if the classifier is different to
     * any other classifier.
     */
    protected TestResults testResults = null;

    /**
     * Test Results class holds the results of the classifier tested against the training data.
     * It provides an equals method that allows us to determine if the classifier is different to
     * any other classifier.
     */
    public TestResults getTestResults() {
        return testResults;
    }

    /**
     * Test Results class holds the results of the classifier tested against the training data.
     * It provides an equals method that allows us to determine if the classifier is different to
     * any other classifier.
     */    
    public void setTestResults(TestResults testResults) {
        this.testResults = testResults;
    }

    /**
     * Jitters the ADF node - see if we can get any better results
     */
    public void jitter() {
        originalNode.jitter();       
    }

    public String toString() {
        if (testResults == null) {
            return super.toString();
        } else {
            return "ADFNOde: " + testResults.toString();
        }
    }

}
