import java.applet.*;
import java.awt.*;
import java.awt.event.*;

/*
<applet code="Bezier.class" width=480 height=480>
</applet>
*/

public class m0104020_Bezier extends Applet
{
	static int WIDTH, HEIGHT;			// 画面サイズ
	Point point[][];							// 制御点
	int point_num = 0;						// 制御点の番号
	int captured_point = -1;				// つかんでいる制御点の番号
													//（-1のときつかんでいない）
    boolean capture_mode_FLG;			// 線を制御できるかを表すフラグ

	static final int point_data[][][] =		// 点情報
	{
		{ 	{90,336},	{108,256},	{156,216},  {270,192},	},	//胴体1
		{ 	{264,210},	{264,144},	{322,140},  {360,104},	},	//背びれ1
		{ 	{326,192},	{322,156},	{360,104}, 	},	//背びれ2
		{ 	{324,188},	{360,180},	{394,184}, 	},	//胴体2
		{ 	{394,184},	{450,178},	{456,56},  {550,52}	},	//尾びれ1
		{ 	{550,52},	{458,186},	{512,270}, 	},	//尾びれ2
		{ 	{512,270},	{484,264},	{474,244}, 	},	//尾びれ2
		{ 	{474,244},	{452,204},	{412,224}, 	},	//尾びれ3
		{ 	{412,224},	{304,336},	{250,364}, 	},	//胴体3
		{ 	{370,264},	{412,256},	{400,236}, 	},	//はらびれ??
		{ 	{208,354},	{246,352},	{280,372}, {240,380}, 	},	//鰓ビレ??
		{ 	{240,380},	{220,388},	{196,360}, 	},	//鰓ビレ2??
		{ 	{100,358},	{120,382},	{208,370}, 	},	//胴体4
		{ 	{104,290},	{90,294},	{98,310}, 	},	//左目玉
		{ 	{300,328},	{376,316},	{342,292}, 	},	//腹ビレ??2
	  	{ 	{169,334},	{244,324},	{360,240}, 	},	//ライン1
		{ 	{163,342},	{232,334},	{308,276}, 	},	//ライン2
		{ 	{138,248},	{126,282},	{128,304}, 	},	//ライン3
		{ 	{128,304},	{170,280},	{264,292}, {308,255},	},	//ライン4
	  	{ 	{151,335},	{160,317},	{169,335}, 	},	//右目玉1
	  	{ 	{151,335},	{160,353},	{169,335},	},	//右目玉2
	  	{	{88,338},	{80,336},	{86,348},	},	//口1
	  	{	{88,338},	{100,348},	{118,350},	},	//口2
	  	{	{118,350},	{124,354},	{118,358},	},	//口3
	  	{	{118,358},	{94,360},	{86,348},	},	//口4
	  	{	{90,344},	{96,352},	{116,354},	},	//口5
    };

    Label tf;

    Graphics offg, backg;
    Image    offi, backi;

    public void initialize()	// 制御点の設定
    {
        int i;
        point = new Point[point_data.length][];

        for(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]);
        }
    }

    public void init()
    {
        Dimension d = getSize();
        WIDTH = d.width; HEIGHT = d.height;

        Button increment, decrement, modify;
        increment = new Button("線番号＋");	// 画面設定
        decrement = new Button("線番号−");
        modify = new Button("制御点表示");
        tf = new Label(Integer.toString(point_num));
        add(modify);
        add(increment);
        add(decrement);
        add(tf);

        increment.addActionListener(new ActionListener()	// 制御点番号を増やす
        {
            public void actionPerformed(ActionEvent ae)
            {
                point_num++;
                if(point_num >= point.length)
                    point_num = 0;
                tf.setText(Integer.toString(point_num));
                repaint();
            }
        });

        decrement.addActionListener(new ActionListener()	// 制御点番号を減らす
        {
            public void actionPerformed(ActionEvent ae)
            {
                point_num--;
                if(point_num < 0) point_num = point.length-1;
                tf.setText(Integer.toString(point_num));
                repaint();
            }
        });

        modify.addActionListener(new ActionListener()	// モード変更
        {
            public void actionPerformed(ActionEvent ae)
            {
                capture_mode_FLG = !capture_mode_FLG;
                repaint();
            }
        });

        addMouseListener(new MouseAdapter()
        {
            public void mousePressed(MouseEvent me)	// 制御点をつかむ
            {
                int i, mx = me.getX(), my = me.getY();
                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)
            {
                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);

		// test
        addKeyListener(new KeyAdapter()
        {
            public void keyPressed(KeyEvent ke)
            {
                int c = ke.getKeyCode();
                if(c == 'P')
                {
                    System.out.println("{");
                    for(int i = 0; i < point[point_num].length; i++)
                        System.out.println(""+point[point_num][i].x
                                        +", "+point[point_num][i].y);
                    System.out.println("},");
                }
                else if(c == 'B')
                {
                    // BezierCurve(backg, Color.black);
                    repaint();
                }
            }
        });
    }

    public void setBackImage(Graphics g)	// 背景の用意
    {
        g.setColor(new Color(200,255,255));
        g.fillRect(0, 0, WIDTH, HEIGHT);

        g.setColor(new Color(150,255,255));
        for(int i=0; i<=10; i++){
			double x=Math.random()*500;
			double y=Math.random()*500;
			double r=Math.random()*100;

			g.fillOval((int)x,(int)y,(int)r,(int)r);
		}

      	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)           // 制御点の描画
        {
            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);
            }
        }

        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);
    }
}
