import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.System;
import java.util.ArrayList;
import javax.swing.*;

/*
<applet code="Bezier.class" width=400 height=400>
</applet>
*/

public class Bezier extends Applet
{
    static int WIDTH, HEIGHT;          // 画面サイズ
    Point point[][];                   // 制御点
    int point_num = 0;                 // 制御点の番号
    int captured_point = -1;           // つかんでいる制御点の番号
                                       //（-1のときつかんでいない）
    boolean capture_mode_FLG = false;  // 線を制御できるかを表すフラグ
    boolean add_mode_FLG = false;      //* 線を追加できるかを表すフラグ *//
    Point epoint[];                    //* 追加中の制御点 *//
    int epoint_num = 0;                //* 追加中の制御点の番号 *// 
    int dimnum = 4;                    //* 追加するベジエ曲線の次元+1 *//

    int point_data[][][] =    // 点情報
{
        {{32,230}, {62,208}, {90,198}},
        {{90,198}, {108,170}, {138,146}},
        {{138,146}, {210,116}, {252,120}},
        {{138,146}, {166,170}, {206,190}},
        {{148,246}, {168,210}, {206,190}},
        {{88,198}, {116,224}, {148,246}},
        {{76,290}, {102,258}, {148,246}},
        {{84,280}, {72,294}, {72,306}},
        {{32,230}, {14,246}, {16,264}},
        {{16,260}, {34,288}, {72,306}},
        {{204,190}, {286,168}, {306,152}},
        {{250,120}, {288,132}, {306,152}},
        {{306,152}, {328,158}, {346,174}},
        {{274,130}, {306,116}, {346,112}},
        {{344,114}, {382,126}, {404,148}},
        {{404,148}, {378,158}, {344,172}},
        {{404,148}, {406,172}, {404,186}},
        {{192,274}, {250,248}, {302,230}},
        {{166,262}, {198,254}, {230,308}, {138,350}, {124,274}, {166,262}},
        {{320,208}, {358,192}, {376,264}, {288,290}, {286,222}, {320,208}},
        {{206,200}, {180,216}, {162,244}},
        {{162,244}, {224,236}, {252,216}},
        {{206,200}, {224,192}, {244,188}},
        {{244,188}, {250,204}, {252,216}},
        {{250,186}, {258,200}, {260,216}},
        {{252,186}, {284,172}, {308,164}},
        {{308,164}, {326,168}, {332,180}},
        {{332,180}, {308,202}, {258,216}},
        {{152,208}, {148,220}, {152,240}},
        {{138,196}, {132,220}, {138,240}},
        {{182,108}, {258,58}, {200,62}, {214,108}, {242,100}, {292,56}},
        {{288,58}, {246,132}, {314,56}, {396,92}, {400,40}, {320,96}},
        {{396,224}, {472,310}, {392,420}},
        {{360,248}, {426,326}, {336,416}},
        {{324,266}, {380,340}, {280,406}},
        {{38,233}, {24,242}, {22,256}},
        {{38,269}, {44,255}, {56,246}},
        {{54,246}, {44,237}, {38,232}},
        {{60,252}, {46,264}, {44,276}},
        {{66,292}, {70,280}, {84,268}},
        {{82,268}, {68,256}, {60,252}},
        {{404,186}, {372,202}, {344,214}},
        {{22,254}, {28,262}, {38,270}},
        {{44,274}, {52,282}, {68,292}},
        {{72,306}, {106,298}, {142,286}},
        {{38,384}, {2,378}, {38,356}, {78,354}},
        {{76,354}, {16,432}, {52,424}, {74,394}},
        {{76,392}, {58,410}, {70,424}, {94,406}, {92,390}, {76,392}},
        {{78,392}, {74,410}, {98,390}},
        {{98,388}, {88,410}, {104,414}, {110,388}},
        {{110,388}, {104,406}, {100,420}},
        {{100,420}, {98,432}, {76,436}, {68,422}, {108,410}, {118,398}},
        {{124,392}, {108,402}, {114,416}, {134,408}, {140,390}, {124,392}},
        {{126,392}, {124,406}, {144,388}},
        {{148,382}, {136,398}, {154,390}, {150,408}, {138,404}},
        {{138,404}, {218,282}, {166,280}, {168,344}, {150,404}},
        {{150,404}, {162,368}, {192,374}, {128,436}, {188,396}, {180,390}},
        {{188,374}, {172,404}, {182,410}, {198,388}},
        {{198,388}, {204,380}, {216,378}, {200,400}},
        {{200,400}, {216,372}, {220,386}, {210,400}},
        {{210,400}, {222,376}, {230,382}, {222,396}},
        {{224,392}, {218,402}, {226,404}, {234,396}},
        {{246,388}, {242,376}, {232,384}, {222,410}, {242,412}, {246,388}},
        {{246,388}, {238,410}, {256,402}, {260,386}},
        {{260,386}, {284,330}, {116,306}, {186,402}, {210,360}, {232,322}},
        {{28,428}, {132,408}, {260,404}},
};
  

