/*
 * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */
import java.awt.*;
import java.util.*;
import java.applet.Applet;

public class Converter extends Applet {
    ConversionPanel metricPanel, usaPanel;
    Unit[] metricDistances = new Unit[3];
    Unit[] usaDistances = new Unit[4];

    /** 
     * Create the ConversionPanels (one for metric, another for U.S.).
     * I used "U.S." because although Imperial and U.S. distance
     * measurements are the same, this program could be extended to
     * include volume measurements, which aren't the same.
     */
    public void init() {
        //Use a GridLayout with 2 rows, as many columns as necessary,
        //and 5 pixels of padding around all edges of each cell.
        setLayout(new GridLayout(2,0,5,5));

        //Create Unit objects for metric distances, and then 
        //instantiate a ConversionPanel with these Units.
        metricDistances[0] = new Unit("Centimeters", 0.01);
        metricDistances[1] = new Unit("Meters", 1.0);
        metricDistances[2] = new Unit("Kilometers", 1000.0);
        metricPanel = new ConversionPanel(this, "Metric System",
                                            metricDistances);

        //Create Unit objects for U.S. distances, and then 
        //instantiate a ConversionPanel with these Units.
        usaDistances[0] = new Unit("Inches", 0.0254);
        usaDistances[1] = new Unit("Feet", 0.305);
        usaDistances[2] = new Unit("Yards", 0.914);
        usaDistances[3] = new Unit("Miles", 1613.0);
        usaPanel = new ConversionPanel(this, "U.S. System", usaDistances);

        //Add both ConversionPanels to the Converter.
        add(metricPanel);
        add(usaPanel);

        //Calling the validate method here can help applets.
        //It's unnecessary when this program runs as an application.
        validate();
    }

    /**
     * Does the conversion from metric to U.S., or vice versa, and
     * updates the appropriate ConversionPanel. 
     */
    void convert(ConversionPanel from) {
        ConversionPanel to;

        if (from == metricPanel)
            to = usaPanel;
        else
            to = metricPanel;
        double multiplier = from.getMultiplier() / to.getMultiplier();
        to.setValue(from.getValue() * multiplier);
    }

    /** Draws a box around this panel. */
    public void paint(Graphics g) {
        Dimension d = size();
        g.drawRect(0,0, d.width - 1, d.height - 1);
    }
        
    /**
     * Puts a little breathing space between
     * the panel and its contents, which lets us draw a box
     * in the paint() method.
     */
    public Insets insets() {
        return new Insets(5,5,5,5);
    }

    /** Executed only when this program runs as an application. */
    public static void main(String[] args) {
        //Create a new window.
        MainFrame f = new MainFrame("Converter Applet/Application");

        //Create a Converter instance.
        Converter converter = new Converter();

        //Initialize the Converter instance.
        converter.init();

        //Add the Converter to the window and display the window.
        f.add("Center", converter);
        f.pack();        //Resizes the window to its natural size.
        f.show();
    }
}


class ConversionPanel extends Panel {
    TextField textField;
    Scrollbar slider;
    Choice unitChooser;
    int min = 0;
    int max = 10000;
    Converter controller;
    Unit[] units;

    ConversionPanel(Converter myController, String myTitle, Unit[] myUnits) {
        //Initialize this ConversionPanel to use a GridBagLayout.
        GridBagConstraints c = new GridBagConstraints();
        GridBagLayout gridbag = new GridBagLayout();
        setLayout(gridbag);

        //Save arguments in instance variables.
        controller = myController;
        units = myUnits;

        //Set up default layout constraints.
        c.fill = GridBagConstraints.HORIZONTAL;

        //Add the label.  It displays this panel's title, centered.
        Label label = new Label(myTitle, Label.CENTER); 
        c.gridwidth = GridBagConstraints.REMAINDER; //It ends a row.
        gridbag.setConstraints(label, c);
        add(label);

        //Add the text field.  It initially displays "0" and needs
        //to be at least 10 columns wide.
        textField = new TextField("0", 10); 
        c.weightx = 1.0;  //This component should use all available horizontal space...
        c.gridwidth = 1; //The default value.
        gridbag.setConstraints(textField, c);
        add(textField);

        //Add the pop-up list (Choice).
        unitChooser = new Choice(); 
        for (int i = 0; i < units.length; i++) { //Populate it.
            unitChooser.addItem(units[i].description);
        }
        c.weightx = 0.0; //The default value.
        c.gridwidth = GridBagConstraints.REMAINDER; //End a row.
        gridbag.setConstraints(unitChooser, c);
        add(unitChooser);

        //Add the slider.  It's horizontal, its initial value is 0,
        //a click increments the value by 100 pixels, and it has the
        //minimum and maximum values specified by the instance variables
        //min and max.
        slider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 100, min, max);
        c.gridwidth = 1; //The default value.
        gridbag.setConstraints(slider, c);
        add(slider);
    }

    /** 
     * Returns the multiplier (units/meter) for the currently
     * selected unit of measurement.
     */
    double getMultiplier() {
        int i = unitChooser.getSelectedIndex();
        return (units[i].multiplier);
    }

    /** Draws a box around this panel. */
    public void paint(Graphics g) {
        Dimension d = size();
        g.drawRect(0,0, d.width - 1, d.height - 1);
    }
        
    /**
     * Puts a little breathing space between
     * the panel and its contents, which lets us draw a box
     * in the paint() method.
     * We add more pixels to the right, to work around a
     * Choice bug.
     */
    public Insets insets() {
        return new Insets(5,5,5,8);
    }

    /**
     * Gets the current value in the text field.
     * That's guaranteed to be the same as the value
     * in the scroller (subject to rounding, of course).
     */
    double getValue() {
        double f;
        try {
            f = Double.valueOf(textField.getText()).doubleValue(); 
        } catch (java.lang.NumberFormatException e) {
            f = 0.0;
        }
        return f;
    }

    /** Respond to user actions on controls. */
    public boolean action(Event e, Object arg) {
        if (e.target instanceof TextField) {
            setSliderValue(getValue());
            controller.convert(this);
            return true;
        }
        if (e.target instanceof Choice) {
            controller.convert(this);
            return true;
        } 
        return false;
    }

    /** Respond to the slider. */
    public boolean handleEvent(Event e) {
        if (e.target instanceof Scrollbar) {
            textField.setText(String.valueOf(slider.getValue()));
            controller.convert(this);
        } 
        return super.handleEvent(e);
    }

    /** Set the values in the slider and text field. */
    void setValue(double f) {
        setSliderValue(f);
        textField.setText(String.valueOf(f));
    }

    /** Set the slider value. */
    void setSliderValue(double f) {
        int sliderValue = (int)f;

        if (sliderValue > max)
               sliderValue = max;
        if (sliderValue < min)
            sliderValue = min;
        slider.setValue(sliderValue);
    }
}


class Unit {
    String description;
    double multiplier;

    Unit(String description, double multiplier) {
        super();
        this.description = description;
        this.multiplier = multiplier;
    }

    public String toString() {
        String s = "Meters/" + description + " = " + multiplier;
        return s;
    }
}

/** Provides a window if this program is run as an application. */
class MainFrame extends Frame {
    MainFrame(String title) {
	super(title);
    } 

    public boolean handleEvent(Event e) {
	if (e.id == Event.WINDOW_DESTROY) {
	    System.exit(0);
	}
	return super.handleEvent(e);
    }
}
