package com.allendowney.thinkdast;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * @author downey
 * @param <T>
 *
 */
public class MyArrayList<T> implements List<T> {
   int size;            // przechowuje bieżącą liczbę elementów
   private T[] array;   // przechowuje elementy

   /**
    *
    */
   @SuppressWarnings("unchecked")
   public MyArrayList() {
      // Nie możesz utworzyć egzemplarza tablicy T[], ale możesz utworzyć egzemplarz tablicy
      // obiektów klasy Object, a następnie dokonać odpowiedniego rzutowania. Szczegóły na ten
      // temat: http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html
      array = (T[]) new Object[10];
      size = 0;
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      // wykonuje kilka prostych testów
      MyArrayList<Integer> mal = new MyArrayList<Integer>();
      mal.add(1);
      mal.add(2);
      mal.add(3);
      System.out.println(Arrays.toString(mal.toArray()) + " rozmiar = " + mal.size);

      mal.remove(new Integer(2));
      System.out.println(Arrays.toString(mal.toArray()) + " rozmiar = " + mal.size);
   }

   @Override
   public boolean add(T element) {
      if (size >= array.length) {
         // powiększ tablicę i skopiuj do niej elementy
         @SuppressWarnings("unchecked")
         T[] bigger = (T[]) new Object[array.length * 2];
         System.arraycopy(array, 0, bigger, 0, array.length);
         array = bigger;
      }
      array[size] = element;
      size++;
      return true;
   }

   @Override
   public void add(int index, T element) {
      // TODO: UZUPEŁNIJ TEN KOD!
   }

   @Override
   public boolean addAll(Collection<? extends T> collection) {
      boolean flag = true;
      for (T element: collection) {
         flag &= add(element);
      }
      return flag;
   }

   @Override
   public boolean addAll(int index, Collection<? extends T> collection) {
      throw new UnsupportedOperationException();
   }

   @Override
   public void clear() {
      // Uwaga: ta wersja tak naprawdę nie ustawia referencji w tablicy
      // na wartość null, dlatego może opóźniać oczyszczanie pamięci.
      size = 0;
   }

   @Override
   public boolean contains(Object obj) {
      return indexOf(obj) != -1;
   }

   @Override
   public boolean containsAll(Collection<?> collection) {
      for (Object element: collection) {
         if (!contains(element)) {
            return false;
         }
      }
      return true;
   }

   @Override
   public T get(int index) {
      if (index < 0 || index >= size) {
         throw new IndexOutOfBoundsException();
      }
      return array[index];
   }

   @Override
   public int indexOf(Object target) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return -1;
   }

   /** Sprawdza, czy element tablicy jest podanym elementem.
    *
    * Obsługuje szczególny przypadek, gdy podany element ma wartość null.
    *
    * @param target
    * @param object
    */
   private boolean equals(Object target, Object element) {
      if (target == null) {
         return element == null;
      } else {
         return target.equals(element);
      }
   }

   @Override
   public boolean isEmpty() {
      return size == 0;
   }

   @Override
   public Iterator<T> iterator() {
      // utwórz kopię tablicy
      T[] copy = Arrays.copyOf(array, size);
      // utwórz listę i zwróć iterator
      return Arrays.asList(copy).iterator();
   }

   @Override
   public int lastIndexOf(Object target) {
      // patrz komentarz do metody indexOf
      for (int i = size-1; i>=0; i--) {
         if (equals(target, array[i])) {
            return i;
         }
      }
      return -1;
   }

   @Override
   public ListIterator<T> listIterator() {
      // utwórz kopię tablicy
      T[] copy = Arrays.copyOf(array, size);
      // utwórz listę i zwróć iterator
      return Arrays.asList(copy).listIterator();
   }

   @Override
   public ListIterator<T> listIterator(int index) {
      // utwórz kopię tablicy
      T[] copy = Arrays.copyOf(array, size);
      // utwórz listę i zwróć iterator
      return Arrays.asList(copy).listIterator(index);
   }

   @Override
   public boolean remove(Object obj) {
      int index = indexOf(obj);
      if (index == -1) {
         return false;
      }
      remove(index);
      return true;
   }

   @Override
   public T remove(int index) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   @Override
   public boolean removeAll(Collection<?> collection) {
      boolean flag = true;
      for (Object obj: collection) {
         flag &= remove(obj);
      }
      return flag;
   }

   @Override
   public boolean retainAll(Collection<?> collection) {
      throw new UnsupportedOperationException();
   }

   @Override
   public T set(int index, T element) {
      // TODO: UZUPEŁNIJ TEN KOD!
      return null;
   }

   @Override
   public int size() {
      return size;
   }

   @Override
   public List<T> subList(int fromIndex, int toIndex) {
      if (fromIndex < 0 || toIndex >= size || fromIndex > toIndex) {
         throw new IndexOutOfBoundsException();
      }
      T[] copy = Arrays.copyOfRange(array, fromIndex, toIndex);
      return Arrays.asList(copy);
   }

   @Override
   public Object[] toArray() {
      return Arrays.copyOf(array, size);
   }

   @Override
   public <U> U[] toArray(U[] array) {
      throw new UnsupportedOperationException();
   }
}
