/* refeerence
 * http://www.ke.ics.saitama-u.ac.jp/kondo/lect/cg/Bezier-koide/Bezier.java
 * http://java.sun.com/j2se/1.4/ja/docs/ja/api/index.htmlおよび以下
 *
 *
 * Vectorについて
 * http://www.techscore.com/tech/Java/Utility/1.html
 * 最終更新日 '05/5/31
 */
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.net.URL;

/*
<applet code="BezierWriter.class" width=400 height=400>
</applet>
*/
public class BezierWriter extends Applet 
    implements MouseMotionListener,MouseListener
{
    // --- Applet Propaty ---
    static String title = "BezierWriter";
    static int WIDTH, HEIGHT;        // 画面サイズ

    int cpn_w;// = 48; // コンパネサイズ
    int cpn_h;    
    boolean cpnFlg = true;           // コンパネ右、左
    // --- Bezier Line ----
    Vector curve = new Vector();     // 制御点

    int point_num = 0;               // 制御点の番号
    int captured_point = -1;         // つかんでいる制御点の番号
    //（-1のときつかんでいない）
    boolean capture_mode_FLG;        // 線を制御できるかを表すフラグ

    Bezier newBzr;                   // 追加用
    int newBzrP = 0;                 // 
    int newBzrPoints = 3;            // 追加の点数Dimension
    
    int pen_x=-1,pen_y=-1;
    int pen_red=0,pen_green=0,pen_blue=0;
    Color pen_c=new Color(pen_red,pen_green,pen_blue);
    boolean cooFlg = false;          // 座標表示切り替え
    boolean flushFlg = true;         // backg初期化
    
    int pen_breadth = 0;             // 線幅
    final char FM_CAP = 0;
    final char FM_BRD = 1;
    final char FM_DIM = 2;
    final char FM_END = 3;
    int fluc_mode = FM_CAP;          //増減モード
    
    final char M_ADD = 0;
    final char M_MOD = 1;
    final char M_PNT = 2;
    final char M_DRW = 3;
    final char M_PIC = 4;
    final char M_END = 5;
    int mode = M_ADD;                 //操作モード

    // ---  Graphics Variable  ----

    Graphics offg, backg , picg;
    BufferedImage offi,backi;
    Image pic;

    boolean pic_on = false;            //背景にBG.gifを表示
    
    //---- Window Tool ----
    Panel cpn_p;
    String bName[]={"add","modify","paint","draw","picture"};
    String bName2[]={"point_num","breadth","dimension"};
    Button mode_b, increment, decrement, move_b 
	, fluc_b, coord_b , delete, clear, pic_b;
    Scrollbar bar_r,bar_g,bar_b;

				//[曲線b1...bn][点列a1...an][x,y]
    //    static final int point_data[][][] =    // 点情報
    Label tf;                                   //数値表示用ラベル
    /*
    public void initialize(){                   // 制御点の設定
	System.out.println("initialize");
	curve = new Vector();
	for(int i=0; i<point_data.length; i++){
	    Bezier bzr = new Bezier();
	    for(int j=0; j<point_data[i].length; j++){
		bzr.add( point_data[i][j][0],
			 point_data[i][j][1]);
	    }
	    if( bzr == null )
		System.out.println("NULL");
	    if( curve == null )
		System.out.println("NULL");
	    curve.add( bzr );
	}
    }
    */
    /*
    static public void main(String arg[]){
	Frame frm = new Frame(title);
	BezierWriter bw = new BezierWriter(480,480);
	frm.setSize(480+4+4,480+4+12);
	frm.add(bw);
	bw.init();

	frm.setVisible(true);
    }
    public BezierWriter(int w,int h){;}
    */

    public void init(){
	// Applet proparty
	Dimension d = getSize();
        WIDTH = d.width; HEIGHT = d.height;
	cpn_w = WIDTH / 8;
	cpn_h = HEIGHT -30;
	setBackground( Color.gray );
	setLayout( null );

        //initialize(); // from point_data to curve

	// Graphics	
	System.out.println("MyCanvas_Constractor_Graphics");
        //offi = createImage(WIDTH, HEIGHT);
	pic = getImage( getCodeBase(), "./BGP.GIF" );

	offi = new BufferedImage(WIDTH,HEIGHT
				 , BufferedImage.TYPE_4BYTE_ABGR );
	if(offi!=null){  offg = offi.getGraphics(); }
        //backi = (BufferedImage)createImage(WIDTH, HEIGHT);
	backi = new BufferedImage(WIDTH, HEIGHT
				  , BufferedImage.TYPE_4BYTE_ABGR );
	if(backi!=null){   backg = backi.getGraphics();
	setBackImage(backg);              }
	
	// Button
	cpn_p = new Panel();
	Scrollbar sb;
	cpn_p.setSize(cpn_w,cpn_h);
	cpn_p.setLayout( null );

	mode_b = new Button(bName[0]);  	cpn_p.add(mode_b);
	mode_b.setLocation(0,0); mode_b.setSize(cpn_w,30);
	
        decrement = new Button("<<");        cpn_p.add(decrement);
	decrement.setLocation(0,30); decrement.setSize(cpn_w/2,30);	

        increment = new Button(">>");        cpn_p.add(increment);
	increment.setLocation(cpn_w/2,30); increment.setSize(cpn_w/2,30);	

        tf = new Label("CAT :"+Integer.toString(point_num));
        cpn_p.add(tf);	tf.setLocation(0,60); tf.setSize(cpn_w,30);	

	fluc_b = new Button(bName2[FM_CAP]);	cpn_p.add(fluc_b);
	fluc_b.setLocation(0,90); fluc_b.setSize(cpn_w,30);

	coord_b = new Button("Coordinate");	cpn_p.add(coord_b);
	coord_b.setLocation(0,120); coord_b.setSize(cpn_w,30);

	delete = new Button("delete");	cpn_p.add(delete);
	delete.setLocation(0,150); delete.setSize(cpn_w,30);

	clear = new Button("clear");	cpn_p.add(clear);
	clear.setLocation(0,180); clear.setSize(cpn_w,30);

        move_b = new Button("move");        cpn_p.add(move_b);
	move_b.setLocation(0,210); move_b.setSize(cpn_w,30);

        pic_b = new Button("pic");        cpn_p.add(pic_b);
	pic_b.setLocation(0,240); pic_b.setSize(cpn_w,30);	

	bar_r = new Scrollbar( Scrollbar.VERTICAL,255,1,0,255 );
	cpn_p.add(bar_r);    bar_r.setLocation(0,300);  bar_r.setSize(cpn_w/3,150);
	
	bar_g = new Scrollbar( Scrollbar.VERTICAL,255,1,0,255 );
	cpn_p.add(bar_g);    bar_g.setLocation(cpn_w/3,300);  bar_g.setSize(cpn_w/3,150);
	
	bar_b = new Scrollbar( Scrollbar.VERTICAL,255,1,0,255 );
	cpn_p.add(bar_b);    bar_b.setLocation(2*cpn_w/3,300);  bar_b.setSize(cpn_w/3,150);
	
	mode_b.addActionListener(new ActionListener(){
		public void actionPerformed(ActionEvent ev){
		    System.out.println("mode_b");
		    mode++;
		    if( mode >= M_END )mode=M_ADD;
		    mode_b.setLabel(bName[mode]);

		    newBzrP  = 0; //init
		    capture_mode_FLG = false;
		    if(mode==M_MOD){//modify
			capture_mode_FLG = true;
		    }
		    repaint();
		}	    });
        increment.addActionListener(new ActionListener(){ // 制御点番号を増やす
		public void actionPerformed(ActionEvent ae){
		    if(fluc_mode==FM_CAP){
			point_num++;
			if(point_num >= curve.size())
			    point_num = 0;
			tf.setText("CAP: "+Integer.toString(point_num));
			repaint();
		    }else if(fluc_mode==FM_BRD){
			if(pen_breadth < 5)pen_breadth++;
			tf.setText("BRD: "+Integer.toString(pen_breadth));
		    }else if(fluc_mode==FM_DIM){
			if(newBzrPoints < Bezier.MAX_POINTS)newBzrPoints++;
			tf.setText("DIM: "+Integer.toString(newBzrPoints-1));
		    }
		}	    });
        decrement.addActionListener(new ActionListener(){  // 制御点番号を減らす
		public void actionPerformed(ActionEvent ae){
		    if(fluc_mode==FM_CAP){
			point_num--;
			if(point_num < 0) point_num = curve.size()-1;
			tf.setText("CAP: "+Integer.toString(point_num));
			repaint();
		    }else if(fluc_mode==FM_BRD){
			if(pen_breadth > 0)pen_breadth--;
			tf.setText("BRD: "+Integer.toString(pen_breadth));
		    }else if(fluc_mode==FM_DIM){
			if(newBzrPoints > 3)newBzrPoints--;
			tf.setText("DIM: "+Integer.toString(newBzrPoints-1));
		    }
		}	    });
        fluc_b.addActionListener(new ActionListener(){  // 制御点番号を減らす
		public void actionPerformed(ActionEvent ae){
		    fluc_mode++;
		    if(fluc_mode>=FM_END)fluc_mode=0;
		    fluc_b.setLabel(bName2[fluc_mode]);
		    if(fluc_mode==FM_CAP)
			tf.setText("CAP: "+Integer.toString(point_num));
		    else if(fluc_mode==FM_BRD)
			tf.setText("BRD: "+Integer.toString(pen_breadth));
		    else if(fluc_mode==FM_DIM)
			tf.setText("DIM: "+Integer.toString(newBzrPoints-1)); 
		}	    });
        coord_b.addActionListener(new ActionListener(){ // 座標表示
		public void actionPerformed(ActionEvent ae){
		    cooFlg = !cooFlg;		    repaint();
		}	    });
        delete.addActionListener(new ActionListener(){  // delete
		public void actionPerformed(ActionEvent ae){
		    if(mode==M_MOD){
			curve.remove(point_num);	point_num = 0;
			tf.setText("CAP: 0");		repaint();    }
		}	    });
        clear.addActionListener(new ActionListener(){  // clear
		public void actionPerformed(ActionEvent ae){
		    flushFlg = true;		    repaint();
		}	    });
	move_b.addActionListener(new ActionListener(){  // コンパネ移動
		public void actionPerformed(ActionEvent ae){
		    cpnFlg = !cpnFlg;
		    if(cpnFlg){
			cpn_p.setLocation(WIDTH-cpn_w,0);
		    }else{
			cpn_p.setLocation(0,0);
		    }
		    flushFlg = true;   repaint();
		}	    });
	pic_b.addActionListener(new ActionListener(){  // ピクチャon/off
		public void actionPerformed(ActionEvent ae){
		    pic_on = !pic_on;
		    repaint();
		}	    });
	bar_r.addAdjustmentListener(new AdjustmentListener(){
		public void adjustmentValueChanged(AdjustmentEvent ev){
		    pen_red = 255-ev.getValue();
		    pen_c=new Color(pen_red,pen_green,pen_blue);
		    fillSelectColor();   repaint();		    
		}
	    });
	bar_g.addAdjustmentListener(new AdjustmentListener(){
		public void adjustmentValueChanged(AdjustmentEvent ev){
		    pen_green = 255-ev.getValue();
		    pen_c=new Color(pen_red,pen_green,pen_blue);
		    fillSelectColor();   repaint();		    
		}	    });
	bar_b.addAdjustmentListener(new AdjustmentListener(){
		public void adjustmentValueChanged(AdjustmentEvent ev){
		    pen_blue = 255-ev.getValue();
		    pen_c=new Color(pen_red,pen_green,pen_blue);
		    fillSelectColor();   repaint();
		}	    });


	add(cpn_p);
	cpn_p.setLocation(WIDTH-cpn_w,0);

	addMouseListener(this);
	addMouseMotionListener(this);

	// test!
        addKeyListener(new KeyAdapter(){
		public void keyPressed(KeyEvent ke){
		    int c = ke.getKeyCode();
		    if(c == 'P'){
			Bezier bzr = (Bezier)curve.get(point_num);
			System.out.println("{");
			for(int i = 0; i< bzr.getPoints();  i++ )
			    System.out.println(""+bzr.getX(i)+", "+bzr.getY(i));
			System.out.println("},");
		    }else if(c == 'O'){
			System.out.println("{");
			for(int j=0;j<curve.size();j++){
			    int i;
			    Bezier bzr = (Bezier)curve.get(j);
			    System.out.print("{ ");
			    for(i = 0; i< bzr.getPoints()-1;  i++ )
				System.out.print("{"+bzr.getX(i)+","+bzr.getY(i)+"},");
			    System.out.print("{"+bzr.getX(i)+","+bzr.getY(i)+"}");
			    if(j==curve.size()-1)
				System.out.println(" }");
			    else
				System.out.println(" },");
			}
			System.out.println("};");			
		    } else if(c == 'B') {
			// BezierCurve(backg, Color.black);
			repaint();
		    } else if(c == ke.VK_Q){
		    }
		}
	    });
	repaint();
	System.out.println("init_end");
    }

    public void fillSelectColor(){
	backg.setColor(new Color(pen_red,pen_green,pen_blue) );// 選択色
	if(cpnFlg)
	    backg.fillRect(WIDTH-cpn_w, cpn_h, cpn_w, HEIGHT-cpn_h);
	else
	    backg.fillRect(0, cpn_h, cpn_w, HEIGHT-cpn_h);
    }

    public void setBackImage(Graphics g){    // 背景の用意
	System.out.println("background white");
	g.setColor(Color.white);
	g.fillRect(0, 0, WIDTH, HEIGHT);
	g.setColor(Color.black);
	g.drawRect(0, 0, WIDTH-1, HEIGHT-1);  // 枠描画
	
    }

    public void paint(Graphics g){
	requestFocus();
	
	if(offi==null){ // サイズ作成の前後より。役にはたっていない
	    //offi = createImage(WIDTH, HEIGHT);
	    offi = new BufferedImage(WIDTH,HEIGHT
				     , BufferedImage.TYPE_4BYTE_ABGR );
	    offg = offi.getGraphics();
	}
	if(backi==null){   
	    //backi = (BufferedImage)createImage(WIDTH, HEIGHT);
	    backi = new BufferedImage(WIDTH,HEIGHT
				      , BufferedImage.TYPE_4BYTE_ABGR );
	    backg = backi.getGraphics();
	    setBackImage(backg);
	}

	if( mode==M_PIC){
	    if(pic==null){
		System.out.println("there is not BGP.GIF !");
		g.drawImage(backi, 0, 0, this);
		g.setColor( Color.black );
		g.drawString("NO PICTURE!",200,200);
	    }else
		g.drawImage(pic,0,0,this);
	}else if( mode == M_DRW ){//draw mode
	    g.drawImage(backi, 0, 0, this);  // 背景の描画
	}else{// 通常表示

	    if( flushFlg ){  // 背景の描画
		setBackImage(backg);
		flushFlg = false;
	    }
	    if( pic_on && pic!=null)
		offg.drawImage(pic , 0, 0,this);
	    else
		offg.drawImage(backi, 0, 0, this);

	    fillSelectColor(); //現在色描画	

	    if(mode==M_MOD)
		if(capture_mode_FLG && curve.size()>0){           // 制御点の描画
		    offg.setColor(Color.red);
		    Bezier bzr = (Bezier)curve.get(point_num);
		    for(int i = 0; i < bzr.getPoints(); i++ ) {
			Point p = bzr.getPoint(i) ;
			offg.fillRect(p.x-3, p.y-3, 7, 7);
			offg.drawString(Integer.toString(i+1), p.x-3,  p.y-6);
		    }
		}

	    for(int i = 0; i < curve.size() ; i++){     // 曲線描画
		Bezier bzr = (Bezier)curve.get(i);
		if(capture_mode_FLG && point_num==i && mode==M_MOD )
		    bzr.drawBezier(offg, Color.blue ,-1);
		else
		    bzr.drawBezier(offg, null, -1);
	    }

	    if(mode==M_MOD && cooFlg){           	// draw coordinate
		Bezier bzr = (Bezier)curve.get(point_num);
		offg.setColor(Color.black);
		for(int i=0;i<bzr.getPoints();i++)
		    if(cpnFlg)
			offg.drawString("("+bzr.getX(i)+","+bzr.getY(i)+")",i*55+cpn_w+5,HEIGHT-20);
		    else
			offg.drawString("("+bzr.getX(i)+","+bzr.getY(i)+")",i*55,HEIGHT-20);
	    }
	    g.drawImage(offi, 0, 0, this);
	}

    }

    public void update(Graphics g) {  paint(g);  }
    
    //--- 塗りつぶし----
    public void chgColor(int x,int y,int newc){
	Point p = new Point(x,y);
	Point q;
	ArrayList que = new ArrayList();
	int c;
	int oldc = backi.getRGB(p.x,p.y);
	int nc = 0xFF000000|newc;
	boolean check[][] = new boolean[HEIGHT+1][WIDTH+1];
	for(int i=0;i<HEIGHT+1;i++)
	    for(int j=0;j<WIDTH+1;j++)
		check[i][j] = true;
	que.add(p);
	check[y][x] = false;
	
	while( que.size()>0 ){
	    p = (Point)que.remove(0);
	    backi.setRGB(p.x,p.y,nc);
	    check[p.y][p.x] = false;
	    if(p.x<WIDTH-1)
		if( offi.getRGB(p.x+1,p.y)==oldc)
		    if( check[p.y][p.x+1] ){
			q = new Point(p.x+1,p.y);
			check[q.y][q.x] = false;
			que.add(q);		    }
	    if(p.y<HEIGHT-1)
		if( offi.getRGB(p.x,p.y+1)==oldc)
		    if( check[p.y+1][p.x] ){
			q = new Point(p.x,p.y+1);
			check[q.y][q.x] = false;
			que.add(q);		    }
	    if(p.y>0)
		if( offi.getRGB(p.x,p.y-1)==oldc)
		    if( check[p.y-1][p.x]){
			q = new Point(p.x,p.y-1);
			check[q.y][q.x] = false;
			que.add(q);		    }
	    if(p.x>0)
		if( offi.getRGB(p.x-1,p.y)==oldc )
		    if( check[p.y][p.x-1] ){
			q = new Point(p.x-1,p.y);		
			check[q.y][q.x] = false;
			que.add(q);		    }
	}
    }
    // -------  Lister  -------------
    public  void mouseDragged(MouseEvent ev){
	if( mode == M_MOD ){ //modify
	    if(captured_point == -1) return;
	    Bezier bzr = (Bezier)curve.get(point_num);
	    bzr.setLocation(captured_point, ev.getX(), ev.getY() );
	    repaint();
	}else if(mode == M_DRW){ //draw
	    if( pen_x == -1 ){
		pen_x = ev.getX();
		pen_y = ev.getY();
	    }else{
		double m;
		backg.setColor( pen_c );
		m = (double)(ev.getY()-pen_y)/(double)(ev.getX()-pen_x);
		if( -1 < m && m < 1 )
		    for(int mm=-pen_breadth;mm<=pen_breadth;mm++)
			backg.drawLine(ev.getX(),ev.getY()+mm,pen_x,pen_y+mm);
		else
		    for(int mm=-pen_breadth;mm<=pen_breadth;mm++)
			backg.drawLine(ev.getX()+mm,ev.getY(),pen_x+mm,pen_y);

		pen_x = ev.getX();
		pen_y = ev.getY();
		repaint();
	    }
	}
    }
    public void mouseMoved(MouseEvent ev){}

    public void mouseClicked(MouseEvent ev){
	System.out.println("Click"+mode
			   +"("+ev.getX()+","+ev.getY()+")");
	if(mode==M_ADD){//add
	    if(newBzrP==0){
		newBzr = new Bezier();
		newBzr.setBreadth( pen_breadth );
		newBzr.setColor( pen_c );
		newBzr.add(ev.getX(),ev.getY());
		newBzrP++;
	    }else if(++newBzrP<newBzrPoints){
		newBzr.add(ev.getX(),ev.getY());
	    }else{
		newBzr.add(ev.getX(),ev.getY());		
		newBzrP = 0;
		curve.add(newBzr);
		repaint();
	    }
	}else if(mode==M_PNT){
	    // paint
	    int chgc=0xFF;
	    chgc <<= 8;  chgc |= pen_red;
	    chgc <<= 8;  chgc |= pen_green;
	    chgc <<= 8;  chgc |= pen_blue;
	    chgColor(ev.getX(), ev.getY(),chgc );
	    repaint();
	}
    }
    public void mouseEntered(MouseEvent ev){}
    public void mouseExited( MouseEvent ev){}
    public void mousePressed(MouseEvent ev){
	System.out.println("pressed");
	if( mode == M_MOD ){ // modify
	    int i, mx = ev.getX(), my = ev.getY();
	    Bezier bzr = (Bezier)curve.get(point_num);
	    for(i = 0; i < bzr.getPoints() ; i++){
		if(bzr.getX(i)-3 <= mx &&
		   bzr.getX(i)+3 >= mx &&
		   bzr.getY(i)-3 <= my &&
		   bzr.getY(i)+3 >= my)
		    break;
	    }
	    if(i < bzr.getPoints() ) captured_point = i;
	    repaint();
	}
    }
    public void mouseReleased(MouseEvent ev){
	if( mode==M_MOD ){// modify
	    captured_point = -1;
	}else if(mode == M_DRW){ // pen
	    pen_x=-1; pen_y=-1;
	}
    }
}

