import java.awt.*;

//閉ベジェ曲線を描画するクラス
//IBezierCurveインターフェースを実装する
public class BezierCurveClose implements IBezierCurve{
	//メンバ
	int dim;					//ベジェ曲線の次数
	int thick = 1;				//ベジェ曲線の太さ
	int N = 50;					//曲線の刻み
	Point[] point;				//入力制御点
	//Point[] point2;				//内部制御点
	int icount = 0;				//制御点入力カウンタ
	boolean isDrawing = true;	//現在、制御点入力中か？ trueだとベジェ曲線を描画しない
	boolean isHideHint = false;	//入力点や入力点を結ぶ線を隠すか
	
	//コンストラクタ
	public BezierCurveClose(int dim, int thick){
		this.dim = dim;
		this.thick = thick;
		
		point = new Point[dim];
		//point2 = new Point[dim];
	}
	
	
	
	//n番目の制御点をセットするメソッド
	public void setPoint(Point p, int n){
		if( n < dim){
			point[n] = new Point(p);
			if(icount < dim)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){
			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;
    }
	
	//ベジェ曲線を描画するメソッド(閉曲線ver)
	//内部的に、n次元のベジェ曲線をn個の2次元のベジェ曲線で表す
	public void drawBezier(Graphics g, Color color){
		int i,j,k,l;
		Point[] point2 = new Point[dim];
		//double x1, x2, y1, y2;
		//double bernstein[][] = new double[dim][3];	//バーンスタイン関数
		//Polygon polygon = new Polygon();	//ポリゴンクラス(太さthickの線の代用)
		//x1 = (point[0].x + point[1].x)/2;
		//y1 = (point[0].y + point[1].y)/2;
		
		//入力点などを描画
		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);
			}
			if(!isDrawing)
				g.drawLine(point[icount-1].x, point[icount-1].y, point[0].x, point[0].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 < 3; i++)
				point2[i] = new Point();
		
			//内部制御点の計算
			for(i = 0; i < dim; i++){
				point2[0].setLocation((point[i].x + point[(i+1)%dim].x)/2 , (point[i].y + point[(i+1)%dim].y)/2);
				point2[1].setLocation(point[(i+1)%dim].x, point[(i+1)%dim].y);
				point2[2].setLocation((point[(i+1)%dim].x + point[(i+2)%dim].x)/2, (point[(i+1)%dim].y + point[(i+2)%dim].y)/2);

				//System.out.println("B"+i);
				//System.out.println(""+point2[0].x +" "+ point2[0].y);
				//System.out.println(""+point2[1].x +" "+ point2[1].y);
				//System.out.println(""+point2[2].x +" "+ point2[2].y);
				drawBezierEx(g, color, point2);
			}
		}
	}
	
	public void drawBezierEx(Graphics g, Color color, Point[] p){
		int i,j,k;
		double x1, x2, y1, y2;
		double bernstein[] = new double[p.length];	//バーンスタイン関数
		Polygon polygon = new Polygon();	//ポリゴンクラス(太さthickの線の代用)
		x1 = p[0].x;
		y1 = p[0].y;
		
		g.setColor(color);
		
		for(i = 0; i <= N; i++){
			
			//バーンスタイン関数を計算
			double t = (double)i / (double)N;
			for(j = 0; j <3; j++){
				bernstein[j] = (double)kaijyo(2)
									/ (kaijyo(j)*kaijyo(2-j));
					
				for(k = 1; k <= j; k++)bernstein[j] *= t;
				for(k = 1; k <= 2-j; k++)bernstein[j] *= (1-t);
			}
			
			//描画する点を計算
			x2 = y2 = 0;
			for(j = 0; j < 3; j++){
				x2 += p[j].x * bernstein[j];
				y2 += p[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();
		}
	}

}
