// Queue.java -- partially written source file for Lab05

package lab05;

import java.awt.*;

public class Queue {
    public static final int DEFAULT_CAPACITY = 5;
    
    // Queue instance variables...

    private Object [] array;   // Reference to internal array
    private int initCapacity;  // Initial capacity
    private int front;         // Index to front value
    private int rear;          // Index to rear value
    private int itemCount;     // Current size (number of stored items)

    // Private constants used to render Queue...

    private static final int CELL_WIDTH = 120;
    private static final int CELL_HEIGHT = 30;
    private static final Color FILLED_CELL_COLOR
            = new Color (0xEC,0xEC,0xEC);
    private static final Color EMPTY_CELL_COLOR
            = new Color (0xB7,0xB7,0xB7);
    private static final Color ARROW_COLOR = Color.black;

    private static final int[] X_ARROW = {0, 20,20,70,70,20,20};
    private static final int[] Y_ARROW = {0,-15,-5,-5, 5, 5,15};
	
    /** Creates an empty Queue instance with the specified
     *  initial capacity.
      * @param initCapacity The initial capacity. 
      */

    public Queue (int initCapacity) {
        array = new Object [initCapacity];
        this.initCapacity = initCapacity;
        front = rear = 0;
        itemCount = 0;
    }

    /** Creates an empty Queue with a default initial capacity.
     */

    public Queue () {
        this (DEFAULT_CAPACITY);
    }    

    // ----------------------------------------------------------------------
    // Stubs for Queue methods... this is where you come in...

/** add() first checks whether the current internal array is full,
 *  and if necessary it invokes setCapacity() to provide one or more
 *  empty array locations. Then the method goes on to add its value
 *  parameter to the Queue.
 * 
 *  @param value The object to be added to the queue.
 */
    public void add (Object value) {
        // STUB implement this method
    }
  
/** remove() first checks whether the Queue is empty, and if so
 *  it immediately throws an IllegalStateException without making
 *  any change to the state of the Queue. Otherwise, it removes
 *  the front value and returns it.
 *
 *  @return The value removed from the front of the queue.
 */
    public Object remove () throws IllegalStateException {
        // STUB -- remove line below and implement this method
        return null;
    }

/** Indicates whether the Queue is currently empty.
 *  @return true if the Queue is empty, false otherwise.
 */
    public boolean isEmpty () {
        // STUB -- remove line below and implement this method
        return false;
    }

    /** Returns the number of values currently stored in the queue.
     * @return Number of stored values.
     */
    public int getItemCount () {
        // STUB -- remove line below and implement this method
        return 0;
    }
  
    /** Returns the current capacity of the internal <B>Queue</B> array.
     * Note that this is the full <I>length</I> of the array, namely 
     * the number of components allocated by the <B>new</B> operator.
     * It does <I>not</I> denote the number of items currently stored in 
     * the queue, which is returned by {@link #getItemCount}.
     * @return The current <B>Queue</B> capacity.
     */
    public int getCapacity () {
        // STUB -- remove line below and implement this method
        return 0;         // STUB -- replace this
    }
  
    /** If the requested new capacity is not sufficient to hold all
     * the values currently stored in the Queue, the method throws
     * an IllegalStateException without making any change to the
     * Queue. Otherwise, this method replaces the internal array
     * with a new array having the requested new capacity.
     * All items stored in the current array are copied to the
     * new array, and the values at the front and rear of the queue
     * are the same as before.
     * @param capacity The requested new capacity.
     */
    public void setCapacity (int capacity) throws IllegalStateException {
        // STUB -- implement this method
    } 

    /** Returns the initial capacity of the internal <B>Queue</B> array.
     * If the <B>Queue</B> object was created using the default constructor,
     * the initial capacity is given by {@link #DEFAULT_CAPACITY}.
     * @return The initial <B>Queue</B> capacity.
     */

/*
    public int getInitCapacity () {
        // STUB -- remove line below and implement this method
        return 0;         // STUB -- replace this
    }
*/  
    /** Removes all values stored in the Queue, without changing
     *  its current capacity.
     */
    public void clear () {
        // STUB -- implement this method
    }
  
    /** Restores the Queue to its original state, just after it
     *  was constructed.
     */
    public void reset () {
        // STUB -- implement this method
    }
  
