import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class homework 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[][][] =    // 点情報
        {
			//月
			{{58,257},{74,412},{235,461},{380,412},{396,243}},
			{{58,257},{74,66},{235,48},{380,66},{396,257}},
			//うす
			{{194,268},{226,289},{260,270}},
			{{194,268},{202,276},{200,285}},
			{{260,270},{251,277},{251,286}},
			{{200,285},{202,307},{191,327}},
			{{251,286},{248,301},{256,324}},
			{{191,327},{228,345},{256,324}},
			//餅つき
			{{232,278},{237,265},{247,255}},
			{{247,255},{240,242},{230,240}},
			{{230,240},{228,245},{222,252}},
			{{219,256},{215,264},{209,270}},
			{{209,270},{210,274},{216,276}},
			{{219,256},{198,244},{182,225}},
			{{222,252},{208,231},{190,217}},
			{{190,217},{183,218},{182,225}},
			//うさぎ（右）
			{{286,256},{275,243},{285,224},{299,221},{313,232}},
			{{313,232},{332,212},{353,226}},
			{{353,226},{340,242},{318,239}},
			{{318,239},{328,247},{316,257}},
			{{316,257},{334,264},{341,283},{330,308}},
			{{330,308},{342,320},{348,336},{338,340},{323,327},{322,311}},
			{{322,311},{309,316},{291,315},{279,294},{278,274},{286,256}},
			{{303,310},{302,326},{293,334},{284,332},{284,316},{296,304}},
			{{336,286},{342,284},{347,292},{344,295},{344,301},{334,299}},
			{{305,226},{311,207},{334,201}},
			{{334,201},{328,216},{320,225}},
			{{285,264},{270,256},{258,257},{262,270},{270,275},{282,277}},
			{{307,268},{295,271},{290,283},{296,291},{306,285},{314,277}},
			//うさぎ（左）
			{{171,171},{193,162},{217,183},{199,211},{173,198}},
			{{173,198},{177,203},{169,210}},
			{{169,210},{180,211},{186,220},{185,232},{176,231},{162,215}},
			{{162,215},{157,222},{147,227},{138,223}},
			{{138,223},{148,233},{144,263},{134,259},{127,239},{129,224}},
			{{129,224},{107,218},{103,194},{125,172},{148,166},{163,180}},
			{{163,180},{161,174},{164,173}},
			{{164,173},{146,162},{148,137}},
			{{148,137},{167,145},{171,170}},
			{{182,168},{177,149},{195,139}},
			{{194,139},{196,156},{189,170}},
			{{121,221},{114,243},{108,245},{103,249},{96,226},{117,215}},
			{{113,199},{103,200},{102,189},{110,183},{115,187},{117,193}},
			{{171,206},{190,201},{198,223},{181,218}},
			//雲（右下右）
			{{359,386},{358,366},{375,362},{389,376}},
			{{384,385},{392,357},{415,353},{426,372}},
			{{426,372},{438,352},{453,356},{461,376}},
			{{461,376},{474,375},{475,418},{450,407}},
			{{458,408},{450,431},{429,433},{420,414}},
			{{420,414},{417,430},{387,431},{376,415}},
			{{376,415},{352,417},{342,400},{359,386}},
			//雲（右下左）
			{{280,426},{283,406},{307,404},{310,415}},
			{{310,415},{318,403},{335,401},{341,413}},
			{{341,413},{349,399},{372,428},{363,431}},
			{{363,431},{387,433},{389,450},{363,455}},
			{{363,455},{367,474},{348,471},{336,458}},
			{{336,458},{326,469},{315,469},{302,452}},
			{{302,452},{295,466},{273,458},{275,443}},
			{{275,443},{261,433},{268,418},{283,417}},
			//雲（左上）
			{{20,88},{26,63},{44,63},{60,79}},
			{{52,71},{62,52},{95,56},{102,78}},
			{{102,78},{125,77},{132,100},{105,103}},
			{{105,103},{119,120},{103,127},{81,121}},
			{{82,114},{84,135},{63,137},{59,113}},
			{{60,119},{45,139},{31,128},{29,118}},
			{{41,115},{10,129},{7,97},{21,89}},
            };

    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()
    {
        WIDTH = 480; HEIGHT = 480;

        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(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)           // 制御点の描画
        {
            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);
    }
}