package EDU.cmu.softagents.util;

import java.util.*;

/** GenericQueue class: This class implements a queue. The class 
    is thread-safe and has wait-notify mechanism in it.
    @version 06/30/97
    @author Somesh Jha
    */

public class GenericQueue {
    static final int MAX=500;
    int front, back;
    int size;
    Object queue[];
    
    public GenericQueue() {
        size = MAX + 1;  // + 1 so queue can actually hold MAX items
	queue = new Object[size];
	front=0;
	back=0;
    } // end of constructor


    /** Arg, size, is the desired capacity of the queue. If size is
        not positive, a defalut size is used.
     */
    public GenericQueue(int size) {
        // If size arg is invalid, set size to default.
        if (size < 1) size = MAX;

        // We must add 1 to size arg, else queue can hold only
        // size-1 items and a size arg of 1 would produce an error.
        this.size = size + 1;
	
	queue = new Object[this.size];
	front =0;
	back=0;
    } // end of constructor

	
    /** Check whether queue is empty
     */
    public boolean isEmpty() {
	return (front == back);
    }


    /** Check whether queue is full 
     */
    public boolean isFull() {
	return((back+1)%size == front);
    }


    /** Report number of elements the queue can hold.
     */
    public int capacity() {return size-1;}


    /** Report number of elements currently in queue.
     */
    public int numberOfElements() {
        if (front > back)
	  return size - (front - back);
	else
	  return back - front;
    }


    /** Synchronized method adding an object.
        Wait if the queue is full and notify if the queue 
        was empty.
    **/
    public synchronized void addObject(Object obj) {
	while (isFull())
          try {wait();}
	  catch (InterruptedException ex){};

	queue[back]=obj;
	boolean flag=isEmpty();
	back = (back+1)%size;
	//notify if the queue was empty
	if (flag) notifyAll();

    } // end of addObject


    /** Synchronized method for deleting an object.
        Wait if the queue is empty and notify if the queue 
        was full.
    **/
    // NOTE: This method could be subsumed by removeObject().
    //
    public synchronized void deleteObject() {
	while (isEmpty())
          try {wait();}
	  catch (InterruptedException ex){};

	queue[front]=null;
	boolean flag = isFull();
	front = (front+1)%size;

	//notify other threads if the queue was 
	// full
	if (flag) notifyAll();
    } 


    /** Get the front of the queue. Return null if
        the queue is empty. 
     */
    // Synchronized to prevent subscript range error with front.
    //
    public synchronized Object getObject() {
	if (isEmpty()) return(null);
	else return(queue[front]);
    }//end of getObject


    /** Replace the front element of the queue. Return the old
        front element or null if the queue was empty. 
     */
    public synchronized Object replaceObject(Object obj) {
	if (isEmpty()) {
	  addObject(obj);
	  return null;
	} else {
	  Object retval = queue[front];
	  queue[front] = obj;
	  return retval;
	}
    }


    /** Synchronized method removes an Object from the queue.
        Wait if the queue is empty and notify if the queue 
        was full.
    **/
    public synchronized Object removeObject() {
	while (isEmpty())
          try {wait();}
	  catch (InterruptedException ex){};

	Object result=queue[front];
	queue[front]=null;
	boolean flag = isFull();
	front = (front+1)%size;

	//notify other threads if the queue was 
	// full
	if (flag) notifyAll();
	return(result);
    } 


    /** Get enumeration type to go through the queue.
     */
    public synchronized Enumeration elements() {
	return(new QueueEnumeration(this));
    } // end of elements

} // end of class GenericQueue


class QueueEnumeration implements Enumeration {
    Object[] enumList = null;
    int enumIndex = 0;
    int enumSize = 0;

    public QueueEnumeration(GenericQueue queue) {
	// Build a private list of the queue's current members
	// that we can then use to supply the enumeration.
        enumSize = queue.numberOfElements();
	enumList = new Object[enumSize];
	int front = queue.front;
	for (int i = 0; front != queue.back; i++) {
	  enumList[i] = queue.queue[front];
	  front = (front+1)%queue.size;
	}
	enumIndex = 0;
    }

    public boolean hasMoreElements() {
	return (enumIndex < enumSize);
    }

    public Object nextElement() {
      Object retval = null;
      if (enumIndex < enumSize) {
	retval = enumList[enumIndex];
	enumIndex++;
      }
      return retval;
    }
} // end of myEnumeration