/**
 * BuddyBar is a control made up of a label, a text input field, and a scrollbar.
 * Together, they allow a user to input a single real number.
 */

import java.awt.*;
import java.awt.event.*;
import java.util.*;

// This panel listens for adjustments of its scrollbar and entries in its text field.
public class BuddyBar implements AdjustmentListener, ActionListener {
	// barValue is the real number this buddy bar currently represents.
	double barValue;
	int iSlideMax; // The scrollbar registers in integers from zero to iSlideMax.
	// The scrollbar is there for intuitive play so its positions represent real
	// numbers in a reasonable range.
	double slideMin, slideMax;
	// The user can, however, enter less reasonable numbers in the text field.
	double textMin, textMax;
	Label textLabel;
	TextField tField; // text input field
	Scrollbar slippery; // scrollbar
	Vector Listeners; // list of objects to notify when the barValue changes.

	public BuddyBar(String message, double fMin, double fMax,double tMin,double tMax,double init)
	{
		String label = new String(message);
		// assign values from the initializer to class variables.
		slideMin = fMin;
		slideMax = fMax;
		textMin = tMin;
		textMax = tMax;
		barValue = init;

		textLabel = new Label(label);
		
		// new text input field 12 characters wide initialized to our initial value.
		tField = new TextField(Double.toString(barValue),12);
		// Tell the textField to notify us when someone hits enter after typing a number.
		tField.addActionListener(this);

		// make a scrollbar with 200 increments and a scrollbutton 10% of the total bar size.
		iSlideMax = 200;
		slippery = new Scrollbar(Scrollbar.HORIZONTAL,0,10,0,iSlideMax);
		// Ask the scrollbar to tell us when someone scrolls.
		slippery.addAdjustmentListener(this);
		// This routine sets the scrollbar to agree with the current barValue.
		setSlide();

		// Initialize the list of listeners to our buddybar.  There will usually only
		// be one.
		Listeners = new Vector(5);
	}

	public TextField getTextField()
	{
		return tField;
	}

	public Label getLabel()
	{
		return textLabel;
	}

	public Scrollbar getScrollbar()
	{
		return slippery;
	}

	// Tell our listeners that the barValue of this buddybar has changed.
	private void changeValue()
	{
		for (int i = 0; i<Listeners.size(); i++) {
			BuddyListener ting = (BuddyListener) Listeners.elementAt(i);
			ting.buddyValueChanged(this,barValue);
		}
	}

	// Returns the current value of the buddybar.
	public double getValue()
	{
		// barValue isn't always completely up to date because, while it does get updated
		// any time the scrollbar moves, it only reads the textfield when someone hits
		// the enter key.  In case someone typed in a number and didn't hit enter,
		// we read whatever is in the textfield each time someone asks us for barValue.
		readTextField();
		return barValue;
	}

	// implementation of actionListener.  Called when someone hits enter in a textField.
	public void actionPerformed(ActionEvent e) {
		double oldVal = barValue;
		this.readTextField();
		if (oldVal != barValue)
			changeValue();
	}

	private void readTextField()
	{
		String s1 = tField.getText();
		try {
			Double dVal = new Double(s1);
			double newval = dVal.doubleValue();
			if (newval<textMin) {
				barValue = textMin;
				tField.setText(Double.toString(barValue));
			} else if (newval>textMax) {
				barValue = textMax;
				tField.setText(Double.toString(barValue));
			}
			barValue = newval;
			setSlide(); // In case the value is changed, make sure the scrollbar is correct.
		} catch (NumberFormatException nfe) {
			tField.setText(Double.toString(barValue));
			// This exception happens when the textField doesn't contain a number.
		}
	}
	
	// Called when the scrollbar is moved.
	public void adjustmentValueChanged(AdjustmentEvent evt) {
		int iSlideVal = evt.getValue();
		// calculate real value of from integer scrollbar position.
		barValue = slideMin+iSlideVal*(slideMax-slideMin)/iSlideMax;
		// set the textField to agree.
		tField.setText(Double.toString(barValue));
		// Tell our listeners it changed.
		// We don't need to do this if it is slow.  Doing this allows us to have the
		// scrollbar control something else (like the angle of a cannon in a picture)
		// while you slide the control.
		changeValue();
	}

	// make the slide position agree with the barValue.
	private void setSlide()
	{
		if (barValue<=slideMax)
			slippery.setValue((int)(iSlideMax*(barValue-slideMin)/(slideMax-slideMin)));
		else if (barValue>slideMax)
			slippery.setValue(iSlideMax);
		else if (barValue<0)
			slippery.setValue(0);
	}

	// This interface is called BuddyBar.BuddyListener by other objects.
	// An object implementing this interface, or implementing the method shown below,
	// can register itself as a buddyListener with the next method.
	public interface BuddyListener
	{
		// This method is called for every listener every time the real value of
		// a buddybar changes.
		public void buddyValueChanged(BuddyBar bbar, double val);
	}

	public void addBuddyListener(BuddyListener bl)
	{
		Listeners.addElement(bl);
	}
}
