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

/*
<applet code="m0104287_bezier.class" width=500 height=500>
</applet>
*/

public class m0104287_bezier extends Applet
{
    static int WIDTH, HEIGHT;
    Point point[][];
    int point_num = 0;
	int captured_point = -1;
    boolean capture_mode_FLG = false;
    boolean add_mode_FLG = false;
    Point epoint[];
    int epoint_num = 0;
    int dimnum = 4;

    int point_data[][][] =
	{
        {{163,142}, {149,136}, {159,120}, {170,125}, {175,137}, {163,142}},
        {{157,142}, {142,132}, {156,114}, {171,115}, {181,134}, {168,143}},
        {{240,141}, {229,138}, {232,123}, {244,124}, {254,137}, {240,141}},
        {{232,141}, {227,137}, {226,120}, {241,112}, {264,128}, {244,145}},
        {{167,147}, {186,137}, {195,119}, {210,137}, {229,147}},
        {{152,140}, {146,148}, {107,126}, {142,164}, {174,181}},
        {{248,141}, {241,154}, {308,119}, {252,167}, {226,182}},
        {{139,148}, {184,173}, {199,95}, {208,171}, {255,150}},
        {{141,149}, {178,162}, {199,165}, {220,165}, {254,150}},
        {{174,136}, {185,129}, {202,105}, {216,132}, {230,137}},

        {{131,144}, {114,209}, {102,299}, {102,319}, {145,365}},
        {{268,143}, {281,239}, {302,290}, {286,343}, {244,369}},
        {{246,367}, {268,433}, {247,500}},
        {{237,368}, {263,432}, {239,500}},
        {{143,363}, {122,421}, {137,500}},
        {{151,368}, {126,427}, {147,519}},
        {{150,368}, {187,384}, {237,371}},
        {{151,162}, {200,185}, {244,165}},
        {{128,166}, {122,213}, {119,245}},
        {{134,174}, {130,206}, {125,247}},

        {{112,267}, {124,266}, {132,276}, {111,278}, {113,267}},
        {{115,248}, {125,252}, {132,266}, {108,259}, {115,247}},
        {{111,290}, {123,291}, {130,311}, {113,301}, {111,292}},
        {{117,257}, {117,258}, {116,267}},
        {{117,272}, {117,283}, {116,292}},
        {{127,247}, {128,278}, {127,309}},
        {{116,300}, {133,359}, {142,220}, {146,364}, {165,300}},
        {{122,245}, {135,270}, {148,188}, {148,285}, {171,244}},
        {{144,241}, {144,259}, {143,300}},
        {{148,241}, {147,265}, {147,281}},

        {{150,245}, {150,265}, {150,278}},
        {{170,245}, {194,207}, {201,382}, {208,92}, {211,330}, {239,237}},
        {{163,305}, {184,248}, {208,496}, {196,89}, {212,406}, {241,299}},
        {{238,243}, {249,225}, {256,290}, {275,242}},
        {{239,305}, {248,269}, {256,350}, {277,303}},
        {{277,255}, {260,259}, {267,272}, {279,262}, {279,256}},
        {{278,265}, {266,274}, {264,285}, {282,277}, {279,266}},
        {{280,291}, {264,296}, {256,307}, {284,304}, {280,291}},
        {{276,239}, {276,250}, {276,256}},
        {{277,261}, {276,269}, {273,265}},

        {{277,272}, {274,286}, {276,292}},
        {{276,300}, {275,307}, {276,303}},
        {{122,246}, {115,228}, {116,249}},
        {{165,252}, {164,275}, {164,294}},
        {{168,249}, {167,267}, {168,277}},
        {{174,241}, {174,260}, {174,270}},
        {{276,239}, {276,252}, {276,268}},
        {{182,245}, {182,277}, {182,303}},
        {{188,254}, {188,288}, {188,311}},
        {{204,251}, {203,277}, {203,285}},

        {{207,242}, {207,253}, {207,267}},
        {{209,245}, {209,258}, {209,275}},
        {{214,250}, {214,271}, {214,281}},
        {{219,254}, {219,276}, {219,307}},
        {{233,255}, {233,276}, {233,294}},
        {{233,255}, {233,276}, {233,294}},
        {{240,242}, {240,262}, {240,277}},
        {{255,259}, {255,278}, {255,297}},
        {{250,252}, {250,261}, {250,273}},
        {{245,242}, {245,264}, {245,279}},

        {{265,259}, {265,280}, {265,295}},
        {{268,167}, {282,235}, {281,257}},
        {{281,251}, {281,275}, {279,296}},
        {{263,177}, {270,220}, {273,245}}
};


    ArrayList points = new ArrayList(point_data.length);
    Label tf;
    Label 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)
	{
		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;
        WIDTH = 500; HEIGHT = 500;
		Panel mainPanel,subPanel,subsubPanel;
		int i;
		Button increment, decrement, modify;
		Button add, delete, clear, output;
		Button stamp;
		ButtonGroup dim = new ButtonGroup();
		JRadioButton dim2,dim3,dim4,dim5;
		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();
	    }
	});


	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();
	    }
	});

	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(1, 1, WIDTH-2, HEIGHT-2);
	}

    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[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);
			offg.drawString("now F", 5, 45);
            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);

			  offg.drawString("[" + Integer.toString(point[point_num][i].x) + ", "
			  						+  Integer.toString(point[point_num][i].y) + "]", 10, 50 + 12 * ( i + 1));

            }
        }

	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);
    }
}
