/*  
This applet will plot a line
*/ 


import java.applet.Applet;
import java.awt.*;
import java.util.Date;
import java.util.Random;

// These classes are for Url's.
import java.net.*;
// import an extra class for the ActionListener
import java.awt.event.*;


public class electricity1 extends Applet implements ActionListener {
// Specify variables that will be needed everywhere, anytime here 
	private Image buffer;
	private Graphics bufG;

       	Font bigFont; 
       	Font verybigFont; 
	Font smallFont;
	Font verysmallFont;
	Color whiteColor; 
	Color blackColor; 
	Color greyColor; 
	Color redColor; 
	Color greenColor; 
	Color blueColor; 
	Color orangeColor; 
	Color bgColor; 
	Button resetButton;
	URL base;
	MediaTracker mt;
	myFloatSlider slopeSlider;
        mySlider ydistSlider;
	Image my_start;
	Image my_run;
	Image my_hit;
	boolean firsttime;	
	boolean reset;	

	public void init()  
	{ 
		// Resize, in case the HTML author forgot to
		resize(520, 420);

                // Prepare double-buffer
                buffer = createImage(520, 420);
                bufG = buffer.getGraphics();

		// Tell the applet not to use a layout manager.
                setLayout(null);

		// initialize the MediaTracker
                mt = new MediaTracker(this);

		// The try-catch is necessary when the URL isn't valid
                // Of course this one is valid, since it is generated by
                // Java itself.
		try {
                      // getDocumentbase gets the applet path.
                      base = getDocumentBase();
              	}

		catch (Exception e) {}

		// Get the images
		my_start = getImage(base,"generator.jpg");
		my_run = getImage(base,"generator2.jpg");
		my_hit = getImage(base,"generator3.jpg");
	
        	// Will use Arial as type, 16 as size and bold as style 
	        smallFont = new Font("Arial",Font.BOLD,12); 
	        bigFont = new Font("Arial",Font.BOLD,16); 
	        verybigFont = new Font("Arial",Font.BOLD,24); 
	        verysmallFont = new Font("Arial",Font.BOLD,8); 
	        whiteColor = Color.white; 
	        blackColor = Color.black; 
	        greyColor = Color.gray; 
	        redColor = Color.red; 
	        greenColor = Color.green; 
	        blueColor = Color.blue; 
	        orangeColor = Color.orange; 
	        bgColor = new Color(255, 237, 169);

		resetButton = new Button("Reset");
		resetButton.setBounds(20,385,100,30);

		slopeSlider = new myFloatSlider( -200, 209, 0, 1);
		slopeSlider.setParent(this);
		FontMetrics fm = bufG.getFontMetrics(smallFont);
		int w1 = fm.stringWidth("a: ");
                slopeSlider.setBounds(20+w1,10,200,30);

		ydistSlider = new mySlider( -5000, 5100, 0, 1);
		ydistSlider.setParent(this);
		int w2 = fm.stringWidth("b: ");
                ydistSlider.setBounds(20+w2,45,200,30);

		 // Attach actions to the components
                resetButton.addActionListener(this);

		// Sliders are attached separately, using 'doAction' function
		add(resetButton);
		add(slopeSlider);
		add(ydistSlider);

		// Set first time flag
		firsttime = true;
        } 

	// When the button is clicked this method will get automatically called
        public void actionPerformed(ActionEvent evt)
        {
                // Here we will ask what component called this method
                if (evt.getSource() == resetButton) { 
			firsttime = false;
			reset = true;

			slopeSlider.setValue(0);
			ydistSlider.setValue(0);

                	repaint();
		}
	}

	// When the sliders are adjusted this method will get called
        public void doAction() 
	{
		firsttime = false;
		reset = false;

		// Read current values and repaint
               	repaint();
	}

        public void stop() 
        { 
        } 


	// Update method gets rid of flickering
        public void update(Graphics g)  
        { 
		paint(g);
	}

