import java.util.ArrayList;
import java.io.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.Dimension;
import java.io.*;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.ImageIcon;
import java.awt.Button;
import java.awt.Graphics;
import java.beans.*;
import java.awt.Shape;
import java.awt.image.BufferedImage;



public class Spline extends JFrame implements MouseListener, MouseMotionListener {
 	Point[] points;	//points to be interpolated
	Point[] control;	//control points
	int width = 500;
	int height = 500;
	int numpoints;
	BufferedImage offscreenImg;	//used in double buffering
	Graphics offscreenG;
	double t;	//time variable
	static final double k = .05; //partition length
	int moveflag;	//point movement



	public Spline() {
		super("Splines!");
		setPreferredSize(new Dimension(500, 300));
		setSize(new Dimension(500, 300));

		numpoints = 4;
		points = new Point[numpoints];
		control = new Point[numpoints];

		moveflag = numpoints;
		int increment = (int)((width-60)/(numpoints-1));
		for(int i=0;i<numpoints;i++) {
			control[i] = new Point((i+1)*50,(i+1)*50);
			points[i] = new Point((i*increment)+30,(int)(height/2));
		}

		//create offscreen buffer
		offscreenImg = new BufferedImage(width,height,java.awt.image.BufferedImage.TYPE_INT_RGB);
		offscreenG = offscreenImg.getGraphics();

		this.setVisible(true);
		addMouseListener(this);
		addMouseMotionListener(this);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	}
	
	//this method is called by the repaint() method
	public void update(Graphics g) {
		paint(g);
	}
	
	public void paint(Graphics g) {
		//points to be plotted
		int x,y;
		//Clear screen and set colors
		setBackground(Color.white);
		offscreenG.setColor(Color.white);
		offscreenG.fillRect(0,0,width,height);
		offscreenG.setColor(Color.black);

		points[0] = control[0];
		points[numpoints-1] = control[numpoints-1];
		
		
		points[1].x = (int)(3.0*control[1].x - 0.833*points[0].x + 0.333*points[3].x - 1.5*control[2].x);
		points[1].y = (int)(3.0*control[1].y - 0.833*points[0].y + 0.333*points[3].y - 1.5*control[2].y);

		points[2].x = (int)(3.0*control[2].x + 0.333*points[0].x - 0.833*points[3].x - 1.5*control[1].x);
		points[2].y = (int)(3.0*control[2].y + 0.333*points[0].y - 0.833*points[3].y - 1.5*control[1].y); 
		

		
		//Plot points
		offscreenG.setColor(Color.RED);
		for(int i=0;i<numpoints;i++) offscreenG.fillOval(points[i].x-2,points[i].y-2,4,4);
		
		offscreenG.setColor(Color.GREEN);
		for(int i=0;i<numpoints;i++) offscreenG.fillOval(control[i].x-2,control[i].y-2,4,4);
		
		offscreenG.setColor(Color.BLACK);
		
		/* plot the actual Bezier curve */
		double x1,x2,y1,y2;
		x1 = points[0].x;
		y1 = points[0].y;
		for(t=k;t<=1+k;t+=k) {
			//use Berstein polynomials
			
			x2=(points[0].x+t*(-points[0].x*3+t*(3*points[0].x-points[0].x*t)))
			+ t*(3*points[1].x+t*(-6*points[1].x+points[1].x*3*t))
			+ t*t*(points[2].x*3-points[2].x*3*t)
			+ t*t*t*points[3].x;
			
			y2=(points[0].y+t*(-points[0].y*3+t*(3*points[0].y-
			points[0].y*t)))+t*(3*points[1].y+t*(-6*points[1].y+
			points[1].y*3*t))+t*t*(points[2].y*3-points[2].y*3*t)+
			points[3].y*t*t*t;
			//draw curve
			offscreenG.drawLine((int)x1,(int)y1,(int)x2,(int)y2);
			x1 = x2;
			y1 = y2;
		}
	
		//draw buffered image to screen
		g.drawImage(offscreenImg,0,0,this);
	}
	
	
	
	
	
	//Check if user has clicked on point
	//public boolean mouseDown(Event evt, int x, int y) {
	public void mousePressed(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();

		Point p = new Point(x,y);
		for(int i=0;i<numpoints;i++) {
			for(int j=-2;j<3;j++)
				for(int l=-2;l<3;l++)
					if(p.equals(new Point(control[i].x+j,control[i].y+l))) {
						//set moveflag to the ith point
						moveflag=i;
						return;
					}
		}
	}
	
	

	public void mouseDragged(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();
		//check if user is trying to drag an old point
		if(moveflag < numpoints) {
			//move the point and redraw screen
			control[moveflag].move(x,y);
			repaint();
		}
	}
	
	
	//if user unclicks mouse, reset moveflag
	    public void mouseReleased(MouseEvent e) {
		moveflag = numpoints;
	}

	public void mouseMoved(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}

	public static void main (String[] args) throws IOException {
		Spline spline = new Spline();
	}
}