import java.awt.*;
import javax.swing.*;

class RainfallDisplay extends JComponent {
        /** Create a display without the data having been set.
         */
    public RainfallDisplay(){
        this(null);
    }

        /** Create a display to manage the given values.
         */
    public RainfallDisplay(int[] values){
        int width = 500, height = 300;
        setMinimumSize(new Dimension(width,height));
        setPreferredSize(new Dimension(width,height));
        ComponentFrame frame = new ComponentFrame("Bar Chart");
        frame.addComponent(this);
        frame.pack();
        setData(values);
    }

        /** Provide a set of labels for the bars.
         */
    public void setBarLabels(String[] labels) throws RuntimeException {
        if(labels != null){
            // Make sure there are the same number of labels as
            // data values.
            final int numLabels = labels.length;
            int[] data = getData();
            if((data != null) && (numLabels != data.length)){
                throw new RuntimeException(
                                "Wrong number of labels: "+numLabels+
                                " in setBarLabels.");
            }
            else{
                barLabels = labels;
                repaint();
            }
        }
        else{
            throw new RuntimeException(
                                "Null label array passed to setBarLabels.");
        }
    }

        // Display each value in data in a separate bar.
        // The number of bars will be equal to the length of data.
        // A copy of the data is *not* made.
    public void setData(int[] d) throws RuntimeException {
        if(d != null){
            final String[] labels = getBarLabels();
            if((labels != null) && (d.length != labels.length)){
                throw new RuntimeException(
                        "Mismatch betwen number of data values: "+
                        d.length+" and the number of labels: "+
                        labels.length+" in setData.");
            }
            else{
                data = d;
            }
        }
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Image i = getImage();
        // Check for resizing.
        final Dimension size = getSize();
        if(size.width == 0 || size.height == 0){
            System.out.println("Size = "+size);
            return;
        }
        if((i == null) || (i.getWidth(this) != size.width) ||
                          (i.getHeight(this) != size.height)){
            // Redraw it.
            drawBars();
            // Drawing the bars should have set up an Image.
            i = getImage();
        }
        if(i != null){
            g.drawImage(getImage(),0,0,this);
        }
    }


        // Redisplay the data after it has been altered.
    public void repaint(){
        drawBars();
        paintComponent(getGraphics());
    }

        // Don't try to draw the bars if there is no data.
    protected void drawBars(){
        // Draw the bars directly from the counts.
        int[] data = getData();
        if(data != null){
            drawBarsFromData(data);
        }
    }

        // Draw all the bars on the graphics context of an offscreen
        // image. Make sure the current image is the right size.
    protected void drawBarsFromData(int[] dataValues){
        // Make sure it is sensible to be drawing anything.
        final Dimension size = getSize();
        if((size.width == 0) || (size.height == 0)){
            return;
        }
        Image i = getImage();
        // Check for resizing.
        if((i == null) || (i.getWidth(this) != size.width) ||
                          (i.getHeight(this) != size.height)){
            i = createImage(size.width,size.height);
            setImage(i);
        }
        // Where to draw to.
        final Graphics g = i.getGraphics();
        // Clear any old drawing.
        g.setColor(Color.white);
        g.fillRect(0,0,size.width,size.height);

        final int numBars = dataValues.length;
        final String[] labels = getBarLabels();

        // Offset at the left (and right) edge, above and below the chart.
        final int leftEdge = 5, topEdge = 25, bottomEdge = 30;
        // The width of each bar.
        final int barWidth = (size.width-2*leftEdge)/numBars;
        // Remaining height for the whole chart.
        final int availableHeight = size.height-(topEdge+bottomEdge);
        // We need to calculate string widths in the current font.
        final FontMetrics metrics = getFontMetrics(getFont());

        // Find the largest bar to be drawn.
        final int maxCount = findMax(dataValues);
        for(int barNumber = 0; barNumber < numBars; barNumber++){
            final int value = dataValues[barNumber];
            // Determine the bar height based upon the current maximum.
            final int barHeight;
            if(maxCount > 0){
                barHeight = availableHeight*value/maxCount;
            }
            else{
                barHeight = 0;
            }
            final int x = leftEdge+barNumber*barWidth,
                                y = size.height-(barHeight+bottomEdge);
            if(barHeight > 0){
                g.setColor(Color.black);
                g.drawRect(x,y,barWidth,barHeight);
                g.setColor(Color.yellow);
                g.fillRect(x+1,y+1,barWidth-1,barHeight-1);
            }

            // Show the count just above the bar.
            final String howMany = ""+value;
            int stringWidth = metrics.stringWidth(howMany);
            g.setColor(Color.black);
            g.drawString(howMany,x+(barWidth-stringWidth)/2,
                            size.height-(barHeight+bottomEdge));

            if(labels != null){
                // Draw a label under the bar.
                final String label = labels[barNumber];
                stringWidth = metrics.stringWidth(label);
                g.drawString(label,x+(barWidth-stringWidth)/2,
                                size.height-bottomEdge+metrics.getHeight()+1);
            }
        }
    }

        // Return the bar labels. If there are none, make some up,
        // based upon the lowest value of each bar.
    protected String[] getBarLabels(){
        return barLabels;
    }

        // Find the largest, for scaling.
    protected int findMax(int[] dataValues){
        if(dataValues != null){
            final int numBars = dataValues.length;
            int max = dataValues[0];
            for(int i = 1; i < numBars; i++){
                if(dataValues[i] > max){
                    max = dataValues[i];
                }
            }
            return max;
        }
        else{
            return 0;
        }
    }

    protected Image getImage(){
        return image;
    }

    protected void setImage(Image i){
        image = i;
    }

    protected int[] getData(){
        return data;
    }

    // User settable labels for the bars.
    private String[] barLabels = null;
    // The user's data.
    private int[] data = null;

    // The offscreen image for double-buffering.
    private Image image;
}