    /** Changes the Queue capacity to match its current size (number
     * of stored items). Note: if the Queue is currently empty, its
     * capacity is set to 1 rather than 0.
     */
    public void trim () {
        if (itemCount == 0) {
            setCapacity(1);
        }
        else {
            setCapacity (itemCount);
        }
    } 

    /** Renders the Queue in a graphical display window.
     * The window dimensions and its associated graphics context
     * (java.awt.Graphics instance) are both supplied as input
     * parameters.
     * @param g The graphics context associated with the display window
     * (which provides rendering methods as well as the reference frame
     * for the pixel coordinates).
     * @param panelSize The width and height of the display area,
     * in pixels.
     */

    public void render (Graphics g, Dimension panelSize) {
        int panelWidth = (int) panelSize.getWidth();
        int panelHeight = (int) panelSize.getHeight();

        int xbase = panelWidth / 2;                   // Vertical axis
        int ybase = panelHeight - CELL_HEIGHT - 20;   // Bottom of array

        // Find x-coordinate of upper left-hand corner of each cell
        
        int xloc = (panelWidth - CELL_WIDTH) / 2;
        int yloc = ybase - CELL_HEIGHT;

        if (itemCount == 0) {                               // Empty queue
            for (int i = 0; i < array.length; i++) {
                g.setColor (EMPTY_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
            }
        }
        else if (front == rear) {                           // Full queue
            for (int i = 0; i < array.length; i++) {
                g.setColor (FILLED_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
                g.setColor (Color.black);
                g.drawString(array[i].toString(),
                     xloc + 2, yloc - i * CELL_HEIGHT + 20);
            }
        }
        else if (front < rear) {
            for (int i = 0; i < front; i++) {
                g.setColor (EMPTY_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
            }

            for (int i = front; i < rear; i++) {
                g.setColor (FILLED_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
                g.setColor (Color.black);
                g.drawString(array[i].toString(),
                     xloc + 2, yloc - i * CELL_HEIGHT + 20);
            }

            for (int i = rear; i < array.length; i++) {
                    g.setColor (EMPTY_CELL_COLOR);
                    g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                           CELL_WIDTH, CELL_HEIGHT, true);
            }
        }
        else if (front > rear) {
            for (int i = 0; i < rear; i++) {
                g.setColor (FILLED_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
                g.setColor (Color.black);
                g.drawString(array[i].toString(),
                     xloc + 2, yloc - i * CELL_HEIGHT + 20);
            }

            for (int i = rear; i < front; i++) {
                g.setColor (EMPTY_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
            }

            for (int i = front; i < array.length; i++) {
                g.setColor (FILLED_CELL_COLOR);
                g.fill3DRect(xloc, yloc - i * CELL_HEIGHT,
                       CELL_WIDTH, CELL_HEIGHT, true);
                g.setColor (Color.black);
                g.drawString(array[i].toString(),
                     xloc + 2, yloc - i * CELL_HEIGHT + 20);
            }
        }

        paintArrow (g, xbase, ybase, "Front", front, false);
        paintArrow (g, xbase, ybase, "Rear", rear, true);
    }

    private void paintArrow (Graphics g, int xbase, int ybase,
        String label, int index, boolean rhs) {
        g.setColor (ARROW_COLOR);
    
        int[] xarrow = new int [X_ARROW.length];
        int[] yarrow = new int [Y_ARROW.length];
        int points = X_ARROW.length;
    
        for (int k = 0; k < points; k++) {
        if (rhs)
            xarrow[k] =  X_ARROW [k] + xbase + CELL_WIDTH / 2 + 10;
        else
            xarrow[k] = -X_ARROW [k] + xbase - CELL_WIDTH / 2 - 10;

        yarrow[k]   =  Y_ARROW [k] + ybase - CELL_HEIGHT * index
                     - CELL_HEIGHT / 2;
        }

        g.fillPolygon (xarrow, yarrow, points);

        // Draw label...
    
        g.setColor (Color.black);
        Font font = new Font ("Arial", Font.BOLD, 18);
        Font currentfont = g.getFont();
        g.setFont(font);

        if (rhs) {
            g.drawString (label,
                xbase + CELL_WIDTH / 2 + 90,
                ybase - CELL_HEIGHT * index - CELL_HEIGHT / 2 + 5);
        }
        else {
            g.drawString (label, xbase - CELL_WIDTH / 2 - 140,
                ybase - CELL_HEIGHT * index - CELL_HEIGHT / 2 + 5);
        }
    
        g.setFont (currentfont);
    }
}
