/*****************
 * CupsNode class, an example of a SearchNode
 * Mark Goadrich
 ******************/

import java.util.*;

public class CupsNode implements SearchNode {
    
    // Class data member, since goal is always constant
    public static int goalValue = 0;

    // Data Members
    private Cup[] cups = null;
    private CupsNode parent = null;
    private String action = null;
    private int level = 0;
    private int hValue = 0;

    // Initial Constructor
    public CupsNode(String[] args) {
        cups = new Cup[args.length - 1];
        goalValue = Integer.parseInt(args[0]);
        for (int i = 1; i < args.length; i++) {
            cups[i-1] = new Cup(Integer.parseInt(args[i]), 0);
        }
        action = "INITIAL";
        parent = null;
        level = 0;
    }

    // Child Constructor
    public CupsNode(Cup[] cups, CupsNode parent, String action) {
        this.cups = cups;
        this.parent = parent;
        this.action = action;
        if (parent != null) {
            level = parent.getLevel() + 1;
        }
    }

    // Returns the level of the node (i.e how many parents)
    public int getLevel() {
        return level;
    }

    // Returns the heuristic value (not written)
    public int gethValue() {
        return hValue;
    }
    
    // Test for equality between two objects
    public boolean equals(Object o) {
        if (o instanceof CupsNode) {
            CupsNode cn = (CupsNode)o;
            for (int i = 0; i < cups.length; i++) {
                if (cups[i].getContents() != cn.cups[i].getContents()) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    // Tests for the goal state
    public boolean isGoal() {
        if (cups[0].getContents() == goalValue) {
            return true;
        }
        return false;
    }
    
    // Generate all the children nodes of this node
    public CupsNode[] generateChildren() {
    
        // First find the number of children, p
        int p = 0;
        for (int i = 1; i < cups.length; i++) {
            p += i;
        }
        p = p * 2 + cups.length * 2;
    
        CupsNode[] cns = new CupsNode[p];
        int x = 0;
        for (int i = 0; i < cups.length; i++) {

            // Find all fill children
            Cup[] ca = new Cup[cups.length];
            for (int j = 0; j < cups.length; j++) {
                ca[j] = cups[j].clone();
            }
            ca[i].fill();
            cns[x] = new CupsNode(ca, this, "Fill " + ca[i].getSize());
            x++;

            // Find all empty children
            ca = new Cup[cups.length];
            for (int j = 0; j < cups.length; j++) {
                ca[j] = cups[j].clone();
            }
            ca[i].empty();
            cns[x] = new CupsNode(ca, this, "Empty " + ca[i].getSize());
            x++;

            // Find all pour children
            for (int k = 0; k < cups.length; k++) {
                if (k != i) {
                    ca = new Cup[cups.length];
                    for (int j = 0; j < cups.length; j++) {
                        ca[j] = cups[j].clone();
                    }
                    ca[i].pourFrom(ca[k]);
                    cns[x] = new CupsNode(ca, this, "Pour " + ca[k].getSize() 
                                                    + " to " + ca[i].getSize());
                    x++;
                }
            }
        }
        return cns;
    }

    // Converts this node into a String
    public String toString() {
        String s = "";
        for (int i = 0; i < cups.length; i++) {
            s += cups[i].getContents() + " ";
        }
        return s;
    }
    
    // Prints a recursive path from initial node to here
    public void printPath() {
        if (parent != null) {
            parent.printPath();
        }
        System.out.println(this.toString() + " " + action);
    }
}
