package cpsc2150.MyDeque;

/**
 * Class that implements the IDeque interface using an array.
 *
 * @invariant
 *      length of myQ, |myQ| {@code <} MAX_LENGTH AND
 *      0 {@code <=} myLength {@code <=} MAX_LENGTH
 *
 *
 * @correspondance
 *      length_of_deque = myLength AND
 *      self = myQ[0...myLength-1]
 */
public class ArrayDeque<T> extends AbsDeque<T> implements IDeque<T> {

    // Where the data is stored
    // myQ[0] is the front of the deque
    private T[] myQ;

    // Tracks how many items are in the deque
    // Also used to find the end of the deque
    private int myLength;

    /**
     * Constructor for ArrayDeque that creates an "empty" deque with a maximum length of 100.
     *
     * @post
     *      myLength = 0 AND myQ = Object[MAX_LENGTH]
     */
    @SuppressWarnings("unchecked")
    public ArrayDeque() {

        // Set myLength to 0
        myLength = 0;
        // Initialize myQ as an Object array of size MAX_LENGTH
        myQ = (T[]) new Object[MAX_LENGTH];

    }

    /**
     * Adds x to the end of the deque.
     *
     * @param x Object to be added to end of deque
     *
     * @pre
     *      length of myQ, |myQ| {@code <} MAX_LENGTH AND
     *      0 {@code <=} myLength {@code <} MAX_LENGTH
     *
     * @post
     *      myQ = [ x added to the end of #myQ ] AND
     *      length of myQ, |myQ| = length of #myQ, |#myQ| + 1 AND
     *      myLength = #myLength + 1
     */
    @SuppressWarnings("unchecked")
    public void enqueue(T x) {

        // Create temp array of Objects with size of myLength plus 1
        T[] temp = (T[]) new Object[myLength + 1];

        // Loop through myQ
        for (int i = 0; i < myLength; i++) {

            temp[i] = myQ[i];

        }
        // Set the last value of temp to x
        temp[myLength] = x;

        // Set myQ equal to temp
        myQ = temp;

        // Increment myLength
        myLength++;

    }

    /**
     * Removes and returns the object at the front of the deque.
     *
     * @return Object at the front of deque
     *
     * @pre
     *      length of myQ, |myQ| {@code >} 0 AND
     *      0 {@code <} myLength {@code <=} MAX_LENGTH
     *
     * @post
     *      dequeue = [ object at the front of #myQ ] AND
     *      myQ = [ dequeue removed from the front of #myQ ] AND
     *      length of myQ, |myQ| = length of #myQ, |#myQ| - 1 AND
     *      myLength = #myLength - 1
     */
    @SuppressWarnings("unchecked")
    public T dequeue() {

        // Store the value being removed
        T removed = myQ[0];

        // Create temp array of Objects with size of myLength minus 1
        T[] temp = (T[]) new Object[myLength - 1];

        // Loop through myQ
        for (int i = 1; i < myLength; i++) {

            temp[i - 1] = myQ[i];

        }

        // Set myQ equal to temp
        myQ = temp;

        // Decrement myLength
        myLength--;

        return removed;

    }

    /**
     * Adds x to the front of the deque.
     *
     * @param x Object to be added to front of deque
     *
     * @pre
     *      length of self, |self| {@code <} MAX_LENGTH AND
     *      0 {@code <=} myLength {@code <} MAX_LENGTH
     *
     * @post
     *      myQ = [ x added to the front of #myQ ] AND
     *      length of myQ, |myQ| = length of #myQ, |#myQ| + 1 AND
     *      myLength = #myLength + 1
     */
    @SuppressWarnings("unchecked")
    public void inject(T x) {

        // Create temp array of Objects with size of myLength plus 1
        T[] temp = (T[]) new Object[myLength + 1];

        // Set the first value of temp to x
        temp[0] = x;
        // Loop through myQ
        for (int i = 0; i < myLength; i++) {

            temp[i + 1] = myQ[i];

        }

        // Set myQ equal to temp
        myQ = temp;

        // Increment myLength
        myLength++;

    }

    /**
     * Removes and returns the object at the end of the deque.
     *
     * @return Object at the end of deque
     *
     * @pre
     *      length of self, |self| {@code >} 0 AND
     *      0 {@code <} myLength {@code <=} MAX_LENGTH
     *
     * @post
     *      removeLast = [ object at the end of #myQ ] AND
     *      myQ = [ removeLast removed from the end of #myQ ] AND
     *      length of myQ, |myQ| = length of #myQ, |#myQ| - 1 AND
     *      myLength = #myLength - 1
     */
    @SuppressWarnings("unchecked")
    public T removeLast() {

        // Store the value being removed
        T removed = myQ[myLength - 1];

        // Create temp array of Objects with size of myLength minus 1
        T[] temp = (T[]) new Object[myLength - 1];

        // Loop through myQ minus the last index
        for (int i = 0; i < (myLength - 1); i++) {

            temp[i] = myQ[i];

        }

        // Set myQ equal to temp
        myQ = temp;

        // Decrement myLength
        myLength--;

        return removed;

    }

    /**
     * Returns the number of objects in the deque.
     *
     * @return The number of objects in the deque
     *
     * @post
     *      length = myLength AND myLength = #myLength AND
     *      myQ = #myQ
     */
    public int length() {

        return myLength;

    }

    /**
     * Clears the entire deque.
     *
     * @post
     *      self = {@code <>} AND myLength = #myLength AND
     *      myLength = 0
     */
    @SuppressWarnings("unchecked")
    public void clear() {

        // Create temp array of Objects with size of MAX_LENGTH
        T[] temp = (T[]) new Object[MAX_LENGTH];

        // Set myQ equal to temp
        myQ = temp;

        // Set myLength to 0
        myLength = 0;

    }

}