import java.util.ArrayList;

public class BinarySearchTree {

    //------------ TreeNode inner class ------------
    
    private static class TreeNode {
        private int data;
        private TreeNode left; 
        private TreeNode right;
        
        public TreeNode( int d, TreeNode l, TreeNode r ) {
            this.data = d;
            this.left = l;
            this.right = r;
        }
        
        public int totalSum() {
            return this.data + this.left.totalSum() + 
                        this.right.totalSum();
        }
        
        public void printPreOrder() {
            System.out.print( this.data + " " );
            this.left.printPreOrder();
            this.right.printPreOrder();
        }

        public void printInOrder() {
            this.left.printInOrder();
            System.out.print( this.data + " " );
            this.right.printInOrder();
        }

        public void printPostOrder() {
            this.left.printPostOrder();
            this.right.printPostOrder();
            System.out.print( this.data + " " );
        }

        public boolean contains(int x) {
            if ( x == this.data )
                return true;
            else if ( x < this.data )
                return this.left.contains(x);
            else //if ( x > this.data )
                return this.right.contains(x);
        }
        
        // replacing this..
//         public void add(int x) {
//              if( x < this.data )
//                 this.left.add(x);
//              else
//                 this.right.add(x);
//         }
        
        // with this...
        public void add(int x) {
            if( x < data && left == NULLNODE )
                left = new TreeNode(x, NULLNODE, NULLNODE);
            else if( x < data ) // && left != NULLNODE
                left.add(x);
            else if( right == NULLNODE ) // && x >= data
                right = new TreeNode(x, NULLNODE, NULLNODE);
            else //if x >= data && right != NULLNODE 
                right.add(x);
        }
        
        public TreeNode betterAdd( int x ) {
            if (x < data) {
                this.left = this.left.betterAdd( x );
            }
            else {
                this.right = this.right.betterAdd( x );
            }
            return this;
        }
        
    } // end TreeNode class
    
    //------------ NullTreeNode inner class ---------------
    
    private static class NullTreeNode extends TreeNode {
        public NullTreeNode() { super( 0, null, null ); }
        
        @Override
        public int totalSum() { return 0; }
        
        @Override
        public void printPreOrder() { }

        @Override
        public void printInOrder() { }

        @Override
        public void printPostOrder() { }

        public boolean contains(int x) {
            return false;
        }
        
        public void add(int x) {
            System.out.println( "oops, you can't have a child of a Null node!" );
        }
        
        public TreeNode betterAdd( int x ) {
            return new TreeNode( x, NULLNODE, NULLNODE );
        }
    } // end NullTreeNode class
    
    //-------------------------------------------------
    
    private TreeNode root;
    private static final NullTreeNode NULLNODE = new NullTreeNode();
    
    public BinarySearchTree() {
        this.root = NULLNODE;
    }
    
    public int totalSum() {
        return this.root.totalSum();
    }    
    
    public void printPreOrder() {
        this.root.printPreOrder();
    }    

    public void printInOrder() {
        this.root.printInOrder();
    }    
        
    public void printPostOrder() {
        this.root.printPostOrder();
    }    
    
    public boolean contains(int x) {
        return this.root.contains(x);
    }
    
    public void add(int x) {
        if ( this.root == NULLNODE )
            this.root = new TreeNode(x, NULLNODE, NULLNODE);
        else
            this.root.add(x);
    }
    
    public void betterAdd( int x ) {
        this.root = this.root.betterAdd( x );
    }
    
    
    //----------------- unit test code -------------------
    
    public static void main( String[] args ) {
        BinarySearchTree tree = new BinarySearchTree();
        tree.add(9);
        tree.add(10);
        tree.add(8);
        tree.add(5);
        tree.add(6);
        tree.add(7);
        tree.add(3);
        tree.printInOrder();
        System.out.println( "total sum = " + tree.totalSum() ); 
        System.out.println( "contains 4: " + tree.contains(4) );
        System.out.println( "contains 3: " + tree.contains(3) );

        tree = new BinarySearchTree();
        tree.betterAdd(9);
        tree.betterAdd(10);
        tree.betterAdd(8);
        tree.betterAdd(5);
        tree.betterAdd(6);
        tree.betterAdd(7);
        tree.betterAdd(3);
        tree.printInOrder();
        System.out.println( "total sum = " + tree.totalSum() ); 
        System.out.println( "contains 4: " + tree.contains(4) );
        System.out.println( "contains 3: " + tree.contains(3) );

    }
}

// Methods to add for the TreeSorter.
// TREENODE        
//         
//         public void addToList(ArrayList<Integer> list) {
//             this.left.addToList(list);
//             list.add(this.data);
//             this.right.addToList(list);
//         }

// NULLTREENODE
//         
//         public void addToList(ArrayList<Integer> list) { }

// BINARYSEARCHTREE
//     
//     public void addToList(ArrayList<Integer> list) {
//         this.root.addToList(list);
//     }
