package cs211.examples;

import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;

public class ArrayList<E> implements List<E> {
	private final static int DEFAULT_CAPACITY=30;
	private E[] _array;
	private int _size;
	
	public ArrayList(){
		this(DEFAULT_CAPACITY);
	}
	
	
	@SuppressWarnings("unchecked")
	public ArrayList(int initialCapacity){
		_array = (E[])new Object[initialCapacity];
		_size = 0;
	}
	
	/**
	 * Appends the specified element to the end of the list.
	 * 
	 * @param item the element to be added to the list
	 */
	@Override
	public void add(E item){
		ensureCapacity(_size+1);
		_array[_size] = item;
		_size += 1;
	}
	

	/**
	 * Ensures that there are at least {@code capacity} spaces in the underlying array.
	 *
	 * This actually adds 1.5 times the amount of room requested to reduce the number of 
	 * requests that need to be made.
	 *
	 * @param capacity the minimum amount of space that this structure should have available
	 */
	@SuppressWarnings("unchecked")
	public void ensureCapacity(int capacity){
		if (capacity > _array.length){
			E[] tmp = (E[])new Object[capacity * 3 / 2 + 1];
			
			for (int i = 0; i < _size;i++){
				tmp[i] = _array[i];
			}
			_array = tmp;
		}
	}
	
	
	
	/**
	 * Appends the specified element at the specified position.
	 * 
	 * This does not replace any existing elements at this location. 
	 * This shifts any current element and all subsequent elements 
	 * one position to the right.
	 * 
	 * @param index index at which the specified element is to be inserted
	 * @param item the element to be inserted
	 * @throws IndexOutOfBoundsException if the index is out of range (index <0 || index >size())
	 */
	@Override
	public void add(int index, E item){
		if (index <0 || index >size()){
			throw new IndexOutOfBoundsException();
		}
		
		
		ensureCapacity(_size+1);
		
		for (int i = _size; i > index; i--){
			_array[i] = _array[i-1];
		}
		_array[index] = item;
		_size++;
	}
	
	/**
	 * Empty all items from the list.
	 */
	@Override
	public void clear(){
		
	}
	
	/**
	 * Returns {@code true} if this list contains the specified element.
	 * 
	 * More formally, returns {@code true} if and only if this list contains
	 * at least one element {@code e} such that {@code (item == null) ? e == null : item.equals(e)}.
	 * 
	 * @param item element whose presence in this list is being tested
	 * @return {@code true} if the list contains the specified item
	 */
	@Override
	public boolean contains(E item){
		return false;
	}
	
	/**
	 * Returns the element at the specified position in this list
	 * 
	 * @param index index of the element to return
	 * @return the element at the specified position
	 * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= size())
	 */
	@Override
	public E get(int index){
		if (index < 0 || index >= size()){
			throw new IndexOutOfBoundsException(index + " is not a valid index");
		}
		return _array[index];
	}
	
	/**
	 * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
	 * 
	 * More formally, returns the lowest index {@code i} such that {@code (o==null ? get(i)==null : o.equals(get(i)))},
	 *  or -1 if there is no such index.
	 *  
	 * @param item the element to search for
	 * @return the index of the first occurrence of the specified element in this list, or -1 if the list does not contain the element.
	 */
	@Override
	public int indexOf(E item){
		return -1;
	}
	
	/**
	 * Removes the element at the specified position in this list.
	 *  
	 * Shifts any subsequent elements to the left (subtracts one from their indices). 
	 * Returns the element that was removed from the list.
	 * 
	 * @param index the index of the element to be removed
	 * @return the element previously at the specified location
	 * @throws IndexOutOfBoundsException if the index is out of range {@code (index < 0 || index >= size())}
	 */
	@Override
	public E remove(int index){
		return null;
	}
	
	/**
	 * Replaces the element at the specified position with the specified element.
	 * 
	 * @param index index of the element to replace
	 * @param item element to be stored at the specified position
	 * @throws IndexOutOfBoundsException if the index is out of the range {@code (index < 0 || index >= size())
	 */
	@Override
	public void set(int i, E item){
		
	}
	
	
	/**
	 * Returns the number of elements in this list.
	 * 
	 * @return the number of elements in the list
	 */
	@Override
	public int size(){
		return _size;
	}
	
	/**
	 * Returns an iterator over the elements in this list in proper sequence.
	 * 
	 * @return an iterator over the elements in this list in proper sequence
	 */
	@Override
	public Iterator<E> iterator(){
		return listIterator();
	}
	
	/**
	 * Returns a list iterator over the elements in this list in proper sequence.
	 * 
	 * @return a list iterator over the elements in this list in proper sequence
	 */
	@Override
	public ListIterator<E> listIterator(){
		return new ArrayListIterator();
	}


