// Definition of a Scheme-like list class

import ListNode;
import java.lang.NullPointerException;
import SimpleOutput;

public class ListLikeScheme {
    protected ListNode first;

    // Constructor
    public ListLikeScheme () {
        first = null;
    }

    // observers
    public boolean isNull() {
        return first == null;
    }

    public int length () {
        // an iterative method to find the length of a list
        int i = 0;
        ListNode ptr = first;
        while (ptr != null) {
            i++;
            ptr = ptr.getNext();
        }
        return i;
    }

    public String toString() {
        String temp;
        if (first == null)
            temp = "nil";
        else {
            temp = "(" + first.getData();
            for (ListNode ptr=first.getNext(); ptr!=null; ptr=ptr.getNext())
                temp = temp + "  " + ptr.getData();
            temp = temp + ")";
        }
        return temp;
    }
        

    public int size () {
        // a recursive method to find the length of a list
        // recursive algorithm uses a husk-and-kernel approach
        return sizeKernel (first, 0);
    }

    private int sizeKernel (ListNode ptr, int count) {
        // a private, recursive method for finding the length of a list
        if (ptr == null)
            return count;
        else return sizeKernel (ptr.getNext(), (count + 1));
    }

    // extractors
    public Object car () {
        if (isNull())
            throw new NullPointerException("car applied to null list");
        else return first.getData();
    }

    public ListLikeScheme cdr () {
        if (isNull())
            throw new NullPointerException("cdr applied to null list");
        else { 
            ListLikeScheme temp = new ListLikeScheme ();
            temp.first = first.getNext();
            return temp;
        }
    }
    
    // modifiers
    public void cons (Object newData, ListLikeScheme rest) {
        // a first approach, reusing previous nodes
        first = new ListNode (newData, rest.first);
    }

    public static void main (String argv[]) {
        // testing method for list class
        SimpleOutput out = new SimpleOutput();

        // construct the list (nodeA nodeB nodeC) in four steps
        ListLikeScheme A = new ListLikeScheme();
        ListLikeScheme B = new ListLikeScheme();
        ListLikeScheme C = new ListLikeScheme();
        ListLikeScheme D = new ListLikeScheme();

        C.cons ("nodeC", D);
        B.cons ("nodeB", C);
        A.cons ("nodeA", B);
        
        // check lists and operations
        try {
        out.println ("List A:  " + A);
        out.println ("      length/size:   " + A.length() + "\t" + A.size());
        out.println ("      null?:    " + A.isNull());
        out.println ("      car:      " + A.car());
        out.println ("      cdr:      " + A.cdr());

        out.println ("List B:  " + B);
        out.println ("      length/size:   " + B.length() + "\t" + B.size());
        out.println ("      null?:    " + B.isNull());
        out.println ("      car:      " + B.car());
        out.println ("      cdr:      " + B.cdr());

        out.println ("List C:  " + C);
        out.println ("      length/size:   " + C.length() + "\t" + C.size());
        out.println ("      null?:    " + C.isNull());
        out.println ("      car:      " + C.car());
        out.println ("      cdr:      " + C.cdr());

        out.println ("List D:  " + D);
        out.println ("      length/size:   " + D.length() + "\t" + D.size());
        out.println ("      null?:    " + D.isNull());
        out.println ("      car:      " + D.car());
        out.println ("      cdr:      " + D.cdr());
        } catch (NullPointerException e) {
            out.println (e);
        }
        try {
        out.println ("      cdr:      " + D.cdr());
        } catch (NullPointerException e) {
            out.println (e);
        }
    }

}
