//package STOPCL;

import java.lang.Math;

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

/** BarDial extends Component to draw a Bar Chart,
 * specified by arrays of colors and starting values.
 * and one more to close the last segment.

 * @author Morris Hirsch
 * IPD Inc Newport RI
 * Nov 10 1998 */

public class BarDial
extends Component
{

    public static final int
	HORIZONTAL = 1,
	VERTICAL = 2;

    protected int orient;

    protected Color [] colors = null;

    protected float [] starts = null;

    protected Color markColor = null;

    protected float markValue = 0;

    protected int [] coords = null;

    protected boolean active = false;

    protected float floatUnitIncrement = 1;

/** Construct a BarDial.
 * @param orient -- direction of the bar,
 * @param colors -- colors to show for each segment,
 * @param starts -- starting value of each segment,
 * and one more to close the last segment.  * */

    public BarDial (int orient, Color [] colors, float [] starts) {
	this.orient = orient;
        setBar (colors, starts);
    }

/** Construct a BarDial.
 * @param colors -- colors to show for each segment,
 * @param starts -- starting value of each segment,
 * and one more to close the last segment.  * */

    public BarDial (Color [] colors, float [] starts) {
        this (HORIZONTAL, colors, starts);
    }

/** Construct a BarDial.
 * @param orient -- direction of the bar, */

    public BarDial (int orient) {
        this (orient, null, null);
    }

/** Construct a BarDial per defaults */

    public BarDial () { }

/** Update a BarDial.
 * @param colors -- colors to show for each segment,
 * @param starts -- starting value of each segment,
 * and one more to close the last segment.  * */

    public void setBar (Color [] colors, float [] starts) {
        this.colors = colors;
        this.starts = starts;

        scaleCoords ();
	repaint (100);
    }

/* Mark some value on the scale */

    public void setMark (Color mc, float mv) {
	this.markColor = mc;
	this.markValue = mv;
    }

/** MinimumSize is 4 times the current Font point size.  *  */

    public Dimension getMinimumSize () {

        int points = 12;
        Font oFont = getFont ();
        if (null != oFont)
	    points = oFont.getSize ();

	if (HORIZONTAL == orient)
	    return new Dimension (4*points, points);
	else
	    return new Dimension (points, 4*points);
    }

/** PreferredSize is 8 times the current Font point size.  * */

    public Dimension getPreferredSize () {

        int points = 12;
        Font oFont = getFont ();
        if (null != oFont)
	    points = oFont.getSize ();

	if (HORIZONTAL == orient)
	    return new Dimension (8*points, 2*points);
	else
	    return new Dimension (2*points, 8*points);
    }

/** Draw an edge to show we have focus. */

    public void setActive (boolean bv) {
	active = bv;
	repaint (100);
    }

    public void setBounds (int x, int y, int w, int h) {
	super.setBounds (x, y, w, h);
	scaleCoords ();
    }

    public void setBounds (Rectangle r) {
	super.setBounds (r);
	scaleCoords ();
    }

    public void setSize (int w, int h) {
	super.setSize (w, h);
	scaleCoords ();
    }

    public void setSize (Dimension d) {
	super.setSize (d);
	scaleCoords ();
    }

/** Map the starts array onto width or height,
 * starts [0] always maps at the left or top,
 * progressing right or down,
 * starts [last] maps at the right or bottom,
 * although the numbers may be increasing or decreasing. */

    protected void scaleCoords () {
	coords = null;

	if ((null == starts) || (2 > starts.length))
	    return;

        Dimension d = getSize ();

	if ((null == d)
	 || (0 == d.width)
	 || (0 == d.height))
	    return;

	coords = new int [starts.length];
	float span = starts [starts.length-1] - starts [0];

        for (int ii = 0;
             ii < starts.length;
	     ii++) {

            if (HORIZONTAL == orient)
		coords [ii] = (int)(d.width * (starts [ii] - starts [0]) / span);

            else
	        coords [ii] = (int)(d.height * (starts [ii] - starts [0]) / span);
	}
    }

/** Map the coords array from width or height back onto starts.
 * starts [0] always maps at the left or top,
 * starts [last] maps at the right or bottom,
 * they provide the mapping for all other starts from coords. */

    protected void scaleStarts () {

	if ((null == coords) || (2 > coords.length))
	    return;

	if ((null == starts) || (2 > starts.length))
	    return;

        Dimension d = getSize ();

	if ((null == d)
	 || (0 == d.width)
	 || (0 == d.height))
	    return;

	float span = starts [starts.length-1] - starts [0];

        for (int ii = 1;
             ii < coords.length-1;
	     ii++) {

            if (HORIZONTAL == orient)
		starts [ii] = starts [0]
		  + (span * ((float)coords [ii])
			  / ((float)d.width));

            else
	        starts [ii] = starts [0]
		  + (span * ((float)coords [ii])
			  / ((float)d.height));
	}
    }

/** Determine a reasonable Unit Increment for the range,
 * then make all the adjustable starts be multiples of it.
 * Called only when user adjusts the BarControl,
 * otherwise all values are left alone.
 * Note that starts [0] and starts [last] are never changed.  */

    protected void detentStarts () {

	if ((null == starts) || (2 > starts.length))
	    return;

	float span = starts [starts.length-1] - starts [0];
	if (span < 0)
	    span = 0-span;

/* These breaks are the same as used by FloatValueBox */

	if (10000 < span) floatUnitIncrement = 100;
	else if (1000 < span) floatUnitIncrement = 10;
	else if (20 < span) floatUnitIncrement = 1;
	else floatUnitIncrement = (float)0.1;

        for (int ii = 1;
             ii < starts.length-1;
	     ii++) {
	     float fcount = (starts [ii] / floatUnitIncrement);
	     if (0 < fcount)
		 fcount += 0.5;
	     else if (0 > fcount)
		 fcount -= 0.5;
             int count = (int)fcount;
	     starts [ii] = floatUnitIncrement * count;
        }
    }

    public void update (Graphics g) { paint (g); }

    public void paint (Graphics g) {

        Dimension d = getSize ();

/* Initialize the background so that the user can
 * tell there is something there at initialization (other
 * than grey on grey) without giving focus (to see the outline). */

        g.setColor(getBackground ().darker ());
	g.drawRect (0, 0, d.width-1, d.height-1);

/* Draw the sections if we have any */

/* Each section ends at the start of the next,
 * last ends at final extra start.
 * They must be all increasing,
 * or some will be covered. */

	if ((null != coords) && (1 < coords.length)) {

            int aCoord = coords [0];
            int aStop;

            for (int ii = 0;
                 ii < colors.length;
	         ii++)
                if (null != colors [ii]) {
		    g.setColor (colors [ii]);
		    break;
		}

            for (int ii = 0;
                 ii < coords.length-1;
	         ii++) {

                if (null != colors [ii])
		    g.setColor (colors [ii]);

		    aStop = coords [ii+1];

                    if (HORIZONTAL == orient)
                        g.fillRect 
		         (aCoord,
			 0,
		         aStop - aCoord,
			 d.height);

                    else
                        g.fillRect 
		        (0,
			 aCoord,
		         d.width,
			 aStop - aCoord);

		    aCoord = aStop;
            }

/* This works but does not always show up well enough.
 * Maybe provide contrasting outline. */

	    if (null != markColor) {
		g.setColor (markColor);

	        float span = starts [starts.length-1] - starts [0];

                if (HORIZONTAL == orient) {
                    aCoord = (int)(d.width * (markValue - starts [0]) / span);
                    g.fillRect 
		         (aCoord, 0,
		         2, d.height);
                }

                else {
	            aCoord = (int)(d.height * (markValue - starts [0]) / span);
                    g.fillRect 
		        (0, aCoord,
		         d.width, 2);
                }
	    }
        }

/* Draw an edge to show we have focus */

	if (active) {
	    g.setColor (Color.black);
	    g.drawRect (0, 0, d.width-1, d.height-1);
	}

    }

}
/* <IMG SRC="/cgi-bin/counter">*/