class Bezier{
    public Vector points = new Vector();
    public static final short MAX_POINTS = 7;
    //private short pointcount = 0;
    private Color mycolor;
    private int breadth;
    
    public Bezier(){
	//System.out.println("Bezier Constractor");
	mycolor = Color.black;
	breadth=0;
    }
    public void setColor(Color c){	mycolor = c;    }
    public Color getColor(){ return mycolor; }
    public void setBreadth(int BRD){	breadth = BRD;    }
    public int getBreadth(){ return breadth; }
    public int getX(int index){//indexの点のX座標を返す
	return ((Point)points.get(index)).x;
    }
    public int getY(int index){//indexの点のY座標を返す
	return ((Point)points.get(index)).y;
    }
    public Point getPoint(int index){//indexの点を返す
	return (Point)points.get(index);
    }
    /*    public int size(){
	  return points.size();
	  }*/
    public int getPoints(){ // 保持点の数
	return points.size();
	//return pointcount;
    }
    public void setLocation( int index, int x,int y ){//indexの点の座標を修正
	Point p = getPoint(index);
	p.x = x;
	p.y = y;
	points.set(index, p);
    }
    public void add(int x,int y){//その点を加える
	Point p = new Point(x,y);
	add(p);
    }

    public void add(Point p){
	//if( pointcount < MAX_POINTS ){
	if( points.size() < MAX_POINTS ){
	    //pointcount++;
	    points.add(p);
	}
    }
    public void remove(int index){
	if( 0 <= index && index < points.size() )
	    points.remove(index);
    }
    public void drawBezier(Graphics g, Color clr ,int brd )  // 曲線描画
    { //サンプルよりほぼそのまま引用
	int i, j;
	double x1, x2, y1, y2;
	double bernstein[] = new double[points.size()];
	Point p = (Point)points.get(0);
	Color color;
	int brdth;
	if( clr == null ){ color = mycolor ; }
	else {color = clr; }
	if( brd < 0 ){ brdth = breadth; }
	else { brdth = brd; }
	x1 = p.x;	y1 = p.y;

	g.setColor(color);
	for(double t = 0; t <= 1; t += 0.01){
	    for(i = 0; i < points.size(); i++){        // bernstein関数を計算
		bernstein[i] = (double)kaijyo(points.size()-1)
		    / (kaijyo(i)*kaijyo(points.size()-1-i));
		for(j = 1; j <= i; j++) bernstein[i] *= t;
		for(j = 1; j <= points.size()-1-i; j++) bernstein[i] *= (1-t);
	    }

	    x2 = y2 = 0;
	    for(i = 0; i < points.size(); i++){
		p = (Point)points.get(i);
		x2 += p.x * bernstein[i];
		y2 += p.y * bernstein[i];
	    }

		double m;
		m = (y1-y2)/(x1-x2);
		if( -1 < m && m < 1 )
		    for(int mm=-brdth;mm<=brdth;mm++)
			g.drawLine((int)x1,(int)(y1+mm),(int)x2,(int)(y2+mm));
		else
		    for(int mm=-brdth;mm<=brdth;mm++)
			g.drawLine((int)(x1+mm),(int)y1,(int)(x2+mm),(int)y2);

	    
		//g.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
	    x1 = x2;	    y1 = y2;
	}
    }

    private int kaijyo(int num){            // 階乗を計算
	int total = 1;
	if(num == 0) return 1;
	for(; num > 1; num--) total *= num;
	return total;
    }
}

