package trees;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import junit.framework.TestCase;
/*
 * Created on Feb 11, 2005
 */

/**
 * @author David Matuszek
 * @version Feb 11, 2005
 */
public class TreeTest extends TestCase {
    Tree<String> tree, tree1, tree2;
    Tree<String> a1, b1, c1, d1, e1, f1, g1, a2, b2, c2, d2, e2, f2, g2;
    static boolean firstTime = true;

    public static void main(String[] args) {
        junit.swingui.TestRunner.run(TreeTest.class);
    }

    /*
     * @see TestCase#setUp()
     */
    protected void setUp() throws Exception {
        super.setUp();
        /*
         *              a1              a2
         *             / \             / \
         *            /   \           /   \
         *          b1     c1        b2    c2
         *          /\     /\       /\     /\
         *        d1  e1 f1  g1   e2  d2 f2  g2
         */
        a1 = new Tree<String>("a");
        b1 = new Tree<String>("b");
        c1 = new Tree<String>("c");
        d1 = new Tree<String>("d");
        e1 = new Tree<String>("e");
        f1 = new Tree<String>("f");
        g1 = new Tree<String>("g");
        a1.addChild(b1);
        a1.addChild(c1);
        b1.addChild(d1);
        b1.addChild(e1);
        c1.addChild(f1);
        c1.addChild(g1);
        tree1 = a1;
//        if (firstTime) System.out.println("tree1 = " + tree1);
        a2 = new Tree<String>("a");
        b2 = new Tree<String>("b");
        c2 = new Tree<String>("c");
        d2 = new Tree<String>("d");
        e2 = new Tree<String>("e");
        f2 = new Tree<String>("f");
        g2 = new Tree<String>("g");
        a2.addChild(b2);
        a2.addChild(c2);
        b2.addChild(e2);
        b2.addChild(d2);
        c2.addChild(f2);
        c2.addChild(g2);
        tree2 = a2;
//        if (firstTime) System.out.println("tree2 = " + tree2);
        firstTime = false;
    }

    public final void testTree() {
        Tree t = new Tree<String>("x");
        assertTrue(t.isRoot());
        assertTrue(t.isLeaf());
        assertEquals("x", t.getValue());
        assertNull(t.parent());
    }

    public final void testGetValue() {
        assertEquals("a", a1.getValue());
        assertEquals("b", b1.getValue());
        assertEquals("a", a2.getValue());
    }

    public final void testSetValue() {
        a1.setValue("A");
        assertEquals("A", a1.getValue());
    }

    public final void testIsRoot() {
        assertTrue(a1.isRoot());
        assertFalse(b1.isRoot());
        assertTrue(a2.isRoot());
        assertFalse(e1.isRoot());
    }

    public final void testIsLeaf() {
        assertFalse(a1.isLeaf());
        assertFalse(b1.isLeaf());
        assertFalse(c1.isLeaf());
        assertTrue(d1.isLeaf());
        assertTrue(e1.isLeaf());
        assertTrue(f1.isLeaf());
        assertTrue(g1.isLeaf());
    }

    public final void hasChildren() {
        assertTrue(a1.isLeaf());
        assertTrue(b1.isLeaf());
        assertTrue(c1.isLeaf());
        assertFalse(d1.isLeaf());
        assertFalse(e1.isLeaf());
        assertFalse(f1.isLeaf());
        assertFalse(g1.isLeaf());
    }

    public final void testHasNextSibling() {
        assertFalse(a1.hasNextSibling());
        assertTrue(b1.hasNextSibling());
        assertFalse(c1.hasNextSibling());
        assertTrue(d1.hasNextSibling());
        assertFalse(e1.hasNextSibling());
    }

    public final void testHasPreviousSibling() {
        assertFalse(a1.hasPreviousSibling());
        assertFalse(b1.hasPreviousSibling());
        assertTrue(c1.hasPreviousSibling());
        assertFalse(d1.hasPreviousSibling());
        assertTrue(e1.hasPreviousSibling());
    }

