import java.util.Iterator;

/**
 * A simplified version of the generic ArrayList class.
 *   @author Dave Reed
 *   @version 9/2/13
 */
public class MyArrayList<E> implements Iterable<E> {
    private static final int INIT_SIZE = 10;
    private E[] items;
    private int numStored;
    
    /**
     * Constructs a list using the default capacity.
     */
    public MyArrayList() {
        this(MyArrayList.INIT_SIZE);
    }
    
    /**
     * Constructs a list with the specified capacity
     *   @param capacity the initial capacity of the list
     */
    public MyArrayList(int capacity) {
        this.items = (E[]) new Object[capacity];
        this.numStored = 0;
    }
    
    /**
     * Determines the size of the list.
     *   @return the number of values currently stored
     */
    public int size() {
        return this.numStored;
    }
    
    /**
     * Adds an item at a specified index.
     *   @param index the index where the item is to be added.
     *   @param newItem the item being added
     */
    public void add(int index, E newItem) {
        this.rangeCheck(index, "ArrayList add()", numStored);
        
        if (this.numStored == this.items.length) {
            E[] temp = (E[]) new Object[2*items.length];
            for (int i = 0; i < this.items.length; i++) {
                temp[i] = this.items[i];
            }
            this.items = temp;
        }

        for (int i = this.numStored; i > index; i--) {
            this.items[i] = this.items[i-1];
        }
        this.items[index] = newItem;
        this.numStored++;
    }
    
    /**
     * Adds an item at the end of the list.
     *   @param newItem the item being added
     *   @return true if successfully added
     */
    public boolean add(E newItem) {
        this.add(this.numStored, newItem);
        return true;
    }
    
    /**
     * Gets the item stored at the specified index.
     *   @param index  the list index
     *   @return the item stored at that index
     */
    public E get(int index) {
        this.rangeCheck(index, "ArrayList get()", numStored-1);
        return items[index];
    }
    
    /**
     * Finds the index of an item in the list.
     *   @param oldItem the item being searched for
     *   @return the first occurrence of oldItem in the list
     */
    public int indexOf(E oldItem) {
        for (int i = 0; i < this.size(); i++) {
            if (oldItem.equals(this.items[i])) {
                return i;
            }
        }  
        return -1;
    }
      
    /**
     * Determines whether the specified item is contained in the list.
     *   @param oldItem the item being searched for
     *   @return true if the item appears in the list, else false
     */
    public boolean contains(E oldItem) {
        return (this.indexOf(oldItem) >= 0);
    }
    
    /**
     * Removes the item at the specified index.
     *   @param index the index of the item being removed
     */
    public void remove(int index) {
        this.rangeCheck(index, "ArrayList remove()", numStored-1);
        
        for (int i = index; i < this.numStored-1; i++) {
            this.items[i] = this.items[i+1];
        }
        this.numStored--;
    }
     
    /**
     * Removes the first occurrence of the specified item.
     *   @param oldItem the item being removed
     *   @return true if the item was found and removed, else false
     */
    public boolean remove(E oldItem) {
        int index = this.indexOf(oldItem);
        if (index >= 0) {
            this.remove(index);
            return true;
        }
        return false;
    }
     
    /**
     * Creates an iterator over the list.
     *   @return the list iterator
     */
    public Iterator<E> iterator() {
         return new ArrayIterator();
    }
    
    /////////////////////////////////////////////////////////////////////
    
    /**
     * Private helper method that determines whether a index is in range.
     *   @param index the index that is being accessed
     *   @param msg a text message that describes the context of the access
     *   @param upperBound the upper bound for accessing the list
     */
    private void rangeCheck(int index, String msg, int upperBound)  {
        if (index < 0 || index > upperBound)
            throw new IndexOutOfBoundsException("\n" + msg + ": index " 
                    + index + " out of bounds. Should be in the range 0 to " +
                    upperBound);
    }
    
    /**
     * Private inner class for defining an iterator.
     */
    private class ArrayIterator implements Iterator<E> {
        private int nextIndex;
        public ArrayIterator() {
            this.nextIndex = 0;
        }

        public boolean hasNext() {
            return this.nextIndex < MyArrayList.this.size();
        }

        public E next() {
            this.nextIndex++;
            return MyArrayList.this.get(nextIndex-1);
        }

        public void remove() {
           if (this.nextIndex <= 0) {
               throw new RuntimeException("Iterator call " +
                                           "to next() required before " +
                                           "calling remove()");
           }
           MyArrayList.this.remove(this.nextIndex-1);
       }
    }
}