	private class ArrayListIterator implements ListIterator<E>{
		// the cursor is the index of the element that will returned by the next call to {@code next()}
		private int _cursor;
		
		public ArrayListIterator(){
			_cursor = 0;
		}

		
		/**
		 * Inserts the specified element into the list (optional operation). The element is inserted 
		 * immediately before the element that would be returned by {@ code next()}, if any, and after the 
		 * element that would be returned by {@code previous()}, if any. (If the list contains no 
		 * elements, the new element becomes the sole element on the list.) The new element is inserted 
		 * before the implicit cursor: a subsequent call to {@code next} would be unaffected, and a 
		 * subsequent call to {@code previous} would return the new element. (This call increases by 
		 * one the value that would be returned by a call to {@code nextIndex} or {@code previousIndex}.)
		 * 
		 * @param item the item to insert
		 */
		@Override
		public void add(E item) {
			ArrayList.this.add(_cursor, item);
			_cursor++;
			
		}

		/**
		 * Returns {@code true} if this list iterator has more elements when traversing the list in 
		 * the forward direction. (In other words, returns {@code true} if {@code next()} would return 
		 * an element rather than throwing an exception.)
		 * 
		 * @return {@code true} if the list iterator has more elements when traversing the list in the forward direction
		 */
		@Override
		public boolean hasNext() {
			return _cursor < _size;
		}

		/**
		 * Returns true if this list iterator has more elements when traversing the list in the 
		 * reverse direction. (In other words, returns {@code true} if {@code previous()} would 
		 * return an element rather than throwing an exception.)
		 * 
		 * @return {@code true} if the list iterator has more elements when traversing the list in the reverse direction
		 */
		@Override
		public boolean hasPrevious() {
			return _cursor > 0;
		}

		/**
		 * Returns the next element in the list and advances the cursor position. This method may be called 
		 * repeatedly to iterate through the list, or intermixed with calls to {@code previous()} to go back and forth. 
		 * (Note that alternating calls to {@code next} and {@code previous} will return the same element repeatedly.)
		 * 
		 * @return the next element in the list
		 * @throws NoSuchElementException - if the iteration has no next element
		 */
		@Override
		public E next() {
			if (_cursor < _size){
				return _array[_cursor++];
			}else{
				throw new NoSuchElementException();
			}
		}

		/**
		 * Returns the index of the element that would be returned by a subsequent call to {@code next()}. (Returns 
		 * list size if the list iterator is at the end of the list.)
		 */
		@Override
		public int nextIndex() {
			return _cursor;
		}

		/**
		 * Returns the previous element in the list and moves the cursor position backwards. This method may be 
		 * called repeatedly to iterate through the list backwards, or intermixed with calls to {@code next()} to go 
		 * back and forth. (Note that alternating calls to {@code next} and {@code previous} will return the same 
		 * element repeatedly.)
		 * 
		 * @return the previous element in the list
		 * @throws NoSuchElementException - if the iteration has no previous element
		 */
		@Override
		public E previous() {
			if (_cursor > 0){
				return _array[--_cursor];
			}else{
				throw new NoSuchElementException();
			}
		}

		/**
		 * Returns the index of the element that would be returned by a subsequent call to {@code previous()}. 
		 * (Returns -1 if the list iterator is at the beginning of the list.)
		 * 
		 * @return the index of the element that would be returned by a subsequent call to previous, or -1 if the list iterator is at the beginning of the list
		 */
		@Override
		public int previousIndex() {
			return _cursor - 1;
		}

		/**
		 * Removes from the list the last element that was returned by {@code next()} or {@code previous()} 
		 * (optional operation). This call can only be made once per call to {@code next} or {@code previous}. It can 
		 * be made only if {@code add(E)} has not been called after the last call to {@code next} or {@code previous}.
		 * 
		 * @throws IllegalStateException - if neither {@code next} nor {@code previous} have been called, or {@code remove}
		 *  or {@code add} have been called after the last call to {@code next} or {@code previous}
		 */
		@Override
		public void remove() {
			// TODO Auto-generated method stub
			
		}

		/**
		 * Replaces the last element returned by {@code next()} or {@code previous()} with the specified element 
		 * (optional operation). This call can be made only if neither {@code remove()} nor {@code add(E)} have 
		 * been called after the last call to {@code next} or {@code previous}.
		 * 
		 * @param the element with which to replace the last element returned by {@code next} or {@code previous}
		 * @throws IllegalStateException - if neither {@code next} nor {@code previous} have been called, or 
		 * {@code remove} or {@code add} have been called after the last call to {@code next} or {@code previous}
		 */
		@Override
		public void set(E e) {
			// TODO Auto-generated method stub
			
		}
		
	}
	
}