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

// Objects are enqueued into a QueueG, and they are dequeued.
// Either operation is possible at either end of the queue.

import java.awt.*;
import java.awt.event.*;
import java.util.*;

class QueueG extends StepTestCanvas {
  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 QueueG() {                     // constructor
    data = new Object[BufferSize];      // create array for Objects
    clear();
  }

  public void clear() {                 // reinitialize queue
    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;  // end of buffer, wrap around
    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;                                  // one element removed
    return temp;
  }

  public boolean empty() {
    return nElements == 0;                       // buffer is empty
  }

  public boolean full() {
    return nElements == BufferSize;              // buffer is full
  }

  public void dataPrint() {                      // print queue content
     System.out.print("QueueG: 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("");
  }

  public void drawRightArrow(Graphics g, int x1, int y1, int x2) {
     Polygon poly = new Polygon();     // arrow from (x1, y1) to (x2, y1)
    poly.addPoint(x1, y1 - 2);
    poly.addPoint(x1, y1 + 2); 
    poly.addPoint(x2 - 6, y1 + 2);
    poly.addPoint(x2 - 6, y1 + 6);
    poly.addPoint(x2, y1);             // tip of right arrow
    poly.addPoint(x2 - 6, y1 - 6);
    poly.addPoint(x2 - 6, y1 - 2);
    g.fillPolygon(poly);               // draw arrow
  }

  static final Color BoxColor =  new Color(150, 0, 0);     // for box outline
  static final Color EmptyBox =  new Color(0, 150, 0);     // for empty box
  static final Color FilledBox =  new Color(100, 255, 0);  // for filled box
  static final Color EmptyLetter =  new Color(255,255, 255); // for null box
  static final Color FullLetter =  new Color(100, 100, 0); // for value in box
  static final Color RightArrowColor =  new Color(0, 0, 200);
  static final Color ArrowLabel =  new Color(0, 0, 200); // 

  Font f = new Font("TimesRoman", Font.BOLD, 18);

  public synchronized void paint(Graphics g) {
    int x1 = 280;                   // left position for queue elements
    int y = 50;                     // top position of queue elements
    int width = 60;                 // width of queue element
    int height = 35;                // height of queue element
    int PointerXLoc = x1 - width * 2 / 3;

    g.setFont(f);                   // set font for letters

    if (headIdx == tailIdx) {
      int y2 = y + headIdx * height;   // y position of arrow
      g.setColor (FullLetter);         // set letter color
      g.drawString("head & tail", x1 - width * 8 / 3, y2 + height / 2 + 5);
      g.setColor(RightArrowColor);     // set right arrow color
      drawRightArrow(g, PointerXLoc, y2 + height / 2,    // draw head pointer
                        x1 - 2);
    } else {
      int y2 = y + headIdx * height;   // y position of arrow
      g.setColor (FullLetter);         // set letter color
      g.drawString("head", x1 - width * 4 / 3 - 15, y2 + height / 2 + 5);
      g.setColor(RightArrowColor);     // set right arrow color
      drawRightArrow(g, PointerXLoc, y2 + height / 2,    // draw head pointer
                        x1 - 2);
      int y3 = y + tailIdx * height;    // y position of arrow
      g.setColor (FullLetter) ;        // set letter color
      g.drawString("tail", x1 - width * 4 / 3, y3 + height / 2 + 5);
      g.setColor(RightArrowColor);     // set right arrow color
      drawRightArrow(g, PointerXLoc, y3 + height / 2,    // draw tail pointer
                        x1 - 2);
    }
    for (int i = 0; i < data.length; i ++) {  // draw queue elements
      g.setColor(BoxColor);                   // set box outline color
      g.drawRect(x1, y, width, height);       // draw box outline
      if (data[i] == null) {                  // is queue element empty?
        g.setColor(EmptyBox);                 // yes, use empty box color
        g.fillRect(x1 + 1, y + 1, width - 1, height - 1);  // fill box
        g.setColor(EmptyLetter);              // set light letter color
      } else {                                // queue element exists
        g.setColor(FilledBox);                // set clor for box with element
        g.fillRect(x1 + 1, y + 1, width - 1, height - 1);  // draw box
        g.setColor(FullLetter);               // set dark letter color
      }
      if (data[i] == null) {                     // no queue element?
        g.drawString("null", x1 + 12, y + height / 2 + 5);   // indicate null
      } else {
        g.drawString(String.valueOf(data[i]), x1 + 20, y + height / 2 + 5);
      }
      y += height;               // update y position of next array element
    }
  }

  public void runMain() { 
    String defaultCommands = 
      "add 201 add 202 remove addH 203 removeT add 204 addH 205 removeT remove";
    String command;
    int item;
    clear();
    repaintStep("You may provide a sequence of add and remove commands;" +
                " then press the Step button.");

    StringTokenizer inputTokens = getInputTokens();  // get tokens
    if (!inputTokens.hasMoreTokens()) {
      inputTokens = new StringTokenizer(defaultCommands);
      if( defaultCommands.length() > 60 )
        repaintStep("Using default command sequence.");
      else
        repaintStep("Default commands: " + defaultCommands + ".");
    }
    while (inputTokens.hasMoreTokens()) {
      command = inputTokens.nextToken();
      if (command.equals("add")) {                        // add item command
        if (full()) {             
          repaintStep("No more items can be enqueued.");  // buffer full
          continue;
        } else {
          if (inputTokens.hasMoreTokens()) {
            item = Integer.parseInt(inputTokens.nextToken());
            repaintStep("Entry " + item + " will be enqueued at tail.");
            enqueue(new Integer(item)); // insert item
            repaintStep("Successfully enqueued " + item + " at tail.");
          } else {
            repaintStep("add command requires item.");
          }
          continue;
        }
      } else if (command.equals("addH")) {      // addH item command
        if (full()) {             
          repaintStep("No more items can be enqueued.");  // buffer full
          continue;
        } else {
          if (inputTokens.hasMoreTokens()) {
            item = Integer.parseInt(inputTokens.nextToken());
            repaintStep("Entry " + item + " will be enqueued at head.");
            addHead(new Integer(item)); // insert item
            repaintStep("Successfully enqueued " + item + " at head.");
          } else {
            repaintStep("addH command requires item.");
          }
          continue;
        }
      } else if (command.equals("remove")) {    // remove item command
        if (empty()) {             
          repaintStep("Queue is empty.");  // boundedBuffer is empty
        } else {
          repaintStep("Item at head will be dequeued.");
          item =  ((Integer) dequeue()).intValue();  // remove item
          repaintStep("Successfully dequeued " + item + " from head.");
        }
        continue;
      } else if (command.equals("removeT")) {   // removeT item command
        if (empty()) {             
          repaintStep("Queue is empty.");  // boundedBuffer is empty
        } else {
          repaintStep("Item at tail will be dequeued.");
          item =  ((Integer) removeTail()).intValue();  // removeT item
          repaintStep("Successfully dequeued " + item + " from tail.");
        }
        continue;
      } else if (command.equals("reset")) {     // reset command
        clear();  // clear buffer
        repaintStep("Bounded buffer cleared.");
        continue;
      }
    }
    repaintStep("Commands ended; Press Restart button to replay.");
  }

  public static void main(String[] args) {
    StepTestFrame f = new StepTestFrame("Bounded Buffer Test Program",
                                  new QueueG(), 580, 400); 
    f.show();                // display frame
  }
}

