package list;

import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public abstract class AbstractLinkedList<E> implements Iterable<E> {

    public AbstractLinkedList() {
    }
    
    //--------------------- Basic list interface, used by ListIteratorImpl

    public abstract int size();
    public abstract E get(int index);
    public abstract E set(int index, E element);
    public abstract void add(int index, E element);
    public abstract E remove(int index);

    //--------------------- Iterator methods

    public Iterator<E> iterator() {
        return new list.ListIteratorImpl<E>(this, 0);
    }

    public ListIterator<E> listIterator() {
        return new list.ListIteratorImpl<E>(this, 0);
    }

    public ListIterator<E> listIterator(int index) {
        return new list.ListIteratorImpl<E>(this, index);
    }

    //--------------------- Implementation of equals
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[ ");
        for(int i=0; i<size(); i++) {
            if (i>0) {
                sb.append(", ");
            }
            sb.append(get(i));
        }
        sb.append(" ]");
        return sb.toString();
    }
    
    /**
     * Almost directly borrowed from {@link AbstractList}. The contract of
     * equals() says that, whenever you override this method, you also have to
     * override hashCode().
     * <p>
     * Note: equals() is especially important for unit tests!
     */
    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof List)) {
            return false;
        }

        ListIterator<E> e1 = listIterator();
        ListIterator e2 = ((List) o).listIterator();
        while(e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (! safeEquals(o1, o2)) {
                return false;
            }
        }
        return !e1.hasNext() && !e2.hasNext();
    }

    /**
     * Almost directly borrowed from {@link AbstractList}.
     */
    @Override
    public int hashCode() {
        int hashCode = 1;
        for(Iterator<E> iter = iterator(); iter.hasNext(); ) {
            E obj = iter.next();
            hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
        }
        return hashCode;
    }
    
    //--------------------- Helper methods
    
    /**
     * This method will come in handy in subclass {@link SimpleLinkedList}.
     */
    protected abstract ListElementContainer<E> getContainer(int index);
    
    /**
     * Binary method for safely comparing two objects, even if one of them is
     * null.
     */
    public static <T> boolean safeEquals(T left, T right) {
        return (left == null && right == null) || (left != null && left.equals(right));
    }
}