    ArrayList points = new ArrayList(point_data.length);    
    Label tf;
    Label massage;  //* massageラベル *//

    Graphics offg, backg;
    Image    offi, backi;
 
    public void initialize()                   // 制御点の設定
    {
	point = new Point[point_data.length][];

        for(int i = 0; i < point.length; i++)
        {
            point[i] = new Point[point_data[i].length];
            for(int j = 0; j < point_data[i].length; j++)
                point[i][j] = new Point(point_data[i][j][0],
                                        point_data[i][j][1]);
	}
	for (int i=0; i<point.length; i++)
	    points.add(point[i]);
    }

    public void chmd2add(int n){              //* addモードに変更する *//
	epoint = new Point[dimnum = n];
	for (int i=0; i<dimnum; i++)
	    epoint[i] = new Point();
	epoint_num = 0;
	
	capture_mode_FLG = false;
	switch(dimnum){
	case 3:
	    massage.setText("add mode : click 3 pints");
	    break;
	case 4:
	    massage.setText("add mode : click 4 points");
	    break;
	case 5:
	    massage.setText("add mode : click 5 points");
	    break;
	case 6:
	    massage.setText("add mode : click 6 points");
	    break;
	default:
	    break;
	}
	add_mode_FLG = true;
	repaint();
    }
    
