import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import javax.swing.*;
import javax.imageio.ImageIO;

/* <applet code="EmpPengo.class" width=800 height=800></applet> */

public class EmpPengo extends Applet {
	private static final int CAPTURE_REGION = 4, T_SPLIT = 100;
	private static final String SZ_RETURN = "\r\n";
	
	private static final int TOOL_SELECT = 0, TOOL_BEZIER = 1, TOOL_EDIT = 2, TOOL_ADD = 3, TOOL_DELETE = 4, TOOL_NUM = 5;
	private static final String[] SZ_TOOL = { "Select", "Bezier", "Edit", "Add", "Delete" };
	private static final String SZ_WALLPAPER = "";
	private static final int[][][] POINT_DATA = {
		{ { 264, 40 }, { 160, 16 }, { 130, 12 }, { 62, 128 }, { 14, 293 }, { 61, 402 }, { 112, 155 }, { 121, 26 }, { 117, 36 }, { 297, 81 } },
		{ { 439, 152 }, { 511, 80 }, { 636, 188 }, { 584, 93 }, { 464, 116 } },
		{ { 440, 142 }, { 462, 89 }, { 527, 99 }, { 614, -11 }, { 766, 75 }, { 715, 248 }, { 774, 283 }, { 606, 150 }, { 551, 55 } },
		{ { 491, 219 }, { 353, 201 }, { 413, 128 }, { 524, 114 }, { 760, 136 }, { 500, 145 } },
		{ { 445, 235 }, { 425, 190 }, { 462, 188 }, { 496, 207 } },
		{ { 445, 346 }, { 417, 333 }, { 456, 258 }, { 353, 262 }, { 436, 147 } },
		{ { 434, 150 }, { 500, 77 }, { 433, 141 }, { 439, 190 } },
		{ { 441, 159 }, { 438, 268 }, { 477, 185 }, { 420, 287 } },
		{ { 446, 217 }, { 483, 235 }, { 465, 160 }, { 477, 191 }, { 511, 233 }, { 540, 202 }, { 487, 252 }, { 368, 277 }, { 475, 390 }, { 448, 341 }, { 425, 267 } },
		{ { 460, 268 }, { 411, 303 }, { 478, 376 }, { 431, 376 }, { 438, 261 } },
		{ { 688, 327 }, { 614, 259 }, { 608, 236 }, { 552, 21 }, { 584, 104 }, { 701, 174 } },
		{ { 614, 236 }, { 572, 110 }, { 575, 58 }, { 462, 149 }, { 426, 85 }, { 541, 324 }, { 481, 249 }, { 350, 31 }, { 569, 26 }, { 625, 124 } },
		{ { 508, 183 }, { 447, 296 }, { 503, 540 }, { 669, 488 }, { 459, 559 }, { 596, 558 }, { 585, 636 } },
		{ { 261, 38 }, { 365, 79 }, { 242, 116 }, { 409, 157 }, { 417, 256 }, { 179, 142 }, { 343, 243 }, { 278, 206 }, { 430, 441 }, { 129, 301 }, { 355, 212 }, { 344, 224 }, { 286, 79 } },
		{ { 218, 57 }, { 245, 98 }, { 317, 102 }, { 328, 65 } },
		{ { 246, 146 }, { 218, 125 }, { 164, 73 }, { 0, 102 }, { 264, 23 }, { 302, 75 }, { 304, 125 } },
		{ { 275, 143 }, { 230, 127 }, { 265, 99 }, { 305, 101 } },
		{ { 297, 263 }, { 289, 154 }, { 212, 199 }, { 225, 104 }, { 248, 102 }, { 322, 85 }, { 170, 227 }, { 402, 134 }, { 220, 17 }, { 303, 12 }, { 330, 158 } },
		{ { 675, 300 }, { 526, 293 }, { 627, 691 }, { 763, 443 } },
		{ { 610, 217 }, { 640, 283 }, { 536, 444 }, { 649, 553 }, { 689, 425 }, { 676, 579 }, { 679, 432 }, { 720, 561 }, { 630, 427 }, { 682, 307 } },
		{ { 101, 174 }, { 120, 286 }, { 106, 439 }, { 80, 448 } },
		{ { 697, 157 }, { 752, 277 }, { 800, 395 }, { 655, 707 }, { 673, 695 }, { 684, 592 }, { 660, 588 }, { 667, 635 }, { 623, 664 }, { 634, 671 }, { 678, 353 } },
		{ { 112, 131 }, { 117, 173 }, { 149, 232 }, { 25, 175 }, { 74, 206 }, { 217, 363 }, { 72, 610 }, { -6, 434 }, { 64, 429 }, { 117, 430 }, { 58, 301 }, { 45, 243 }, { 89, 158 } },
		{ { 72, 194 }, { 44, 292 }, { 62, 342 }, { 79, 476 }, { 82, 503 }, { 92, 532 }, { 102, 474 }, { 77, 442 }, { 94, 448 } },
		{ { 113, 153 }, { 119, -27 }, { 380, 86 }, { 183, 157 }, { 178, 179 }, { 362, 69 }, { 241, 7 }, { 130, 37 }, { 109, 127 } },
		{ { 306, 264 }, { 314, 296 }, { 333, 356 }, { 335, 415 }, { 299, 446 }, { 301, 390 } },
		{ { 306, 292 }, { 326, 339 }, { 326, 393 }, { 312, 406 }, { 319, 429 }, { 289, 406 }, { 304, 382 }, { 304, 337 } },
		{ { 214, 123 }, { 306, 196 }, { 333, 224 }, { 333, 364 }, { 242, 436 }, { 324, 480 }, { 230, 517 }, { 360, 496 }, { 259, 481 }, { 300, 638 } },
		{ { 87, 483 }, { 78, 512 }, { 114, 547 }, { 108, 625 }, { 141, 678 } },
		{ { 274, 572 }, { 256, 607 }, { 237, 616 }, { 228, 600 }, { 211, 602 }, { 195, 624 }, { 188, 647 }, { 168, 596 } },
		{ { 244, 604 }, { 250, 612 }, { 241, 618 }, { 243, 626 }, { 248, 628 }, { 258, 622 }, { 264, 624 }, { 268, 633 }, { 278, 638 }, { 296, 636 } },
		{ { 181, 618 }, { 181, 627 }, { 176, 642 } },
		{ { 237, 142 }, { 248, 165 }, { 221, 147 }, { 143, 82 } },
		{ { 500, 213 }, { 502, 191 }, { 544, 149 }, { 524, 205 } },
		{ { 104, 575 }, { 101, 593 }, { 90, 603 }, { 105, 627 }, { 1, 658 }, { 106, 625 }, { 146, 655 }, { 6, 671 }, { 126, 650 } },
		{ { 210, 611 }, { 221, 611 }, { 170, 665 }, { 220, 636 }, { 220, 622 }, { 199, 686 }, { 281, 541 }, { 210, 668 }, { 179, 681 }, { 253, 630 }, { 255, 624 }, { 242, 604 } },
		{ { 177, 643 }, { 177, 665 }, { 191, 647 }, { 205, 651 }, { 231, 671 }, { 221, 678 }, { 204, 675 }, { 213, 680 }, { 80, 697 }, { 150, 683 }, { 163, 660 } },
		{ { 238, 637 }, { 239, 647 }, { 248, 647 }, { 247, 659 }, { 253, 663 }, { 273, 659 }, { 267, 666 }, { 402, 688 }, { 309, 611 }, { 268, 651 }, { 245, 615 } },
		{ { 442, 335 }, { 435, 258 }, { 477, 268 }, { 441, 271 } },
		{ { 298, 257 }, { 291, 208 }, { 269, 177 }, { 270, 163 }, { 301, 206 } },
		{ { 486, 652 }, { 530, 594 }, { 541, 676 }, { 587, 654 }, { 592, 622 }, { 635, 622 }, { 668, 635 }, { 687, 630 }, { 707, 598 }, { 722, 592 }, { 739, 631 }, { 772, 595 }, { 799, 608 } },
		{ { 587, 679 }, { 608, 667 }, { 630, 646 }, { 643, 655 }, { 671, 658 }, { 685, 637 } },
		{ { 713, 680 }, { 744, 668 }, { 762, 654 }, { 770, 637 }, { 788, 631 } },
		{ { 512, 669 }, { 526, 655 }, { 556, 651 } },
		{ { 2, 657 }, { 33, 653 }, { 66, 646 } },
		{ { 30, 680 }, { 54, 671 }, { 70, 671 } },
		{ { 339, 688 }, { 366, 681 }, { 393, 681 } },
		{ { 403, 689 }, { 425, 679 }, { 449, 676 }, { 480, 676 } },
		{ { 227, 687 }, { 246, 679 }, { 259, 683 }, { 276, 686 }, { 296, 676 }, { 322, 677 }, { 341, 677 }, { 365, 670 }, { 395, 668 } },
		{ { 364, 342 }, { 392, 380 }, { 346, 372 }, { 348, 369 }, { 387, 421 }, { 397, 374 }, { 426, 378 }, { 377, 372 }, { 395, 337 } },
		{ { 416, 374 }, { 416, 306 }, { 343, 306 }, { 337, 288 }, { 389, 487 }, { 388, 248 }, { 412, 372 } },
		{ { 345, 384 }, { 339, 356 }, { 324, 313 }, { 426, 279 }, { 396, 310 }, { 392, 454 }, { 355, 268 }, { 345, 374 } },
		{ { 422, 646 }, { 418, 656 }, { 425, 671 }, { 449, 657 }, { 464, 645 }, { 526, 654 }, { 465, 642 } },
		{ { 415, 370 }, { 447, 413 }, { 370, 432 }, { 499, 404 }, { 421, 458 }, { 385, 454 }, { 584, 602 }, { 428, 667 }, { 494, 647 }, { 422, 655 } },
		{ { 314, 646 }, { 314, 659 }, { 319, 665 }, { 328, 666 }, { 334, 666 }, { 341, 647 }, { 374, 700 }, { 374, 635 } },
		{ { 343, 383 }, { 347, 397 }, { 342, 414 }, { 332, 440 }, { 319, 486 }, { 320, 551 }, { 249, 430 }, { 253, 667 }, { 325, 690 }, { 308, 658 }, { 346, 648 }, { 354, 641 } },
		{ { 347, 644 }, { 362, 651 }, { 374, 690 }, { 392, 652 }, { 399, 604 }, { 456, 688 }, { 481, 682 }, { 435, 691 }, { 449, 352 }, { 331, 535 } },
		{ { 343, 378 }, { 371, 412 }, { 540, 461 }, { 420, 595 }, { 341, 610 } },
		{ { 414, 370 }, { 382, 424 }, { 352, 385 }, { 336, 361 }, { 448, 557 } },
		{ { 419, 417 }, { 456, 432 }, { 465, 383 }, { 525, 393 }, { 558, 406 }, { 483, 466 }, { 510, 430 }, { 462, 391 }, { 452, 414 }, { 436, 519 } },
		{ { 445, 447 }, { 468, 412 }, { 511, 417 }, { 531, 443 }, { 471, 395 }, { 432, 440 } },
		{ { 340, 419 }, { 288, 371 }, { 150, 385 }, { 290, 443 }, { 208, 507 }, { 259, 334 }, { 238, 417 }, { 373, 429 }, { 254, 467 }, { 332, 424 }, { 331, 450 } },
		{ { 333, 443 }, { 299, 431 }, { 269, 377 }, { 240, 424 }, { 208, 461 }, { 262, 381 }, { 306, 436 } },
		{ { 432, 615 }, { 325, 548 }, { 320, 517 }, { 343, 389 }, { 360, 413 }, { 386, 492 }, { 421, 387 }, { 422, 362 } },
		{ { 363, 366 }, { 353, 365 }, { 354, 356 }, { 363, 348 } },
		{ { 399, 364 }, { 396, 354 }, { 407, 347 }, { 407, 365 } },
		{ { 345, 378 }, { 408, 435 }, { 415, 347 }, { 432, 318 }, { 419, 451 }, { 343, 382 } },
		{ { 384, 373 }, { 401, 377 }, { 384, 384 }, { 372, 381 } }
	};
	