        // Now lets draw things on screen 
        public void paint(Graphics g)  
        { 
		int ix, iy;
		String s;

		// Draw background
	        bufG.setColor(bgColor); 
		bufG.fillRect(0, 0, 520, 420);

		// Don't do much if first time
		if (!firsttime) {
			// Print run images -- image depends on slider readings
			int ydist = ydistSlider.getValue();
			int islope = slopeSlider.getValue();
		
			if (ydist != 800 || islope != 84)
				bufG.drawImage(my_run,0+77,80,this);
			else
				bufG.drawImage(my_hit,0+77,80,this);
			

		}	// All this if not firsttime
		else {
			// Reset the screen:
			// Print start images
			bufG.drawImage(my_start,0+77,80,this);
		}

		// Write labels
                bufG.setColor(blackColor);
                bufG.setFont(smallFont);
                bufG.drawString("a: ", 20,30);
                bufG.drawString("b: ", 20,65);

		// Draw a coordinate system
                bufG.drawLine(40,350,490,350);
                bufG.drawLine(40,120,490,120);
                bufG.drawLine(40,350,40,120);
                bufG.drawLine(490,350,490,120);
	
                bufG.setFont(verysmallFont);
		for (ix=0;ix<=50000;ix+=5000)
		{
			bufG.drawLine( findX(ix), 350, findX(ix),340 );
			bufG.drawLine( findX(ix), 120, findX(ix),130 );
			s = "" + ix;
			FontMetrics fm = bufG.getFontMetrics(verysmallFont);
			int w1 = fm.stringWidth(s);
			bufG.drawString( s, findX(ix)-w1/2, 370);
		}
		for (iy=-5000;iy<=25000;iy+=5000)
		{
			bufG.drawLine( 40, findY(iy), 50, findY(iy) );
			bufG.drawLine( 490, findY(iy), 480, findY(iy) );
			s = "" + iy;
			FontMetrics fm = bufG.getFontMetrics(verysmallFont);
			int w1 = fm.stringWidth(s);
			bufG.drawString( s, 40-w1-5, findY(iy) + 3 );
		}

                bufG.setFont(smallFont);
		s = "Power Consumption (kWh)";
		bufG.drawString( s, 200, 390);

		s = "Electricity Bill (NOK)";
		bufG.drawString( s, 5, 100);

		// Draw the current bill (a point)
		bufG.setColor(greenColor);
                bufG.fillOval(findX(20000)-4, findY(17600)-4, 10, 10);

		// Print the equation of the line
		bufG.setColor(redColor); 
	        bufG.setFont(verybigFont); 
		s = "y =  " + slopeSlider.getText() + "x + " + ydistSlider.getText();
	        bufG.drawString(s, 150, 240 ); 

		// Print the line
		for (ix=0;ix<50000;ix+=1000) 
		{
			s = slopeSlider.getText();
			double slope = Double.valueOf(s).doubleValue();
			s = ydistSlider.getText();
			double ydist = Double.valueOf(s).doubleValue();
			iy = (int)(ydist + slope*(double)ix);
			if (iy > -5000 && iy < 25000) {
	                	bufG.fillOval(findX(ix)-2, findY(iy)-2, 5, 5);
				
				if (ix>0) {	
					int ixold = ix-1000;	
					int iyold = (int)(ydist + slope*(double)ixold);
					bufG.drawLine(findX(ix),findY(iy),findX(ixold),findY(iyold));
				}
			}

		}

		 // Copy buffer on screen
                g.drawImage(buffer, 0, 0, this);
        } 

	private int findX( int worldX )
	{
		return (int)(40 + (double)(worldX-0)/50000.0*(double)(490-40));
	}

	private int findY( int worldY )
	{
		return (int)(350 - (double)((worldY-(-5000))/30000.0)*(double)(350-120));
	}
} 


