package cpsc2150.MyDeque;

import java.lang.reflect.Array;

/**
 * A deque containing integers.
 * A deque is a double-ended queue data structure that allows you
 * to insert and remove from both ends.
 * This deque is bound by MAX_LENGTH.
 *
 * Initialization ensures:
 *      Deque contains nothing, i.e., an empty string {@code <>}.
 *
 * Defines:
 *      length_of_deque: Z
 *
 * Constraints:
 *      0 {@code <= } length of a self {@code <=} MAX_LENGTH
 */
public interface IDeque {
    public static final int MAX_LENGTH = 100;

    /**
     * Adds x to the end of the deque.
     *
     * @param x Integer to be added to end of deque
     *
     * @pre
     *      length of self, |self| {@code <} MAX_LENGTH
     *
     * @post
     *      self = [ x added to the end of #self ] AND
     *      length of self, |self| = length of #self, |#self| + 1
     */
    public void enqueue(Integer x);

    /**
     * Removes and returns the integer at the front of the deque.
     *
     * @return Integer at the front of deque
     *
     * @pre
     *      length of self, |self| {@code >} 0
     *
     * @post
     *      dequeue = [ integer at the front of #self ] AND
     *      self = [ dequeue removed from the front of #self ] AND
     *      length of self, |self| = length of #self, |#self| - 1
     */
    public Integer dequeue();

    /**
     * Adds x to the front of the deque.
     *
     * @param x Integer to be added to front of deque
     *
     * @pre
     *      length of self, |self| {@code <} MAX_LENGTH
     *
     * @post
     *      self = [ x added to the front of #self ] AND
     *      length of self, |self| = length of #self, |#self| + 1
     */
    public void inject(Integer x);

    /**
     * Removes and returns the integer at the end of the deque.
     *
     * @return Integer at the end of deque
     *
     * @pre
     *      length of self, |self| {@code >} 0
     *
     * @post
     *      removeLast = [ integer at the end of #self ] AND
     *      self = [ removeLast removed from the end of #self ] AND
     *      length of self, |self| = length of #self, |#self| - 1
     */
    public Integer removeLast();

    /**
     * Returns the number of integers in the deque.
     *
     * @return The number of integers in the deque
     *
     * @post
     *      length = length of self, |self| and self = #self
     */
    public int length();

    /**
     * Clears the entire deque.
     *
     * @post
     *      self = {@code <>}
     */
    public void clear();

    /**
     * Returns the integer number at the front of the deque but does
     * not remove it from the deque.
     *
     * @return The integer at the front of the deque
     *
     * @post
     *      peek = [ integer at the front of #self ] AND
     *      self = #self
     */
    default public Integer peek() {

        Integer peeked = dequeue();

        inject(peeked);

        return peeked;

    }

    /**
     * Returns the integer number at the end of the deque but
     * does not remove it from the deque.
     *
     * @return The integer at the end of the deque
     *
     * @post
     *      endOfDeque = [ integer at the front of #self ] AND
     *      self = #self
     */
    default public Integer endOfDeque() {

        Integer end = removeLast();

        enqueue(end);

        return end;

    }

    /**
     * Inserts x at position pos in the deque.
     * Pos index starts at 1, so the item at the very front of the deque is pos 1.
     *
     * @param x Integer to be inserted
     * @param pos Position to insert x at
     *
     * @pre
     *      pos {@code >=} 1 AND
     *      length of self, |self| {@code <} MAX_LENGTH
     *
     * @post
     *      self = [ x added to #self at position pos ] AND
     *      length of self, |self| = length of #self, |#self| + 1
     */
    default public void insert(Integer x, int pos) {

        // Create array to store dequeued integers
        int removed[] = new int[pos - 1];

        // Loop through deque and dequeue
        for (int i = 0; i < (pos - 1); i++) {

            removed[i] = dequeue();

        }

        // Add new integer to deque
        inject(x);

        // Loop through deque and inject
        for (int i = (pos - 2); i >= 0; i--) {

            inject(removed[i]);

        }

    }

    /**
     * Removes whatever integer number was in position pos in the deque and returns it.
     * Pos index starts at 1, so the item at the very front of the deque is pos 1.
     *
     * @param pos Position to remove an integer at
     * @return Integer removed from position pos
     *
     * @pre
     *      pos {@code >=} 1 AND
     *      length of self, |self| {@code >} 0
     *
     * @post
     *      remove = [ integer in #self removed from position pos ] AND
     *      self = [ remove removed from #self ] AND
     *      length of self, |self| = length of #self, |#self| - 1
     */
    default public Integer remove(int pos) {

        // Create array to store dequeued integers
        int removed[] = new int[pos];

        // Loop through deque and dequeue
        for (int i = 0; i < pos; i++) {

            removed[i] = dequeue();

        }

        // Loop through deque and inject
        for (int i = (pos - 2); i >= 0; i--) {

            inject(removed[i]);

        }

        return removed[pos - 1];

    }

    /**
     * Returns whatever integer number was in position pos in the deque without removing it.
     * Pos index starts at 1, so the item at the very front of the deque is pos 1.
     *
     * @param pos Position to get an integer at
     * @return Integer at position pos
     *
     * @pre
     *      pos {@code >=} 1
     *
     * @post
     *      get = [ integer in #self at position pos ] AND
     *      self = #self
     */
    default public Integer get(int pos) {

        // Create array to store dequeued integers
        int removed[] = new int[pos];

        // Loop through deque and dequeue
        for (int i = 0; i < pos; i++) {

            removed[i] = dequeue();

        }

        // Loop through deque and inject
        for (int i = (pos - 1); i >= 0; i--) {

            inject(removed[i]);

        }

        return removed[pos - 1];

    }

}
