import java.awt.*;

//ベジェ曲線を描画するクラス
//IBezierCurveインターフェースを実装する
public class BezierCurve implements IBezierCurve{
	//メンバ
	int dim;					//ベジェ曲線の次数
	int thick = 1;				//ベジェ曲線の太さ
	boolean isClose = false;	//開曲線か閉曲線か
	boolean isFill = false;		//塗りつぶしか否か
	int N = 50;					//曲線の刻み
	Point[] point;				//制御点
	int icount = 0;				//制御点入力カウンタ
	boolean isDrawing = true;	//現在、制御点入力中か？ trueだとベジェ曲線を描画しない
	boolean isHideHint = false;	//入力点や入力点を結ぶ線を隠すか
	
	//コンストラクタ
	public BezierCurve(int dim, int thick, boolean isClose, boolean isFill){
		this.dim = dim;
		this.thick = thick;
		this.isClose = isClose;
		this.isFill = isFill;
		
		point = new Point[dim +1];
	}
	
	
	
	//n番目の制御点をセットするメソッド
	public void setPoint(Point p, int n){
		if( n < dim+1){
			point[n] = new Point(p);
			if(icount < dim+1)icount++;
			//System.out.println("set"+n);
			//System.out.println("  x="+point[n].x);
			//System.out.println("  y="+point[n].y);
		}
	}
	
	//n番目の制御点を返すメソッド
	public Point getPoint(int n){
		if( n < dim+1){
			return point[n].getLocation();
		}
		else{
			Point p = new Point();
			p.x=0;p.y=0;
			return p;
		}
	}
	
	//制御点入力完了したら呼ぶメソッド
	//	これを呼ぶことで描画できるようになる
	public void setPointEnd(){
		isDrawing = false;
	}
	
	//制御点の数を返すメソッド
	public int size(){
		return icount;
	}
	
	//入力点などを表示させるかの状態を変更するメソッド
	public void setHint(boolean f){
		isHideHint = f;
	}
	
	//入力点自体の描画の状態を変更するメソッド
	public void changeHint(boolean f){
		isHideHint = f;
	}
	
	//階乗を計算するメソッド
	private int kaijyo(int num){
        int total = 1;

        if(num == 0) return 1;

        for(; num > 1; num--)
            total *= num;
        return total;
    }
	
	//ベジェ曲線を描画するメソッド
	public void drawBezier(Graphics g, Color color){
		int i,j,k;
		double x1, x2, y1, y2;
		double bernstein[] = new double[point.length];	//バーンスタイン関数
		Polygon polygon = new Polygon();	//ポリゴンクラス(太さthickの線の代用)
		x1 = point[0].x;
		y1 = point[0].y;
		
		//入力点などを描画
		if(!isHideHint){
			g.setColor(Color.green);
			for(i = 1; i < icount; i++){
				g.drawLine(point[i-1].x, point[i-1].y, point[i].x, point[i].y);
			}
			g.setColor(Color.red);
			for(i = 0; i <icount; i++){
				g.fillOval(point[i].x -3, point[i].y -3, 6, 6);
			}
		}
		
		//ベジェ曲線自体の描画
		g.setColor(color);
		if(!isDrawing){	//制御点入力中はベジェ曲線自体は描画しない
			for(i = 0; i <= N; i++){
			
				//バーンスタイン関数を計算
				double t = (double)i / (double)N;
				for(j = 0; j <point.length; j++){
					bernstein[j] = (double)kaijyo(point.length-1)
									/ (kaijyo(j)*kaijyo(point.length-1-j));
					
					for(k = 1; k <= j; k++)bernstein[j] *= t;
					for(k = 1; k <= point.length-1-j; k++)bernstein[j] *= (1-t);
				}
			
				//描画する点を計算
				x2 = y2 = 0;
				for(j = 0; j <point.length; j++){
					x2 += point[j].x * bernstein[j];
					y2 += point[j].y * bernstein[j];
				}
				//g.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
				polygon.addPoint((int)x1-thick, (int)y1-thick);
				polygon.addPoint((int)x2-thick, (int)y2-thick);
				polygon.addPoint((int)x2+thick, (int)y2+thick);
				polygon.addPoint((int)x1+thick, (int)y1+thick);
				g.fillPolygon(polygon);
				x1 = x2;
				y1 = y2;
				polygon.reset();
			}
		}
	}
	

}
