/*****************************************************************************/
/*                                                                           */
/*                  Queue (Bounded Buffer) Test Program                      */
/*                                                                           */
/*                     January 1999, Toshimi Minoura                         */
/*                     October 1999, Jacob Lundberg                          */
/*                                                                           */
/*****************************************************************************/

// Objects are enqueued at the tail of a BoundedBuffer,
// and they are dequeued from its head.

// Or alternatively, they are prequeued (addHead) at its head,
// and premoved (removeTail) at its tail.
 
class BoundedBuffer {
  static final int BufferSize = 5;  // constant defining size of this buffer
  private Object data[];            // storage for data queued
  private int headIdx;              // index to head of queued data
  private int tailIdx;              // index to tail of queued data
  private int nElements;            // number of elements in this buffer

  public BoundedBuffer() {              // constructor
    data = new Object[BufferSize];      // create array for Objects
    for (int i = 0; i < BufferSize; i++) {
      data[i] = null;
    }
    headIdx = 0;                        // position of data[0]
    tailIdx = BufferSize - 1;           // position preceding data[0]
    nElements = 0;                      // initially, no elements
  }

  public void enqueue(Object x) {
    if (++tailIdx == BufferSize) tailIdx = 0;   // end of buffer, wrap around
    System.out.println("Object " + x + " enqueued in data[" + tailIdx + "]");
    data[tailIdx] = x;                          // store argument object
    ++nElements;                                // one element added
  }

  public void addHead(Object x) { 
    if (--headIdx < 0) headIdx = BufferSize - 1;  // front of buffer; wrap
    System.out.println("Object " + x + " prequeued in data[" + headIdx + "]");
    data[headIdx] = x;                            // store argument object
    ++nElements;                                  // one element added
  }

  public Object dequeue() {
    Object temp = data[headIdx];                // object to be returned
    data[headIdx] = null;                       // no object in this slot
    System.out.println("Object " + temp + " dequeued from data[" + headIdx + "]");
    if (++headIdx == BufferSize) headIdx = 0;   // wrap around
    --nElements;                                // one element removed
    return temp;
  }

  public Object removeTail() { 
    Object temp = data[tailIdx];                // object to be returned
    data[tailIdx] = null;                       // no object in this slot
    System.out.println("Object " + temp + " premoved from data[" + tailIdx + "]");
    if (--tailIdx < 0) tailIdx = BufferSize - 1;  // wrap around
    --nElements;
    return temp;
  }

  public void dataPrint() {                    // print stack content
     System.out.print("BoundedBuffer: headIdx = " + headIdx +
                                   ", tailIdx = " + tailIdx + ", data = ");
    for (int i = 0; i < BufferSize; i++) {
      System.out.print(data[i]);    // print every value in data[]
      if (i < BufferSize - 1) System.out.print(", ");
    }
    System.out.println("");
  }
}

class Queue {
  public static void main(String argv[]) {          
    System.out.println("main() started");

    BoundedBuffer queue = new BoundedBuffer();

    Integer item = new Integer(201);         // create new item
    queue.enqueue(item);                     // enqueue item
    System.out.println(item + " enqueued");
    queue.dataPrint();                       // print queue content

    item = new Integer(202);                 // create new item
    queue.enqueue(item);                     // enqueue item
    System.out.println(item + " enqueued");
    queue.dataPrint();                       // print queue content

    item = (Integer) queue.dequeue();        // remove element from queue
    System.out.println(item + " dequeued");
    queue.dataPrint();

    item = new Integer(203);                 // create new item
    queue.addHead(item);                     // prequeue item
    System.out.println(item + " prequeued");
    queue.dataPrint();                       // print queue content

    item = (Integer) queue.removeTail();     // premove element from queue
    System.out.println(item + " premoved");
    queue.dataPrint();

    item = new Integer(204);                 // create new item
    queue.enqueue(item);                     // enqueue item
    System.out.println(item + " enqueued");
    queue.dataPrint();                       // print queue content

    item = new Integer(205);                 // create new item
    queue.addHead(item);                     // prequeue item
    System.out.println(item + " prequeued");
    queue.dataPrint();                       // print queue content

    item = (Integer) queue.dequeue();        // remove element from queue
    System.out.println(item + " dequeued");
    queue.dataPrint();

    item = (Integer) queue.removeTail();     // premove element from queue
    System.out.println(item + " premoved");
    queue.dataPrint();

    item = (Integer) queue.dequeue();        // remove element from queue
    System.out.println(item + " dequeued");
    queue.dataPrint();
  }  
}

