package org.jcon.ui;

import javax.swing.JComponent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;

/**
 * Your garden variety horizontal color bar with the fancy
 * feature of rainbow colors, to enliven the user's day....
 * It also has the ability to blink the last color, which
 * is very useful for indicating or emphasizing status.
 * <p>
 * *** Note - Not yet fully documented.
 * May be replaced by new Swing progress bar.
 *
 * @author Jack Harich
 */
public class ColorBar extends JComponent implements Runnable {

//---------- Private Fields ------------------------------
// Properties
private int     prefHeight  = 20;
private boolean usePercent;
private int     fullCount;
private int     cellCount   = 20;
private double  percentFull = 0;
private int     cellWidth   = 20;
private int     blinkRate; // 0 if none
// Internal
private static Color[] colors;
private static Color EMPTY = Color.lightGray;
// Blink related
private int     lastFullCellIndex = -1; // -1 if none
private Color   lastFullCellColor;
private boolean isEmpty; // true if lasFullCell is empty

//---------- Initialization ------------------------------
static {
    colors = new Color[7];
    colors[0] = Color.red;
    colors[1] = Color.orange;
    colors[2] = Color.yellow;
    colors[3] = Color.green;
    colors[4] = Color.cyan;
    colors[5] = Color.blue;
    colors[6] = Color.magenta;
}
//---------- Superclass Overrides ------------------------
// The crucial method - Horizontal version
public void paint(Graphics g) {
    //print(".paint() - Entered");
    Rectangle b = getBounds();
    int myWidth = getPreferredWidth();
    int colorCount = colors.length;

    // Erase previous - No longer needed, using repaint()
//    g.setColor(EMPTY);
//    g.fillRect(0, 0, b.width, b.height);

    // Draw border and vertical lines
    g.setColor(Color.black);
    g.drawRect(0, 0, myWidth - 1, b.height - 1);
    for (int i = 0; i < cellCount; i++) {
        int x = i * cellWidth;
        g.drawLine(x, 0, x, b.height - 1);
    }
    // Draw cells
    lastFullCellIndex = -1; // None
    for (int i = 0; i < cellCount; i++) {
        // Use percentFull or fullCount
        boolean showEmpty;
        if (usePercent) {
            // Zero percentFull should show all empty
            showEmpty = ((double)i / (double)cellCount) >= percentFull;
        } else {
            showEmpty = (i >= fullCount);
        }
        // Paint color
        Color color;
        if (showEmpty) {
            color = EMPTY;
        } else {
            int colorIndex = i % colorCount;
            color = colors[colorIndex];
        }
        drawCell(g, i, color);
        // Set blink fields
        if (! showEmpty) {
            lastFullCellIndex = i;
            lastFullCellColor = color;
            isEmpty = false;
        }
    }
}
private void drawCell(Graphics g, int cellIndex, Color color) {
    int x = cellIndex * cellWidth;
    Rectangle b = g.getClipBounds();
    g.setColor(color);
    g.fillRect(x + 1, 1, cellWidth - 1, b.height - 2);
}
private int getPreferredWidth() {
    return (cellWidth * cellCount) + 1;
}
//----- These methods allow control of our size, with default
public void setSize(int width, int height) {
    //print(".setSize() - Entered, width = " + width + ", height = " + height);
    setBounds(getLocation().x, getLocation().y, width, height);
}
public void setBounds(int x, int y, int width, int height) {
    //print(".setBounds() - Entered, width = " + width + ", height = " + height);
    super.setBounds(x, y, width, height);
    //*** Comment out due to Java 1.1.5 bug 4/22/98 ******
    //prefHeight = height;
    repaint(0);
}
public Dimension getPreferredSize() {
    //print(".getPreferredSize() - Entered, prefWidth = " + getPreferredWidth() + ", prefHeight = " + prefHeight);
    return new Dimension(getPreferredWidth(), prefHeight);
}
public Dimension getMinimumSize() {
    return getPreferredSize();
}
//---------- Runnable Implementation ---------------------
public void run() {
while (blinkRate > 0) {
    try {
        Thread.currentThread().sleep(blinkRate);

        if (lastFullCellIndex >= 0) {
            isEmpty = ! isEmpty;
            Color color = (isEmpty ? EMPTY : lastFullCellColor);
            Graphics g = getGraphics();
            drawCell(g, lastFullCellIndex, color);
            g.dispose();
            //print(" drew color " + color + ", index " + lastFullCellIndex);
        }
    } catch(InterruptedException ex) {
        // Ignore
    }
}
} // End method

//---------- Properties ----------------------------------
//----- usePercent
/**
 * Sets the UsePercent property, which if true will cause
 * the bar to reflect the current percent full. If false
 * the bar will reflect the current full count. This may be
 * changed anytime. UsePercent cannot be less than zero but
 * may be greater than 100.
 */
public void setUsePercent(boolean usePercent) {
    this.usePercent = usePercent;
    repaint(0);
}
public boolean isUsePercent() {
    return usePercent;
}
//----- percentFull
public void setPercentFull(double percentFull) {
    if (percentFull < 0) throw new IllegalArgumentException(
        "PercentFull cannot be less than zero.");
    this.percentFull = percentFull;
    repaint(0);
}
public double getPercentFull() {
    return percentFull;
}
//----- fullCount
public void setFullCount(int fullCount) {
    if (fullCount < 0) throw new IllegalArgumentException(
        "FullCount cannot be less than zero.");
    this.fullCount = fullCount;
    repaint(0);
}
public int getFullCount() {
    return fullCount;
}
//----- cellWidth
public void setCellWidth(int cellWidth) {
    if (cellWidth < 0) throw new IllegalArgumentException(
        "CellWidth cannot be less than zero.");
    this.cellWidth = cellWidth;
    repaint(0);
}
public int getCellWidth() {
    return cellWidth;
}
//----- cellCount
public void setCellCount(int cellCount) {
    if (cellCount < 0) throw new IllegalArgumentException(
        "CellCount cannot be less than zero.");
    if (this.cellCount == cellCount) return;

    this.cellCount = cellCount;
    invalidate();
    if (findTopContainer(this) != null) {
        // validate parent is not enough
        findTopContainer(this).validate();
    }
}
public int getCellCount() {
    return cellCount;
}
//----- blinkRate
/**
 * The blink rate, in milliseconds, determines how often
 * the last colored cell blinks. If the rate is zero then
 * there is no blink, which is the default. A reasonable
 * rate is 500. The rate may be changed anytime.
 */
public void setBlinkRate(int blinkRate) {
    if (blinkRate < 0) throw new IllegalArgumentException(
        "BlinkRate cannot be less than zero.");
    this.blinkRate = blinkRate;
    if (blinkRate > 0) {
        Thread thread = new Thread(this);
        thread.setPriority(Thread.NORM_PRIORITY + 2);
        new Thread(this).start();
    }
}
public int getBlinkRate() {
    return blinkRate;
}
//---------- Public Methods ------------------------------
public void incrementFullCount() {
    fullCount++;
    repaint();
}
/**
 * For use when thread contention prevents normal repaints
 * from being processed because the EventQueue is blocked.
 * <p>
 * For example this occurs when repaint() is called on a
 * Component before a menu selection notification has
 * returned, which implies one use of this method.
 */
public void repaintNow() {
    Graphics g = getGraphics();
    paint(g);
    g.dispose();
}
//---------- Private Methods -----------------------------
/**
 * Finds and returns the top Container for the component.
 * Returns null if none. Useful for calling validate() on
 * the top component to force a layout when a component's
 * size or presence has changed dynamically.
 * <p>
 * From VisualLib to reduce package coupling.
 */
private static Container findTopContainer(JComponent component) {
    Container parent = component.getParent();
    while (parent != null) {
        if (parent.getParent() == null) {
            return parent;
        } else {
            parent = parent.getParent();
        }
    }
    return parent;
}
//--- Std
private static void print(String text) {
    System.out.println("ColorBar" + text);
}

} // End class