// Here comes the integer slider
public class mySlider extends Panel implements ActionListener,
                                             AdjustmentListener {
  private Scrollbar scrollbar;
  private TextField textfield;
  private ScrollbarPanel scrollbarPanel;
  private int preferredWidth = 250;

  private electricity1 el;

  /** Construct a slider with the specified min, max and initial
   *  values. The "bubble" (thumb) size is set to 1/10th the
   *  scrollbar range.
   */

  public mySlider(int minValue, int maxValue, int initialValue) {
    this(minValue, maxValue, initialValue,
         (maxValue - minValue)/10);
  }

  /** Construct a slider with the specified min, max,and initial
   *  values, plus the specified "bubble" (thumb) value. This
   *  bubbleSize should be specified in the units that min and
   *  max use, not in pixels. Thus, if min is 20 and max is 320,
   *  then a bubbleSize of 30 is 10% of the visible range.
   */

  public mySlider(int minValue, int maxValue, int initialValue,
                int bubbleSize) {
    setLayout(new BorderLayout());
    maxValue = maxValue + bubbleSize;
    scrollbar = new Scrollbar(Scrollbar.HORIZONTAL,
                              initialValue, bubbleSize,
                              minValue, maxValue);
    scrollbar.addAdjustmentListener(this);
    scrollbarPanel = new ScrollbarPanel(6);
    scrollbarPanel.add(scrollbar, BorderLayout.CENTER);
    add(scrollbarPanel, BorderLayout.CENTER);
    textfield = new TextField(numDigits(maxValue) + 1);
    textfield.addActionListener(this);
    setFontSize(12);
    textfield.setEditable(false);
    setTextFieldValue();
    add(textfield, BorderLayout.EAST);
  }

  /** Set parent address */
  public void setParent(electricity1 thisEl)
  {
	el = thisEl;
  }

  /** A place holder to override for action to be taken when
   *  scrollbar changes.
   */

  public void doAction(int value) {
        el.doAction();
  }

  /** When textfield changes, sets the scrollbar */

  public void actionPerformed(ActionEvent event) {
    String value = textfield.getText();
    int oldValue = getValue();
    try {
      setValue(Integer.parseInt(value.trim()));
    } catch(NumberFormatException nfe) {
      setValue(oldValue);
    }
  }

  /** When scrollbar changes, sets the textfield */

  public void adjustmentValueChanged(AdjustmentEvent event) {
    setTextFieldValue();
    doAction(scrollbar.getValue());
  }

  /** This returns the current scrollbar value */

  public int getValue() {
    return(scrollbar.getValue());
  }

  /** This assigns the scrollbar value. If it is below the
   *  minimum value or above the maximum, the value is set to
   *  the min and max value, respectively.
   */

  public void setValue(int value) {
    scrollbar.setValue(value);
    setTextFieldValue();
  }

  /** Returns the current textfield string. In most cases this
   *  is just the same as a String version of getValue, except
   *  that there may be padded blank spaces at the left.
   */

  public String getText() {
    return(textfield.getText());
  }

  /** Rather than setting the whole font, you can just set the
   *  size (Monospaced bold will be used for the family/face).
   */

  public void setFontSize(int size) {
    setFont(new Font("Monospaced", Font.BOLD, size));
  }

  /** Determines if the textfield is editable. If it is, you can
   *  enter a number to change the scrollbar value. In such a
   *  case, entering a value outside the legal range results in
   *  the min or max legal value. A non-integer is ignored.
   */

  public boolean isEditable() {
    return(textfield.isEditable());
  }

  /** Determines if you can enter values directly into the
   *  textfield to change the scrollbar.
   */

  public void setEditable(boolean editable) {
    textfield.setEditable(editable);
  }

  // Sets a right-aligned textfield number.

  private void setTextFieldValue() {
    int value = scrollbar.getValue();
    int digits = numDigits(scrollbar.getMaximum());

    if (value > 4990) 
	    value = 5000;

    String valueString = padString(value, digits);
    textfield.setText(valueString);
  }

  // Repeated String concatenation is expensive, but this is
  // only used to add a small amount of padding, so converting
  // to a StringBuffer would not pay off.

  private String padString(int value, int digits) {
    String result = String.valueOf(value);
    for(int i=result.length(); i<digits; i++) {
      result = " " + result;
    }
    return(result + " ");
  }

  // Determines the number of digits in a decimal number.

  private static final double LN10 = Math.log(10.0);

  private static int numDigits(int num) {
    return(1 + (int)Math.floor(Math.log((double)num)/LN10));
  }
}


// Here comes the float slider
/** This is a special version that shows the slider value as 
    decimal number (divided by factor 100) */