    public final void testParent() {
        assertNull(a1.parent());
        assertSame(a1, b1.parent());
        assertSame(a1, c1.parent());
        assertSame(b1, d1.parent());
        assertSame(b1, e1.parent());
    }

    public final void testFirstChild() {
        assertSame(b1, a1.firstChild());
        assertSame(d1, b1.firstChild());
        assertSame(f1, c1.firstChild());
        assertNull(d1.firstChild());
        assertNull(e1.firstChild());
    }

    public final void testLastChild() {
        assertSame(c1, a1.lastChild());
        assertSame(e1, b1.lastChild());
        assertSame(g1, c1.lastChild());
        assertNull(d1.lastChild());
        assertNull(e1.lastChild());
    }
    
    public final void testChild() {
        Tree<String> x = new Tree<String>("x");
        Tree<String> y = new Tree<String>("y");
        a1.addChild(x);
        a1.addChild(y);
        assertEquals(b1, a1.child(0));
        assertEquals(c1, a1.child(1));
        assertEquals(x, a1.child(2));
        assertEquals(y, a1.child(3));
    }

    public final void testChildren() {
        ArrayList<Tree<String>> odd = new ArrayList<Tree<String>>();
        odd.add(b1);
        odd.add(c1);
        assertEquals(odd, a1.children());
    }

    public final void testNextSibling() {
        assertNull(a1.nextSibling());
        assertSame(c1, b1.nextSibling());
        assertNull(c1.nextSibling());
        assertSame(e1, d1.nextSibling());
        assertNull(e1.nextSibling());
    }

    public final void testPreviousSibling() {
        assertNull(a1.previousSibling());
        assertNull(b1.previousSibling());
        assertSame(b1, c1.previousSibling());
        assertNull(d1.previousSibling());
        assertSame(d1, e1.previousSibling());
    }
    
    public final void testEquals() {
        assertTrue(f1.equals(f2));
        assertFalse(f1.equals(g1));
        assertTrue(a1.equals(a1));
        assertFalse(a1.equals(a2));
        assertTrue(c1.equals(c2));
        assertFalse(b1.equals(b2));
        Tree x = new Tree<String>("x");
        Tree y = new Tree<String>(null);
        assertFalse(f1.equals(null));
        assertFalse(x.equals(y));
        assertFalse(y.equals(x));
    }

    public final void testAddChild() {
        Tree<String> x = new Tree<String>("x");
        Tree<String> y = new Tree<String>("y");
        Tree<String> z = new Tree<String>("z");
        a1.addChild(x);
        a1.addChild(y);
        a1.addChild(z);
        ArrayList<String> expected =
            new ArrayList(Arrays.asList(new Tree[]
                 { b1, c1, x, y, z }
            ));
        assertEquals(expected, a1.children());
        assertEquals(new ArrayList(), g1.children());
        try {
            f1.addChild(c1);
            fail();
        } catch (Exception e) {}
        try {
            f1.addChild(a1);
            fail();
        } catch (Exception e) {}
        try {
            b1.addChild(b1);
            fail();
        } catch (Exception e) {}
    }

    public final void testAddChildren() {
        Tree<String> x = new Tree<String>("x");
        Tree<String> y = new Tree<String>("y");
        Tree<String> z = new Tree<String>("z");
        ArrayList<Tree<String>> added = new ArrayList<Tree<String>>();
        added.add(x); added.add(y); added.add(z);
        ArrayList<Tree<String>> exp = new ArrayList<Tree<String>>();
        exp.add(b1); exp.add(c1); exp.add(x); exp.add(y); exp.add(z); 
        a1.addChildren(added);
        assertEquals(exp, a1.children());
    }
    
    private ArrayList<Object> makeArrayList(Object[] objects) {
        return new ArrayList<Object>(Arrays.asList(objects));
    }
    