	private static final PointParam[] POINT_PARAMS = {
		new PointParam(51, 51, 51, false, true),
		new PointParam(153, 153, 153, false, true),
		new PointParam(51, 51, 51, true, true),
		new PointParam(255, 255, 153, true, true),
		new PointParam(255, 153, 0, true, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, false),
		new PointParam(51, 51, 51, false, true),
		new PointParam(0, 0, 0, true, true),
		new PointParam(0, 0, 0, false, false),
		new PointParam(0, 0, 0, false, true),
		new PointParam(153, 153, 153, true, true),
		new PointParam(255, 255, 153, false, true),
		new PointParam(255, 153, 0, true, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(153, 153, 153, true, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(153, 153, 153, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(204, 204, 204, false, true),
		new PointParam(0, 0, 0, false, false),
		new PointParam(0, 0, 0, false, false),
		new PointParam(0, 0, 0, false, false),
		new PointParam(0, 0, 0, false, false),
		new PointParam(51, 51, 51, false, false),
		new PointParam(255, 204, 102, true, true),
		new PointParam(255, 204, 102, true, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(255, 153, 153, true, true),
		new PointParam(255, 153, 153, true, true),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 102, 153, false, false),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(51, 51, 51, true, true),
		new PointParam(91, 91, 102, false, true),
		new PointParam(51, 51, 51, true, true),
		new PointParam(91, 91, 102, false, true),
		new PointParam(91, 91, 102, true, true),
		new PointParam(91, 91, 102, false, true),
		new PointParam(91, 91, 102, false, true),
		new PointParam(91, 91, 102, true, true),
		new PointParam(51, 51, 51, true, true),
		new PointParam(91, 91, 102, false, true),
		new PointParam(51, 51, 51, false, true),
		new PointParam(93, 88, 98, true, true),
		new PointParam(0, 0, 0, true, true),
		new PointParam(0, 0, 0, true, true),
		new PointParam(0, 0, 0, false, true),
		new PointParam(153, 153, 153, true, true)
	};
	
	private Vector vPoint;				// 制御点
	private int point_num = -1;			// 制御点の番号
	private int captured_point = -1;	// つかんでいる制御点の番号
										//（-1のときつかんでいない）
	
	private int selectedTool = TOOL_SELECT;
	private BVector vCurBezier;
	private boolean isDragging, isSmoothMode = false;
	private Point dragLog;
	
	private IPPanel pane;
	private JDialog fCode;
	private TextArea tCode;
	private Button bColor;
	
	private Graphics gOff;
	private Image iOff, iWall;
	
	public void initialize() { // 制御点の設定
		vPoint = new Vector();
		
		for (int i = 0; i < POINT_DATA.length; i++) {
			BVector bv = new BVector(POINT_PARAMS[i].getValues(), POINT_PARAMS[i]);
			for (int j = 0; j < POINT_DATA[i].length; j++)
				bv.add(new Point(POINT_DATA[i][j][0], POINT_DATA[i][j][1]));
			bv.update();
			vPoint.add(bv);
		}
		
		vCurBezier = new BVector();
	}
		
	public void init() {
		setLayout(new BorderLayout());
		
		pane = new IPPanel();
		add(pane, BorderLayout.CENTER);
		
		JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
		add(toolbar, BorderLayout.NORTH);
		
		ToolButton[] toolBtn = new ToolButton[TOOL_NUM];
		for (int n = 0; n < toolBtn.length; n++) {
			toolBtn[n] = new ToolButton(n);
			toolbar.add(toolBtn[n]);
		}
		for (int n = 0; n < toolBtn.length; n++) toolBtn[n].setFriend(toolBtn);
		toolBtn[selectedTool].setBackground(Color.GRAY);
		toolbar.addSeparator();
		
		bColor = new Button("Color"); toolbar.add(bColor);
		bColor.setBackground(Color.BLACK); bColor.setForeground(Color.WHITE);
		Button bOC = new Button("O/C"); toolbar.add(bOC);
		Button bFill = new Button("Fill"); toolbar.add(bFill);
		
		toolbar.setBackground(bOC.getBackground());
		
		Button increment = new Button("+"), decrement = new Button("-");
		toolbar.add(increment);
		toolbar.add(decrement);
		toolbar.addSeparator();
		Button bOutput = new Button("Output"), bSmooth = new Button("Smooth"), bClear = new Button("Clear");
		toolbar.add(bOutput); toolbar.add(bSmooth); toolbar.add(bClear);
		
		Container parent = getParent(); while (!(parent instanceof Frame)) parent = parent.getParent();
		fCode = new JDialog((Frame) parent, "", true); tCode = new TextArea();
		fCode.add(tCode);
		
		increment.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				if (point_num == -1) return;
				int p = (int) Math.min(vPoint.size() - 1, point_num + 1);
				vPoint.add(p, vPoint.remove(point_num));
				point_num = p;
				pane.refine();
			}
		});
		
		decrement.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				if (point_num == -1) return;
				int p = (int) Math.max(0, point_num - 1);
				vPoint.add(p, vPoint.remove(point_num));
				point_num = p;
				pane.refine();
			}
		});
		
		bOC.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (isTool(TOOL_BEZIER)) vCurBezier.setClosed(!vCurBezier.isClosed());
				else if (point_num != -1) getCurVector(point_num).setClosed(!getCurVector(point_num).isClosed());
				pane.refine();
			}
		});
		
		bColor.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Color c = JColorChooser.showDialog(null, null, bColor.getBackground());
				if (c == null) return;
				if (isTool(TOOL_BEZIER)) vCurBezier.setColor(c);
				else if (point_num != -1) getCurVector(point_num).setColor(c);
				bColor.setBackground(c);
				pane.refine();
			}
		});
		
		bFill.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (isTool(TOOL_BEZIER)) vCurBezier.setFilled(!vCurBezier.isFilled());
				else if (point_num != -1) getCurVector(point_num).setFilled(!getCurVector(point_num).isFilled());
				pane.refine();
			}
		});
		
		bOutput.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { popCode(); } });
		
		bSmooth.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				isSmoothMode = !isSmoothMode;
				((Graphics2D) gOff).setRenderingHint(RenderingHints.KEY_ANTIALIASING, isSmoothMode ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
				pane.refine();
			}
		});
		
		bClear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (JOptionPane.showConfirmDialog(EmpPengo.this, "全てのBezier曲線をクリアします。よろしいですか？", "フレグランス・ド・フラワー", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) return;
				vPoint.clear(); point_num = -1; captured_point = -1; pane.refine();
			}
		});
		
		pane.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {  // 制御点をつかむ
				switch (getTool()) {
					case TOOL_EDIT: { captured_point = getCapturedPoint(e); } break;
					case TOOL_BEZIER: { vCurBezier.addPoint(e.getPoint()); } break;
					case TOOL_ADD: { getCurVector(point_num).addPoint(e.getPoint()); } break;
					case TOOL_DELETE: {
						int cp = getCapturedPoint(e);
						if (cp != -1) {
							BVector v = getCurVector(point_num);
							if (v.size() == 1) vPoint.remove(point_num--);
							else v.removePoint(cp);
						}
					} break;
					
					case TOOL_SELECT: {
						if (e.isMetaDown()) {
							Vector vN = new Vector();
							for (int n = vPoint.size() - 1; n >= 0; n--)
								if (getCurVector(n).getRect().contains(e.getX(), e.getY())) vN.add(Integer.toString(n));
							if (vN.isEmpty()) break;
							pane.popupMenu(vN, e);
							
						} else {
							boolean isSame = point_num != -1 && getCurVector(point_num).getRect().contains(e.getX(), e.getY());
							if (!isSame) point_num = getSelectedPoint(e);
							if (point_num != -1) {
								bColor.setBackground(getCurVector(point_num).getColor());
								isDragging = true; dragLog = new Point(e.getX(), e.getY());
							}
							if (isSame) return;
						}
					} break;
				}
				
				pane.refine();
			}
			
			public void mouseReleased(MouseEvent me) {
				switch (getTool()) {
					case TOOL_EDIT: {
						if (captured_point != -1) getCurVector(point_num).update();
						captured_point = -1;
					} break;
					
					case TOOL_SELECT: {
						if (isDragging) {
							isDragging = false;
							getCurVector(point_num).update();
							pane.refine();
						}
					} break;
				}
			}
		});
		
		pane.addMouseMotionListener(new MouseMotionAdapter() { // 制御点を動かす
			public void mouseDragged(MouseEvent e) {
				switch (getTool()) {
					case TOOL_EDIT: {
						if (captured_point == -1) break;
						getPoint(point_num, captured_point).setLocation(e.getX(), e.getY());
						getCurVector(point_num).update();
						pane.refine();
					} break;
					
					case TOOL_SELECT: {
						if (!isDragging) break;
						for (int n = 0; n < getPointSize(point_num); n++)
							getPoint(point_num, n).translate(e.getX() - dragLog.x, e.getY() - dragLog.y);
						dragLog.setLocation(e.getX(), e.getY());
						getCurVector(point_num).update();
						pane.refine();
					} break;
				}
			}
		});
		
		initialize();
		
		iOff = createImage(getSize().width, getSize().height);
		gOff = iOff.getGraphics();
		
		if (!SZ_WALLPAPER.equals("")) {
			try {
				iWall = ImageIO.read(new File(SZ_WALLPAPER));
			} catch (Exception e) { e.printStackTrace(); }
		} else iWall = null;
		
	// test!
		pane.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == 'P') popCode();
			}
		});
		
		pane.refine();
	}
	
	private void popCode() {
		StringBuffer szBuffer = new StringBuffer();
		
		szBuffer.append("private static final int[][][] POINT_DATA = {" + SZ_RETURN);
		for (int i = 0; i < vPoint.size(); i++) {
			szBuffer.append("	{ ");
			for (int j = 0; j < getPointSize(i); j++)
				szBuffer.append("{ " + getPoint(i, j).x + ", " + getPoint(i, j).y + (j == getPointSize(i) - 1 ? " }" : " }, "));
			szBuffer.append((i == vPoint.size() - 1 ? " }" : " },") + SZ_RETURN);
		}
		szBuffer.append("};" + SZ_RETURN + SZ_RETURN);
		
		szBuffer.append("private static final PointParam[] POINT_PARAMS = {" + SZ_RETURN);
		for (int i = 0; i < vPoint.size(); i++) {
			BVector v = getCurVector(i); Color c = v.getColor();
			szBuffer.append("	new PointParam(" + c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() + ", " + v.isClosed() + ", " + v.isFilled() + ")");
			szBuffer.append((i == vPoint.size() - 1 ? "" : ",") + SZ_RETURN);
		}
		szBuffer.append("};");
		
		tCode.setText(szBuffer.toString()); fCode.pack(); fCode.setVisible(true);
	}
	
	private boolean isTool(int t) { return selectedTool == t; }
	private int getTool() { return selectedTool; }
	private boolean isSelectableTool() { return !isTool(TOOL_BEZIER); }
	private void selectTool(int t) {
		selectedTool = t;
		if (!vCurBezier.isEmpty()) {
			vPoint.add(new BVector(vCurBezier));
			vCurBezier.clear();
			
			point_num = vPoint.size() - 1;
			if (point_num != -1) getCurVector(point_num).setColor(bColor.getBackground());
		}
		
		if (!isTool(TOOL_BEZIER))
			if (point_num != -1) bColor.setBackground(getCurVector(point_num).getColor());
		
		pane.refine();
	}
	
	private int getPointSize(int i) { return ((Vector) vPoint.get(i)).size(); }
	private Point getPoint(int i, int j) { return ((Point) ((Vector) vPoint.get(i)).get(j)); }
	private BVector getCurVector(int i) { return (BVector) vPoint.get(i); }
	private int getCapturedPoint(MouseEvent e) {
		if (point_num == -1) return -1;
		int i, x = e.getX(), y = e.getY();
		for (i = getPointSize(point_num) - 1; i >= 0; i--) {
			if (
				getPoint(point_num, i).x - CAPTURE_REGION <= x &&
				getPoint(point_num, i).x + CAPTURE_REGION >= x &&
				getPoint(point_num, i).y - CAPTURE_REGION <= y &&
				getPoint(point_num, i).y + CAPTURE_REGION >= y
			) break;
		}
		return i;
	}
	private int getSelectedPoint(MouseEvent e) {
		int n;
		for (n = vPoint.size() - 1; n >= 0; n--)
			if (getCurVector(n).getRect().contains(e.getX(), e.getY())) break;
		return n;
	}
	
	private class IPPanel extends Panel {
		private PopupMenu menu = new PopupMenu();
		
		public IPPanel() {
			menu.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					point_num = Integer.parseInt(e.getActionCommand());
					bColor.setBackground(getCurVector(point_num).getColor());
					refine();
				}
			});
			add(menu);
		}
		
		public void refine() {
			//requestFocus();
			gOff.clearRect(0, 0, getSize().width, getSize().height);
			if (iWall != null) gOff.drawImage(iWall, 0, 0, this);
			
			if (isTool(TOOL_SELECT) && point_num != -1 && !isDragging) {
				gOff.setColor(Color.LIGHT_GRAY);
				((Graphics2D) gOff).draw(getCurVector(point_num).getRect());
			}
			
			for (int i = 0; i < vPoint.size(); i++) {// 曲線描画
				if (isSelectableTool() && point_num == i)
					BezierCurve(gOff, null, getCurVector(i), isTool(TOOL_EDIT));
				else BezierCurve(gOff, null, getCurVector(i), false);
			}
			
			if (point_num != -1 && !vPoint.isEmpty() && isSelectableTool()) { // 制御点の描画
				Color c = isTool(TOOL_EDIT) || isTool(TOOL_DELETE) ? Color.RED : Color.BLUE;
				drawCapturingPoint(gOff, c, getCurVector(point_num));
			}
			
			if (!vCurBezier.isEmpty()) {
				BezierCurve(gOff, Color.RED, vCurBezier, false);
				drawCapturingPoint(gOff, Color.RED, vCurBezier);
			}
			
			repaint();
		}
		
		public void popupMenu(Vector vN, MouseEvent e) {
			menu.removeAll();
			for (int n = vN.size() - 1; n >= 0; n--) menu.add((String) vN.get(n));
			menu.show(this, e.getX(), e.getY());
		}
		
		private void BezierCurve(Graphics g, Color c, BVector v, boolean isEdit) {
			g.setColor(c == null ? v.getColor() : c);
			if (v.isFilled())
				g.fillPolygon(v.getPolygonX(), v.getPolygonY(), v.getPolygonSize());
			if (!v.isFilled() || isEdit) {
				if (isEdit) g.setColor(Color.RED);
				g.drawPolyline(v.getPolygonX(), v.getPolygonY(), v.getPolygonSize());
			}
		}
		
		public void paint(Graphics g) {
			g.drawImage(iOff, 0, 0, this);
		}
		
		public void update(Graphics g) {
			paint(g);
		}
		
		private void drawCapturingPoint(Graphics g, Color c, BVector v) {
			g.setColor(c);
			for (int i = 0; i < v.size(); i++) {
				g.fillRect(v.getPoint(i).x - 3, v.getPoint(i).y - 3, 7, 7);
				g.drawString(Integer.toString(i + 1), v.getPoint(i).x - 3, v.getPoint(i).y - 6);
			}
		}
	}
	
	protected class ToolButton extends Button implements ActionListener {
		int toolID;
		ToolButton[] friend;
		
		public ToolButton(int t) {
			super(SZ_TOOL[t]);
			toolID = t;
			addActionListener(this);
		}
		
		public void setFriend(ToolButton[] b) { friend = b; }
		public void actionPerformed(ActionEvent e) {
			selectTool(toolID);
			for (int n = 0; n < friend.length; n++) friend[n].setBackground(null);
			setBackground(Color.GRAY);
		}
	}
	
	protected class BVector extends Vector {
		private int[] px = null, py = null;
		private boolean isClosed = false, isFilled = false;
		private Color color = Color.BLACK;
		private Rectangle rect = null;
		
		public BVector() {}
		public BVector(boolean[] v, Color c) { isClosed = v[0]; isFilled = v[1]; color = c; }
		public BVector(BVector v) { super(v); isClosed = v.isClosed(); isFilled = v.isFilled(); update(); }
		
		public Point getPoint(int n) { return (Point) get(n); }
		public void addPoint(Point p) { add(p); update(); }
		public void removePoint(int n) { remove(n); update(); }
		
		public boolean isClosed() { return isClosed; }
		public void setClosed(boolean v) { isClosed = v; update(); }
		public Rectangle getRect() { return rect; }
		public void setRect(int x, int y, int w, int h) { rect = new Rectangle(x, y, w, h); }
		public Color getColor() { return color; }
		public void setColor(Color c) { color = c; }
		public boolean isFilled() { return isFilled; }
		public void setFilled(boolean v) { isFilled = v; }
		public int[] getPolygonX() { return px; }
		public int[] getPolygonY() { return py; }
		public int getPolygonSize() { return px.length; }
		
		public void update() { BezierCurve(); }
		
		private void BezierCurve() { // 曲線描画
			px = new int[T_SPLIT + 1];
			py = new int[T_SPLIT + 1];
			if (isClosed) add(getPoint(0));
			
			int i, j, n = 0;
			double x, y, xMin = getSize().width, yMin = getSize().height, xMax = 0, yMax = 0;
			double[] bernstein = new double[size()];
			x = getPoint(0).x;
			y = getPoint(0).y;
			
			for (double t = 0; t <= 1; n++, t += 1.0 / T_SPLIT) {
				for (i = 0; i < size(); i++) {  // bernstein関数を計算
					bernstein[i] = (double) kaijyo(size() - 1) / (kaijyo(i) * kaijyo(size() - 1 - i));
					
					for (j = 1; j <= i; j++) bernstein[i] *= t;
					for (j = 1; j <= size() - 1 - i; j++) bernstein[i] *= (1 - t);
				}
				
				x = 0; y = 0;
				for (i = 0; i < size(); i++) {
					x += getPoint(i).x * bernstein[i];
					y += getPoint(i).y * bernstein[i];
				}
				
				if (xMin > x) xMin = x;
				if (xMax < x) xMax = x;
				if (yMin > y) yMin = y;
				if (yMax < y) yMax = y;
				
				px[n] = (int) x; py[n] = (int) y;
			}
			
			x = getPoint(size() - 1).x; y = getPoint(size() - 1).y;
			px[n] = (int) x; py[n] = (int) y;
			
			if (xMin > x) xMin = x;
			if (xMax < x) xMax = x;
			if (yMin > y) yMin = y;
			if (yMax < y) yMax = y;
			
			setRect((int) xMin, (int) yMin, (int) (xMax - xMin), (int) (yMax - yMin));
			if (isClosed) remove(size() - 1);
		}
		
		private int kaijyo(int num) {   // 階乗を計算
			if (num == 0) return 1;
			
			int total = 1;
			for (; num > 1; num--)
				total *= num;
			return total;
		}
	}
	
	protected static class PointParam extends Color {
		private boolean[] values = new boolean[2];
		
		public PointParam(int r, int g, int b, boolean v0, boolean v1) {
			super(r, g, b); values[0] = v0; values[1] = v1;
		}
		
		public boolean[] getValues() { return values; }
	}
}
