import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;

public class Bezier extends Applet implements AdjustmentListener
{
    BezierCanvas drawZone;
    Label statusLabel = new Label ("Loading...");
    boolean debugging = false;
    Scrollbar hsbNsteps;

    public void init ()
    {
	setLayout (new BorderLayout(2, 2));

	drawZone = new BezierCanvas ();
	Panel controlPanel = new Panel ();
	add ("South", controlPanel);
	add ("Center", drawZone);
	
	int nsteps = 20;
	hsbNsteps = new Scrollbar (Scrollbar.HORIZONTAL, 20, 10, 1, 60);
	hsbNsteps.addAdjustmentListener (this);
	controlPanel.setLayout (new GridLayout (2, 1, 2, 2));
	controlPanel.add (hsbNsteps);
	controlPanel.add (statusLabel);
	setBackground (Color.black);
    }

    public void start ()
    {
	statusMsg ("Running...");
    }

    public void statusMsg (String msg)
    {
	if (!debugging)
	{
	    statusLabel.setText ("Status:     " + msg);
	}
    }

    public void debugMsg (String msg)
    {
	debugging = true;
	statusLabel.setText ("Debug: " + msg);
    }


	//////////////////////////////////////////
	//	AdjustmentListener actions
	//////////////////////////////////////////
    public synchronized void adjustmentValueChanged(AdjustmentEvent e)
    {
	drawZone.setSteps (e.getValue ());
	statusMsg ("Precision level is now " + e.getValue () + " lines.");
	drawZone.repaint ();
	//try {wait ();}catch (InterruptedException ex) {}
    }

};

class BezierCanvas extends Canvas
    implements MouseListener, MouseMotionListener
{
    Dimension boardSize;
    Vector2d [] ctrlPts = new Vector2d [4];
    int nsteps;
    Image Buffer = null;
    Graphics bufGraphics = null;

    public BezierCanvas ()
    {
	nsteps = 20;

	addMouseListener (this);
	addMouseMotionListener (this);
    }

    public void setBounds (int x, int y, int w, int h)
    {
	super.setBounds (x, y, w, h);
	
	boardSize = new Dimension (w, h);
	Buffer = createImage (w, h);
	bufGraphics = Buffer.getGraphics ();

	for (int i = 0; i < 4; i ++)
	{
	    ctrlPts [i] = new Vector2d ((i+1) * (boardSize.width/5),
		boardSize.height/2);
	}
    }

    public void setSteps (int n)
    {
	nsteps = n;
    }

    public void drawOffscreen ()
    {
	//Fill in the background
	bufGraphics.setColor (Color.black);
	bufGraphics.fillRect (0, 0, boardSize.width, boardSize.height);

	//Draw the four points
	bufGraphics.setColor (Color.white);
	String d = "";
	for (int i = 0; i < 4; i ++)
	{
	    bufGraphics.fillRect ((int) ctrlPts [i].x - 2, (int) ctrlPts [i].y - 2, 
			5, 5);
	    
	    d = d + "x[" + i + "] = " + (int) ctrlPts[i].x +
		", y[" + i + "] = " + (int) ctrlPts[i].y + ";   ";
	    
	}
	//debugMsg (d);

	//Draw the Bezier curve
	bufGraphics.setColor (Color.blue);

	double [] B = new double [4];
	Vector2d lastPt = new Vector2d ();
	for (int step = 0; step <= nsteps; step ++)
	{
	    double t = (double) step / (double) nsteps;
	    double at = 1.-t;
	    B [0] = at*at*at;
	    B [1] = 3.*t*at*at;
	    B [2] = 3.*t*t*at;
	    B [3] = t*t*t;

	    Vector2d nextPt = new Vector2d ();
	    nextPt.set (0.,0.);
	    d = "";
	    for (int p = 0; p < 4; p ++)
	    {
		nextPt.x += ctrlPts [p].x * B[p];
		nextPt.y += ctrlPts [p].y * B[p];
	    }
	    if (step > 0)
	    {
	        bufGraphics.drawLine ((int)(lastPt.x+.5), (int)(lastPt.y+.5),
			    (int)(nextPt.x+.5), (int)(nextPt.y+.5));
	    }
	    lastPt.set (nextPt.x, nextPt.y);
	}
    }


    public synchronized void paint (Graphics g)
    {
	drawOffscreen ();
	g.drawImage (Buffer, 0, 0, null);
	notifyAll ();
    }

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

	//////////////////////////////////////////
	//	MouseListener actions
	//////////////////////////////////////////
    int dragging = -1;

    public void mouseEntered (MouseEvent e)
    {
    }
    public void mouseExited (MouseEvent e)
    {
    }
    public void mousePressed (MouseEvent e)
    {
	for (int i = 0; i < 4; i ++)
	{
	    if (Math.abs (e.getX()-ctrlPts [i].x) < 5
		&& Math.abs (e.getY()-ctrlPts [i].y) < 5)
		dragging = i;
	}
    }
    public void mouseReleased (MouseEvent e)
    {
	dragging = -1;
    }
    public void mouseClicked (MouseEvent e)
    {
    }

	//////////////////////////////////////////
	//	MouseEventListener actions
	//////////////////////////////////////////
    public void mouseMoved (MouseEvent e)
    {
    }
    public synchronized void mouseDragged (MouseEvent e)
    {
	if (dragging >= 0)
	{
	    //statusMsg ("Dragged dot #" + dragging + " to ("
	    //+ e.getX () + ", " + e.getY () + ")");
	    ctrlPts [dragging].set (e.getX(), e.getY());
	}
	repaint ();
	//try {wait ();}catch (InterruptedException ex) {}
    }
}
