package de.renew.util;

import java.io.Serial;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.NoSuchElementException;


/**
 *  <code>Queue</code> implements a simple FIFO-Queue.
 *
 * @author <a href="mailto:roelke@informatik.uni-hamburg.de">Heiko Roelke</a>
 * @version 1.0
 * @since Renew 1.5
 */
public class Queue<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 3620540645530043407L;

    /**
     * A LinkedList which is used to store the elements of the queue in order.
     */
    private final LinkedList<T> _linkedList;

    /**
     * adds an element to the queue
     */
    public Queue() {
        _linkedList = new LinkedList<>();
    }

    /**
     *  <code>enqueue</code> inserts an element at the queue's
     *  end.
     *
     * @param element an <code>Object</code> value
     */
    public void enqueue(T element) {
        synchronized (_linkedList) {
            _linkedList.addLast(element);
            _linkedList.notifyAll();
        }
    }

    /**
     *  <code>dequeue</code> returns oldest element and removes
     *  it from the queue.
     *
     * @return an <code>Object</code> value
     * @exception NoSuchElementException if queue is empty
     */
    public T dequeue() throws NoSuchElementException {
        synchronized (_linkedList) {
            return _linkedList.removeFirst();
        }
    }

    /**
     *  <code>front</code> returns the front element
     *  <em>without</em> removing it from the queue.
     *
     * @return an <code>Object</code> value
     * @exception NoSuchElementException if queue is empty.
     */
    public T front() throws NoSuchElementException {
        synchronized (_linkedList) {
            return _linkedList.getFirst();
        }
    }

    /**
     * Waits until the queue is non-empty, then returns the
     * oldest element and removes it from the queue.
     * <p>
     * If the queue contains an element at the moment this method
     * is called, it will return immediately (as {@link #dequeue})
     * would. But if the queue is empty, this method will block
     * until there is an element.
     * </p>
     * Added 25 Jun 2001 by Michael Duvigneau
     *
     * @return an <code>Object</code> value
     * @exception InterruptedException
     *     if another thread has interrupted the current thread.
     */
    public T waitAndDequeue() throws InterruptedException {
        synchronized (_linkedList) {
            while (_linkedList.isEmpty()) {
                _linkedList.wait();
            }
            return _linkedList.removeFirst();
        }
    }

    /**
     * Tells whether the queue doesn't contain any elements.
     *
     * @return <code>true</code>  if the queue is empty, <br>
     *         <code>false</code> if there is at least one
     *                            element in the queue.
     */
    public boolean isEmpty() {
        return _linkedList.isEmpty();
    }
}