package queue;

import java.util.function.Predicate;

/**
 * @author Doschennikov Nikita (me@fymio.us)
 */
public class ArrayQueueModule {
    private static Object[] queue = new Object[4];
    private static int head = 0;
    private static int size = 0;

    public static void enqueue(Object element) {
        assert element != null : "Element must not be null";
        ensureCapacity();
        queue[(head + size) % queue.length] = element;
        size++;
    }

    public static void push(Object element) {
        assert element != null : "Element must not be null";
        ensureCapacity();
        head = (head - 1 + queue.length) % queue.length;
        queue[head] = element;
        size++;
    }

    public static Object element() {
        assert size > 0 : "Queue is empty";
        return queue[head];
    }

    public static Object peek() {
        assert size > 0 : "Queue is empty";
        return queue[(head + size - 1) % queue.length];
    }

    public static Object dequeue() {
        assert size > 0 : "Queue is empty";
        Object result = queue[head];
        queue[head] = null;
        head = (head + 1) % queue.length;
        size--;
        return result;
    }

    public static Object remove() {
        assert size > 0 : "Queue is empty";
        int tail = (head + size - 1) % queue.length;
        Object result = queue[tail];
        queue[tail] = null;
        size--;
        return result;
    }

    public static int size() {
        return size;
    }

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

    public static void clear() {
        queue = new Object[4];
        head = 0;
        size = 0;
    }

    public static int count(Object element) {
        assert element != null : "Element must not be null";
        int cnt = 0;
        for (int i = 0; i < size; i++) {
            if (element.equals(queue[(head + i) % queue.length])) {
                cnt++;
            }
        }
        return cnt;
    }

    public static int countIf(Predicate<Object> predicate) {
        assert predicate != null : "Predicate must not be null";
        int cnt = 0;
        for (int i = 0; i < size; i++) {
            if (predicate.test(queue[(head + i) % queue.length])) {
                cnt++;
            }
        }
        return cnt;
    }

    public static int indexIf(Predicate<Object> predicate) {
        assert predicate != null : "Predicate must not be null";
        for (int i = 0; i < size; i++) {
            if (predicate.test(queue[(head + i) % queue.length])) {
                return i;
            }
        }
        return -1;
    }

    public static int lastIndexIf(Predicate<Object> predicate) {
        assert predicate != null : "Predicate must not be null";
        for (int i = size - 1; i >= 0; i--) {
            if (predicate.test(queue[(head + i) % queue.length])) {
                return i;
            }
        }
        return -1;
    }

    public static int indexOf(Object element) {
        assert element != null : "Predicate must not be null";
        for (int i = 0; i < size; i++) {
            if (queue[(head + i) % queue.length].equals(element)) return i;
        }
        return -1;
    }

    public static int lastIndexOf(Object element) {
        assert element != null : "Predicate must not be null";
        for (int i = size - 1; i >= 0; i--) {
            if (queue[(head + i) % queue.length].equals(element)) return i;
        }
        return -1;
    }

    private static void ensureCapacity() {
        if (size < queue.length) return;
        Object[] newQueue = new Object[queue.length * 2];
        for (int i = 0; i < size; i++) {
            newQueue[i] = queue[(head + i) % queue.length];
        }
        queue = newQueue;
        head = 0;
    }
}