    public final void testRemove() {
        ArrayList<Tree> exp = new ArrayList<Tree>();
        
        exp.add(e1);
        d1.remove();
        assertEquals(exp, b1.children());
        
        e1.remove();
        exp.clear();
        assertEquals(exp, b1.children());
        
        exp.clear();
        exp.add(e2);
        d2.remove();
        assertEquals(exp, b2.children());
        
        e2.remove();
        exp.clear();
        assertEquals(exp, b2.children());
        
        Tree<String> x = new Tree<String>("x");
        Tree<String> y = new Tree<String>("y");
        Tree<String> z = new Tree<String>("z");
        ArrayList<Tree<String>> added = new ArrayList<Tree<String>>();
        added.add(x); added.add(y); added.add(z);
        exp.add(b1); exp.add(c1); exp.add(x); exp.add(y); exp.add(z); 
        a1.addChildren(added); // a1 children now b1 b2 x y z
        assertEquals(exp, a1.children());
        x.remove();
        exp.remove(x);
        assertEquals(exp, a1.children());
    }
    
    public final void testDepth() {
        assertEquals(0, a1.depth());
        assertEquals(1, c1.depth());
        assertEquals(2, d1.depth());
    }
    
    public final void testHasAncestor() {
        assertTrue(a1.hasAncestor(a1));
        assertTrue(b1.hasAncestor(a1));
        assertTrue(c1.hasAncestor(a1));
        assertTrue(d1.hasAncestor(a1));
        assertTrue(d1.hasAncestor(b1));
        assertFalse(a1.hasAncestor(b1));
        assertFalse(a1.hasAncestor(d1));
        assertFalse(b1.hasAncestor(d1));
        assertFalse(b1.hasAncestor(c1));
    }
    
    public final void testIterator( ) {
        Tree t = Tree.parse("a( b(c d e) f(g) h )");
        Iterator iter = t.iterator();
        
        it(iter, 'a');
        it(iter, 'b');
        it(iter, 'c');
        it(iter, 'd');
        it(iter, 'e');
        it(iter, 'f');
        it(iter, 'g');
        it(iter, 'h');
        assertFalse(iter.hasNext());
    }
    
    public final void testParse() {
        PrintStream originalOut = System.out; 
        try {
            // Prepare to capture output       
            OutputStream os = new ByteArrayOutputStream();
            PrintStream ps = new PrintStream(os);
            System.setOut(ps);

            // Perform tests
            Tree<String> expected = new Tree<String>("abc");
            Tree<String> actual = Tree.parse("abc");
            assertEquals(expected, actual);
            
            actual = Tree.parse("a(b(d e) c(f g))");
            assertEquals(a1, actual);
        }
        finally {
            // Restore normal operation
            System.setOut(originalOut);
        }
    }
    
    public void testParseFailure() {
        try {
            Tree.parse("a(b(d e) c(f g)");
            fail("Did not catch unbalanced '('");
        }
        catch (RuntimeException e) {}
        try {
            Tree.parse("a(b(d e) c(f g)))");
            fail("Did not catch unbalanced ')'");
        }
        catch (RuntimeException e) {}
    }
    
    public final void testPrint() {
        PrintStream originalOut = System.out; 
        String sep = System.getProperty("line.separator");
        try {
            // Prepare to capture output       
            OutputStream os = new ByteArrayOutputStream();
            System.setOut(new PrintStream(os));

            // Perform tests
            Tree.parse("a(b(d) c(e f))").print();
            String actualOutput = os.toString();
            String expected = "a" + sep
                            + "   b" + sep
                            + "      d" + sep
                            + "   c" + sep
                            + "      e" + sep
                            + "      f" + sep;
            assertEquals(expected, actualOutput);
        }
        finally {
            // Restore normal operation
            System.setOut(originalOut);
        }
    }
    
    private void it(Iterator iter, char expected) {
        assertTrue(iter.hasNext());
        char actual = ((String)((Tree)iter.next()).getValue()).charAt(0);
        assertEquals(expected, actual);
    }
}