    public void init()
    {
        Dimension d = getSize();
        WIDTH = d.width; HEIGHT = d.height;
	Panel mainPanel,subPanel,subsubPanel;
	int i;

        Button increment, decrement, modify;
	Button add, delete, clear, output;        //* add, delete, clearボタン追加 *//
	ButtonGroup dim = new ButtonGroup();
	JRadioButton dim2,dim3,dim4,dim5;         //* Bezier曲線の次元選択ラジオボタン *//
	dim.add(dim2 = new JRadioButton("2"));
	dim.add(dim3 = new JRadioButton("3",true));
	dim.add(dim4 = new JRadioButton("4"));
	dim.add(dim5 = new JRadioButton("5"));
        increment = new Button("increse(+)");       // 画面設定
        decrement = new Button("decrese(-)");
        modify = new Button("modify");
	tf = new Label(Integer.toString(point_num));

	add = new Button("add");                    //* 追加項目 *//
	delete = new Button("delete");
	clear = new Button("clear");
	output = new Button("output");
        massage = new Label("view mode");
	setLayout(new BorderLayout());

	mainPanel = new Panel();
	mainPanel.add(add);
        mainPanel.add(modify);
        mainPanel.add(increment);
        mainPanel.add(decrement);
 	mainPanel.add(delete);
	mainPanel.add(tf);
	add("North",mainPanel);

	subPanel = new Panel();
	subPanel.setLayout(new GridLayout(1,2));
	subPanel.add(massage);
	    subsubPanel = new Panel();
	    subsubPanel.add(dim2);
	    subsubPanel.add(dim3);
	    subsubPanel.add(dim4);
	    subsubPanel.add(dim5);
	    subsubPanel.add(clear);
	    subsubPanel.add(output);  //点データの吐き出し(コンソールからの実行時のみ)
        subPanel.add(subsubPanel);
	add("South",subPanel);

	add.addActionListener(new ActionListener() //* 点追加モード *//
	{
	    public void actionPerformed(ActionEvent ae){
		chmd2add(dimnum);
	    }
	});

        increment.addActionListener(new ActionListener() // 制御番号を増やす
	{
            public void actionPerformed(ActionEvent ae)
            {
		add_mode_FLG = false;
		capture_mode_FLG = true;
                point_num++;
                if(point_num >= points.size())
                    point_num = 0;
                tf.setText(Integer.toString(point_num));
		massage.setText("modify mode");
                repaint();
            }
        });

        decrement.addActionListener(new ActionListener()  // 制御番号を減らす
        {
            public void actionPerformed(ActionEvent ae)
            {
		if (point.length == 0)
		    return;
		add_mode_FLG = false;
		capture_mode_FLG = true;
                point_num--;
                if(point_num < 0) point_num = point.length-1;
                tf.setText(Integer.toString(point_num));
		massage.setText("modify mode");
               repaint();
            }
        });

        modify.addActionListener(new ActionListener()  // 編集モードに変更
        {
            public void actionPerformed(ActionEvent ae)
            {
		add_mode_FLG = false;
                capture_mode_FLG = !capture_mode_FLG;
		if (capture_mode_FLG)
		    massage.setText("modify mode");
		else
		    massage.setText("view mode");
                repaint();
            }
        });

	
	delete.addActionListener(new ActionListener()  //* 点の削除 *//
	{
	    public void actionPerformed(ActionEvent ae)
	    {
		if (!capture_mode_FLG || points.size() <= 0)
		    return;
		points.remove(point_num);
		point = new Point[points.size()][];
		for(int i = 0; i < points.size(); i++){
		    point[i] = (Point[])points.get(i);
		}
		if (point.length == 0){
		    point_num = 0;
		}
		else if (point_num == points.size())
		    point_num--;
		tf.setText(Integer.toString(point_num));
		massage.setText("deleted");
		repaint();
	    }
	});

	/* Bezier曲線の次元設定 */
	dim2.addActionListener(new ActionListener()
	{
	    public void actionPerformed(ActionEvent as)
	    {
		chmd2add(3);
	    }
	});

	dim3.addActionListener(new ActionListener()
	{
	    public void actionPerformed(ActionEvent as)
	    {
		chmd2add(4);
	    }
	});

	dim4.addActionListener(new ActionListener()
	{
	    public void actionPerformed(ActionEvent as)
	    {
		chmd2add(5);
	    }
	});

	dim5.addActionListener(new ActionListener()
	{
	    public void actionPerformed(ActionEvent as)
	    {
		chmd2add(6);
	    }
	});

	clear.addActionListener(new ActionListener()  //* 画面上の点を全消去 *//
	{
	    public void actionPerformed(ActionEvent as)
	    {
		point_num = 0;
		points.clear();
		point = new Point[0][];
		massage.setText("clear all");
		add_mode_FLG = false;
		capture_mode_FLG = false;
		repaint();
	    }
	});

	// **コンソールへ点データの吐き出し**
	//  コンソールから appletviewer などで実行するときは
	//  output ボタンを押すことで点データを出力できます
	//  枠の外にはみだすように配置してあるので、ウィンドウ
	//  サイズを変更しないと表示されません
	output.addActionListener(new ActionListener()
        {
	    public void actionPerformed(ActionEvent as)
	    {
		int i;
		System.out.print("{\n");
		for (i=0; i<point.length; i++){
		    System.out.print("\t{");
		    for (int j=0; j<point[i].length; j++){
			System.out.print('{');
			System.out.print(point[i][j].x);
			System.out.print(',');
			System.out.print(point[i][j].y);
			System.out.print("}, ");
		    }
		    System.out.print("\b\b},\n");
		}
		System.out.print("}\n");
		System.out.print(i);
		System.out.println(" Curves");
	    }
	});
	//**  **  **//

        addMouseListener(new MouseAdapter()
        {
            public void mousePressed(MouseEvent me)    // 制御点をつかむ
            {
                int i, mx = me.getX(), my = me.getY();
		if (!capture_mode_FLG)
		    return;
                for(i = 0; i < point[point_num].length; i++)
                {
                    if(point[point_num][i].x-3 <= mx &&
                       point[point_num][i].x+3 >= mx &&
                       point[point_num][i].y-3 <= my &&
                       point[point_num][i].y+3 >= my)
                        break;
                }
                if(i < point[point_num].length) captured_point = i;
            }

            public void mouseReleased(MouseEvent me)   //* 点追加 *//
            {
		Point[] tmp;
		if (add_mode_FLG){
		    epoint[epoint_num++].setLocation(me.getX(), me.getY());
		    if (epoint_num >= dimnum){
			points.add(epoint);
			point = new Point[points.size()][];
			for (int i=0; i < points.size(); i++){
			    tmp = (Point[])points.get(i);
			    point[i] = new Point[tmp.length];
			    for (int j=0; j<tmp.length; j++)
				point[i][j] = new Point(tmp[j].x, tmp[j].y);
			}
			points.clear();
			for (int i=0; i<point.length; i++)
			    points.add(point[i]);
			for (int i=0; i<dimnum; i++)
			    epoint[i] = new Point();
			epoint_num = 0;
		    }
		    repaint();
		}
		else
		    captured_point = -1;
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() // 制御点を動かす
        {
            public void mouseDragged(MouseEvent me)
            {
                if(captured_point == -1) return;
                point[point_num][captured_point].setLocation(me.getX(), me.getY());
                repaint();
            }
        });

        initialize();

        offi = createImage(WIDTH, HEIGHT);
        offg = offi.getGraphics();
        backi = createImage(WIDTH, HEIGHT);
        backg = backi.getGraphics();
        setBackImage(backg);

     }

    public void setBackImage(Graphics g)    // 背景の用意
    {
        g.setColor(Color.white);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.black);
        g.drawRect(0, 0, WIDTH-1, HEIGHT-1);  // 枠描画
    }

    public int kaijyo(int num)            // 階乗を計算
    {
        int total = 1;

        if(num == 0) return 1;

        for(; num > 1; num--)
            total *= num;
        return total;
    }

    public void BezierCurve(Graphics g, Color color, Point point[])  // 曲線描画
    {
        int i, j;
        double x1, x2, y1, y2;
        double bernstein[] = new double[point.length];
        x1 = point[0].x;
        y1 = point[0].y;

        g.setColor(color);

        for(double t = 0; t <= 1; t += 0.01)
        {
            for(i = 0; i < point.length; i++)        // bernstein関数を計算
            {
                bernstein[i] = (double)kaijyo(point.length-1)
                            / (kaijyo(i)*kaijyo(point.length-1-i));

                for(j = 1; j <= i; j++) bernstein[i] *= t;
                for(j = 1; j <= point.length-1-i; j++) bernstein[i] *= (1-t);
            }

            x2 = y2 = 0;
            for(i = 0; i < point.length; i++)
            {
                x2 += point[i].x * bernstein[i];
                y2 += point[i].y * bernstein[i];
            }

            g.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
            x1 = x2;
            y1 = y2;
        }
    }
    
    public void paint(Graphics g)
    {
        requestFocus();
        offg.drawImage(backi, 0, 0, this);  // 背景の描画

        if(capture_mode_FLG && points.size()>0)           // 制御点の描画
        {
            offg.setColor(Color.red);
            for(int i = 0; i < point[point_num].length; i++)
            {
                offg.fillRect(point[point_num][i].x-3,
                              point[point_num][i].y-3, 7, 7);
                offg.drawString(Integer.toString(i+1),
				point[point_num][i].x-3,
				point[point_num][i].y-6);
            }
        }

	if(add_mode_FLG){               //* 追加中の制御点描画 *//
	    offg.setColor(Color.blue);
	    for (int i=0; i<epoint_num; i++){
		offg.fillRect(epoint[i].x-3,
			      epoint[i].y-3, 7, 7);
		offg.drawString(Integer.toString(i+1),
				epoint[i].x-3,
				epoint[i].y-6);
	    }
	}

        for(int i = 0; i < point.length; i++)     // 曲線描画
        {
            if(capture_mode_FLG && point_num == i)
                BezierCurve(offg, Color.blue, point[i]);
            else BezierCurve(offg, Color.black, point[i]);
        }

        g.drawImage(offi, 0, 0, this);
    }

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