package com.hithomelabs.princeton1.module4;
import java.util.Iterator;
import javax.annotation.Nonnull;

// Concrete implementation of stack using arrays
// Creating a generic stack of type E
public class ArrayStack<E> extends Stack<E> {

    // Capacity and size are two variables, capacity determines total capacity of array, capacity defaults at 10
    // every time size == capacity, capacity = 2 * capacity
    private static final int DEFAULT_CAPACITY = 10;
    private int capacity;
    private int size;
    private E[] arr;

    public ArrayStack(int capacity){
        this.capacity = capacity;
        arr = (E[]) new Object[this.capacity];
    }

    // Constructor chaining, default constructor will call parametrized constructor with default initial capacity 10
    public ArrayStack(){
        this(DEFAULT_CAPACITY);
    }

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

    private void changeCapacity(int newCapacity){
        E[] resizedArr = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++)
            resizedArr[i] = arr[i];
        arr = resizedArr;
    }

    private void incrementSize(){
        if (size == capacity){
            capacity = 2 * capacity;
            changeCapacity(capacity);
        }
    }

    // Push always happens at the end of the stack
    // Say the size of the stack is 1, new element gets inserted  at 1
    @Override
    public void push(E element) {
        // Lazy approach, we assume size to always be lesser than capacity
        incrementSize();
        arr[size++] = element;
    }

    @Override
    public E pop() {
        if (isEmpty())
            return null;
        else{
            E e = arr[--size];
            arr[size] = null;
            checkResize();
            return e;
        }
    }

    private void checkResize() {
        if (size < capacity / 4 && capacity >= 20){
            capacity = capacity / 2;
            changeCapacity(capacity);
        }
    }

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

    @Override
    @Nonnull
    public Iterator<E> iterator() {
        return new Iterator<E>() {

            int current = 0;

            @Override
            public boolean hasNext() {
                return current != size;
            }

            @Override
            public E next() {
                E element = arr[current];
                current = current + 1;
                return element;
            }
        };
    }
}
