package list;

/**
 * Implementation of List using Java array
 * @author Winston Prakash
 */
public class ArrayList<E> implements List<E> {
    private int arraySize;
    
    private transient E[] arrayData;
    
    public ArrayList(){
        this(11);
    }
    
    public ArrayList(int initialCapacity){
        arrayData = (E[]) new Object[initialCapacity];
    }
    
    public void append(E item) {
        ensureCapacity();
        arrayData[arraySize++] = item;
    }
    
    public void insert(int index, E item) {
        arraySize++;
        ensureCapacity();
        System.arraycopy(arrayData, index, arrayData, index + 1, arraySize - index);
        arrayData[index] = item;
    }
    
    public boolean remove(Object item) {
        int index = indexOf(item);
        if (index >= 0){
            removeAt(index);
            return true;
        }
        return false;
    }
    
    /**
     * Will conflict with remove(Object) if the object is Integer
     * So better to use removeAt rather than remove(int)
     */
    public void removeAt(int index) {
        checkRange(index);
        System.arraycopy(arrayData, index + 1, arrayData, index, arraySize - index - 1);
        arrayData[--arraySize] = null;
    }
    
    public int indexOf(Object item){
        if (item != null){
            for (int i = 0; i < arraySize; i++){
                if (item.equals(arrayData[i])){
                    return i;
                }
            }
        }
        return -1;
    }
    
    public boolean contains(E item) {
        return indexOf(item) >= 0;
    }
    
    public E get(int index) {
        checkRange(index);
        return (E) arrayData[index];
    }
    
    public void set(int index, E item) {
        checkRange(index);
        arrayData[index] = item;
    }
    
    public int size() {
        return arraySize;
    }
    
    public boolean isEmpty() {
        return arraySize == 0;
    }
    
    public void display(){
        for(int i=0; i< arraySize; i++){
            System.out.print(arrayData[i] + "(");
            System.out.print(i + ") ");
        }
    }
    
    private void checkRange(int index){
        if ((index < 0) || (index >= arraySize)){
            throw new IndexOutOfBoundsException("Index: " + index + "Size: " + arraySize);
        }
    }
    
    private void ensureCapacity(){
        if (arraySize > arrayData.length - 1){
            Object[] oldData = arrayData;
            arrayData = (E[]) new Object[oldData.length * 2];
            System.arraycopy(oldData, 0, arrayData, 0, oldData.length);
        }
    }

    public List.Iterator<E> iterator() {
        return new ListIterator<E>();
    }
    
    private class ListIterator<E> implements  List.Iterator<E>{
        int index = 0;
        public boolean hasNext() {
            return index != arraySize;
        }

        public E next() {
            return (E) arrayData[index++];
        }

        public void remove() {
           // TODO check for concurrent modification
           ArrayList.this.removeAt(index--); 
        }
        
    }
}