public class myFloatSlider extends Panel implements ActionListener,
                                             AdjustmentListener {
  private Scrollbar scrollbar;
  private TextField textfield;
  private ScrollbarPanel scrollbarPanel;
  private int preferredWidth = 250;

  private electricity1 el;

  /** Construct a slider with the specified min, max and initial
   *  values. The "bubble" (thumb) size is set to 1/10th the
   *  scrollbar range.
   */

  public myFloatSlider(int minValue, int maxValue, int initialValue) {
    this(minValue, maxValue, initialValue,
         (maxValue - minValue)/10);
  }

  /** Construct a slider with the specified min, max,and initial
   *  values, plus the specified "bubble" (thumb) value. This
   *  bubbleSize should be specified in the units that min and
   *  max use, not in pixels. Thus, if min is 20 and max is 320,
   *  then a bubbleSize of 30 is 10% of the visible range.
   */

  public myFloatSlider(int minValue, int maxValue, int initialValue,
                int bubbleSize) {
    setLayout(new BorderLayout());
    maxValue = maxValue + bubbleSize;
    scrollbar = new Scrollbar(Scrollbar.HORIZONTAL,
                              initialValue, bubbleSize,
                              minValue, maxValue);
    scrollbar.addAdjustmentListener(this);
    scrollbarPanel = new ScrollbarPanel(6);
    scrollbarPanel.add(scrollbar, BorderLayout.CENTER);
    add(scrollbarPanel, BorderLayout.CENTER);
    textfield = new TextField(numDigits(maxValue) + 2);
    textfield.addActionListener(this);
    setFontSize(12);
    textfield.setEditable(false);
    setTextFieldValue();
    add(textfield, BorderLayout.EAST);
  }

  /** Set parent address */
  public void setParent(electricity1 thisEl)
  {
	el = thisEl;
  }

  /** A place holder to override for action to be taken when
   *  scrollbar changes.
   */

  public void doAction(int value) {
        el.doAction();
  }

  /** When textfield changes, sets the scrollbar */

  public void actionPerformed(ActionEvent event) {
    String value = textfield.getText();
    int oldValue = getValue();
    try {
      setValue(Integer.parseInt(value.trim()));
    } catch(NumberFormatException nfe) {
      setValue(oldValue);
    }
  }

  /** When scrollbar changes, sets the textfield */

  public void adjustmentValueChanged(AdjustmentEvent event) {
    setTextFieldValue();
    doAction(scrollbar.getValue());
  }

  /** This returns the current scrollbar value */

  public int getValue() {
    return(scrollbar.getValue());
  }

  /** This assigns the scrollbar value. If it is below the
   *  minimum value or above the maximum, the value is set to
   *  the min and max value, respectively.
   */

  public void setValue(int value) {
    scrollbar.setValue(value);
    setTextFieldValue();
  }

  /** Returns the current textfield string. In most cases this
   *  is just the same as a String version of getValue, except
   *  that there may be padded blank spaces at the left.
   */

  public String getText() {
    return(textfield.getText());
  }

  /** Rather than setting the whole font, you can just set the
   *  size (Monospaced bold will be used for the family/face).
   */

  public void setFontSize(int size) {
    setFont(new Font("Monospaced", Font.BOLD, size));
  }

  /** Determines if you can enter values directly into the
   *  textfield to change the scrollbar.
   */

  public void setEditable(boolean editable) {
    textfield.setEditable(editable);
  }

  // Sets a right-aligned textfield number.

  private void setTextFieldValue() {
    int value = scrollbar.getValue();
    int myvalue = Math.abs(scrollbar.getValue());
    int myvalue2 = Math.abs(scrollbar.getValue()-100);
    int myvalue3 = Math.abs(scrollbar.getValue()+100);
    int digits = numDigits(scrollbar.getMaximum());
//    String valueString = padString(myvalue, digits);
    String valueString = padString(myvalue, 0);
    String valueString2 = padString(myvalue2, 0);
    String valueString3 = padString(myvalue3, 0);
    if (value >= 0 && value < 10) 
	    textfield.setText("0.0" + valueString);
    else if (value >= 0 && value >= 10 && value < 100) 
	    textfield.setText("0." + valueString);
    else if (value >= 0 && value >= 100 && value < 110) 
	    textfield.setText("1.0" + valueString2);
    else if (value >= 0 && value >= 110 && value < 200) 
	    textfield.setText("1." + valueString2);
    else if (value >= 0 && value >= 200) 
	    textfield.setText("2.00" );
    else if (value >= 0 && value >= 390)
	    textfield.setText("2.00");
    else if (value < 0 && value > -10) 
	    textfield.setText("-0.0" + valueString);
    else if (value < 0 && value <= -10 && value > -100) 
	    textfield.setText("-0." + valueString);
    else if (value < 0 && value <= -100 && value > -200) 
	    textfield.setText("-1." + valueString3);
    else 
	    textfield.setText("-2.00");
  }

  // Repeated String concatenation is expensive, but this is
  // only used to add a small amount of padding, so converting
  // to a StringBuffer would not pay off.

  private String padString(int value, int digits) {
    String result = String.valueOf(value);
    for(int i=result.length(); i<digits; i++) {
      result = " " + result;
    }
    return(result + " ");
  }

  // Determines the number of digits in a decimal number.

  private static final double LN10 = Math.log(10.0);

  private static int numDigits(int num) {
    return(1 + (int)Math.floor(Math.log((double)num)/LN10));
  }
}
