package basictype;

import java.util.Iterator;

/**
 * A bag which uses an array for storing its elements.
 * The array is resized dynamically when it becomes 
 * too small for holding the elements of the bag.
 * 
 * @author Frederic Boulanger frederic.boulanger@supelec.fr
 *
 * @param <T> the type of elements in the bag
 */
public class ArrayBag<T> extends Bag<T> {
  private static final int initCapacity = 5;
  private T[] elements;
  private int size;
  
  //* Create an empty ArrayBag
  public ArrayBag() {
    this.size = 0;
    resize(initCapacity);
  }
  //* Add an item to the bag
  @Override
  public void add(T item) {
    if (this.size == this.elements.length) {
      grow();
    }
    this.elements[this.size++] = item;
  }
    
  //* Get the number of items in the bag
  @Override
  public int size() {
    return this.size;
  }
  
  //* Get an iterator on the bag
  @Override
  public Iterator<T> iterator() {
    return new ArrayBagIterator();
  }

  /**
   * An iterator on an ArrayBag
   */
  private class ArrayBagIterator implements Iterator <T> {
    private int currentIndex = 0;
    
    @Override
    public boolean hasNext() {
      return this.currentIndex < ArrayBag.this.size ;
    }
    
    @Override
    public T next() {
      return ArrayBag.this.elements[this.currentIndex++];
    }

    @Override
    public void remove() {
      // Don't do anything, removing items from a bag is not supported
    }
  }
  
  //* Enlarge the array for storing the elements of the bag
  private void grow() {
    resize(this.elements.length * 2);
  }
  
  //* Resize the array for storing the elements of the bag
  private void resize(int newCapacity) {
    // Create a new array with the new capacity
    // Required annotation because Java erases parameter 
    // types at runtime and can't check the following cast.
    @SuppressWarnings("unchecked") 
    T[] newElements = (T[]) new Object[newCapacity];
    // Copy the elements from the old array to the new one
    for (int i = 0; i < this.size; i++) {
      newElements[i] = this.elements[i];
    }
    // Use the new array as storage
    this.elements = newElements;
  }
  
  private static final String[] defaultArgs = {
    "un", "deux", "trois", "quatre", "cinq",
    "six", "sept", "huit", "neuf", "dix",
    "onze", "douze", "treize", "quatorze", "quinze"
  };
  /**
   * Test function
   */
  public static void main(String[] args) {
    String[] items = args;
    if (items.length == 0) { // No arguments -> use default sample of items
      items = defaultArgs;
    }
    Bag<String> b = new ArrayBag<String>();
    for(String item: items) {
      b.add(item);
    }
    
    System.out.println("# Iterate");
    for(String item : b) {
      System.out.println(item);
    }
  }

}
