/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.util;


/**
 * A simple variable length first-in first-out queue.
 */

public class Fifo
{
    /**
     * Create a buffer with a default initial size.
     */
    public Fifo() {
        this(defaultInitialSlots);
    }

    /**
     * Create a buffer with a specified initial size.
     *
     * @param initialSlots      The number of initial slots in the buffer; the number of
     *                          slots required by the buffer will be expanded as required.
     */
    public Fifo(int initialSlots) {
        bufSize = initialSlots;
        buf = new Object[bufSize];
        insertSlot = 0;
        removeSlot = 0;
        entries = 0;
    }

    /**
     * Check if the buffer has an entries or not.
     *
     * @return                  true if the buffer has no entries, and false otherwise.
     */
    public synchronized boolean isEmpty() {
        return (entries == 0);
    }

    /**
     * Return the number of entries currently in the fifo.
     *
     * @return                  The number of entries currently in the fifo
     */
    public synchronized int size() {
        return entries;
    }

    /**
     * Insert an entry into the buffer. The buffer will be increased in size if necessary
     * to accomodate the new entry.
     *
     * @param obj               The object to be inserted.  It must not be null.
     */
    public synchronized void insert(Object obj) {
        if (obj == null)
            throw new NullPointerException();

        if (entries == bufSize) {
            int newBufSize = 2 * bufSize;
            Object[] newBuf = new Object[newBufSize];
            int saveEntries = entries;
            for (int i = 0; entries > 0; i++) {
                newBuf[i] = remove();
            }
            entries = saveEntries;
            buf = newBuf;
            bufSize = newBufSize;
            removeSlot = 0;
            insertSlot = entries;
        }

        buf[insertSlot] = obj;
        insertSlot = (insertSlot + 1) % bufSize;
        entries++;
    }

    /**
     * Remove an entry from the buffer if one is available.
     *
     * @return                  The next object in line to be removed, if one is available,
     *                          or null if none are available.
     */
    public synchronized Object remove() {
        if (entries == 0)
            return null;

        Object o = buf[removeSlot];
        buf[removeSlot] = null;
        removeSlot = (removeSlot + 1) % bufSize;
        entries--;

        return o;
    }

    /**
     * Flush all entries from the buffer.
     */
    public synchronized void flush() {
        insertSlot = 0;
        removeSlot = 0;
        entries = 0;
        for (int i = 0; i < buf.length; i++)
            buf[i] = null;
    }


    //----------Data members---------------------------------------------------------

    private final static int defaultInitialSlots = 16;

    private Object[] buf;               // The circular array to hold the entries
    private int bufSize;                // The size of the array: buf.length
    private int insertSlot;             // The next slot to store an entry
    private int removeSlot;             // The next slot from which to remove an entry
    private int entries;                // The number of entries in the array

}
