/*
 * Turtle.java
 * 
 * Created on 2003/06/12
 */

import java.applet.Applet;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.MenuShortcut;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputMethodEvent;
import java.awt.event.InputMethodListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;

/**
 * Class Turtle.
 * 
 * version 1.0.2 2007/12/12 HolderTurtle追加のため変更 version 1.0.3 2007/12/12
 * InputTurtle追加のため変更
 * 
 * @author macchan
 * @version $Id: Turtle.java,v 1.1 2009/03/08 21:54:19 hashiyaman Exp $
 */
public class Turtle implements KeyListener, MouseListener, MouseMotionListener {

	private static final String version = "1.0.3 (2007/12/15)";

	static {
		System.out.println("Turtle Version: " + version);
	}

	/***************************************************************************
	 * static main
	 **************************************************************************/

	public static void main(String argv[]) {
		String classname;
		if (argv.length >= 1) {
			classname = argv[0];
		} else {
			classname = "Turtle";
		}
		try {
			Object o = Class.forName(classname).newInstance();
			if (o.getClass() == Turtle.class) {
				System.out.println("実行コマンドが間違っています。(Turtle の後に自分のクラス名が必要です)");
				System.exit(0);
			} else if (o instanceof Turtle) {
				startTurtle((Turtle) o);
			} else {
				System.out.println(classname
						+ " is not a subclass of Turtle class.");
			}
		} catch (Exception e) {
			System.out.println(classname + " クラスが見つかりません。コンパイルは通りましたか？");
		}
	}

	/***************************************************************************
	 * Start and Stop
	 **************************************************************************/

	public static void startTurtle(Turtle turtle) {
		allProtectedInstances.remove(turtle);
		defaultTurtle = turtle;
		window.addKeyListener(turtle);
		window.canvas().addKeyListener(turtle);
		window.canvas().addMouseListener(turtle);
		window.canvas().addMouseMotionListener(turtle);
		window.setVisible(true);
		initialized = true;
		window.canvas().mappedWait();
		window.restart();
	}

	public static void stopTurtle() {
		window.dispose();
		window = new TurtleFrame(100, 100, 300, 300);
		initialized = false;
	}

	/***************************************************************************
	 * For Applet
	 **************************************************************************/

	public static Applet applet = null;

	/***************************************************************************
	 * static instances
	 **************************************************************************/

	private static List allProtectedInstances = new ArrayList();

	private static List allInstances = new ArrayList();

	public static List getAllInstances() {
		return new ArrayList(allInstances);
	}

	public static List getAllProtectedInstances() {
		return new ArrayList(allProtectedInstances);
	}

	public static void resetAllInstances() {
		allInstances.clear();
		for (int i = 0; i < allProtectedInstances.size(); i++) {
			Turtle t = (Turtle) allProtectedInstances.get(i);
			t.initialize();
		}
	}

	/***************************************************************************
	 * static variables
	 **************************************************************************/

	private static Object instanciationLock = new Object();
	private static boolean isinstanciation = false;

	public static TurtleFrame window = new TurtleFrame(100, 100, 300, 300);

	public static Turtle defaultTurtle = null;

	private static final Turtle nullTurtle = new DefaultTurtle();

	public static boolean initialized = false;

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	// 親タートル関連
	protected HolderTurtle parent;
	protected List<InputTurtle> inputTurtles = new ArrayList<InputTurtle>();

	// 座標関連
	private Point2D location = new Point2D.Double(100d, 100d); // 中心の位置

	private Point2D balance = new Point2D.Double(100d, 100d); // 重心の位置

	private Dimension2D size = new DoubleDimension(100d, 100d); // 大きさ

	private double angle = 0.0; // 回転角度

	private double direction = 0.0; // 方向

	// 形関連
	private Turtle looks = null; // 見た目

	private boolean show = true;

	private LineList originalShape = null; // 見た目

	private LineList shape = new LineList(); // 形

	// 軌跡関連
	private boolean penDown = false; // ペンが下りているか

	private Color penColor = Color.black; // ペンの色

	private LineList locus = new LineList(); // 軌跡

	// 委譲関連
	private Turtle delegator = null; // 委譲者

	// 形状変換関連
	private boolean dirty = true;

	private AffineTransform currentTransform = null;

	// 入力関連
	private KeyEvent keyEvent = null;

	private MouseEvent mouseEvent = null;

	private MouseEvent mouseMotionEvent = null;

	private boolean inputCaptured = true;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	/**
	 * Constructor.
	 */
	public Turtle() {
		initialize();
	}

	/***************************************************************************
	 * initialize
	 **************************************************************************/

	private void initialize() {
		synchronized (instanciationLock) {
			initializeParameter();
			if (this instanceof DefaultTurtle) {
				// do nothing
			} else if (isinstanciation) {
				delegator(nullTurtle);
			} else if (!initialized) {
				allProtectedInstances.add(this);
			} else if (this.getClass() == Turtle.class) {
				delegator(new TurtleTurtle());
				allInstances.add(this);
			} else if (this instanceof TurtleTurtle) {
				allInstances.add(this);
			} else {
				initializeLooks();
				allInstances.add(this);
			}
		}
	}

	/***************************************************************************
	 * die
	 **************************************************************************/

	public void die() {
		if (allInstances.contains(this)) {
			allInstances.remove(this);
		}
	}

	/***************************************************************************
	 * Reset
	 **************************************************************************/

	private void initializeParameter() {
		// 座標関連
		location = new Point2D.Double(100d, 100d); // 中心の位置
		balance = new Point2D.Double(100d, 100d); // 重心の位置
		size = new DoubleDimension(100d, 100d); // 大きさ
		angle = 0.0; // 回転角度
		direction = 0.0; // 方向

		// 形関連
		looks = null; // 見た目
		show = true;
		originalShape = null; // 見た目
		shape = new LineList(); // 形

		// 軌跡関連
		penDown = false; // ペンが下りているか
		penColor = Color.black; // ペンの色
		locus = new LineList(); // 軌跡

		// 委譲関連
		delegator = null; // 委譲者

		// 形状変換関連
		dirty = true;
		currentTransform = null;

		// 入力関連
		keyEvent = null;
		mouseEvent = null;
		inputCaptured = true;
	}

	public void initializeLooks() {
		DefaultTurtle turtle = new DefaultTurtle();

		synchronized (instanciationLock) {
			isinstanciation = true;
			delegator(turtle);
			try {
				start();
			} catch (RuntimeException ex) {
				System.out.println(ex.getMessage());
			} finally {
				delegator(null);
				isinstanciation = false;
			}
		}

		turtle.die();
		this.originalShape = new LineList(turtle.locus());
		looks(this);
		resetScale();
	}

	/***************************************************************************
	 * Looks関連
	 **************************************************************************/

	public synchronized void looks(Turtle looks) {
		this.looks = looks;
		this.shape = new LineList(looks.originalShape());
		this.currentTransform = null;
		this.dirty = true;
	}

	public Turtle looks() {
		return looks;
	}

	public void resetLooks() {
		looks(this);
	}

	public void looksSize() {
		size(looks.size());
	}

	/***************************************************************************
	 * 委譲関連
	 **************************************************************************/

	public void delegator(Turtle delegator) {
		this.delegator = delegator;
	}

	/***************************************************************************
	 * shape(形状) locus(軌跡) 関連
	 **************************************************************************/

	protected LineList shape() {
		return this.shape;
	}

	protected LineList locus() {
		return this.locus;
	}

	protected LineList originalShape() {
		if (this.originalShape != null) {
			return this.originalShape;
		} else {
			return this.locus;
		}
	}

	/***************************************************************************
	 * show関連
	 **************************************************************************/

	public void show() {
		show(true);
	}

	public void hide() {
		show(false);
	}

	public void show(boolean show) {
		if (delegator != null) {
			delegator.show(show);
		} else {
			this.show = show;
			if (show) {
				reshape();
			}
		}
	}

	public boolean isShow() {
		return show;
	}

	/***************************************************************************
	 * Location関連
	 **************************************************************************/

	public Point2D location() {
		return new Point2D.Double(x(), y());
	}

	public void location(Point2D location) {
		location(location.getX(), location.getY());
	}

	public void location(double x, double y) {

		if (penDown) {
			Point2D newLocation = new Point2D.Double(x, y);
			locus.add(new Line(location, newLocation, penColor));
		}

		double moveX = x - x();
		double moveY = y - y();

		location.setLocation(x, y);
		balance.setLocation(balance.getX() + moveX, balance.getY() + moveY);

		dirty = true;

	}

	public double x() {
		return location.getX();
	}

	public double y() {
		return location.getY();
	}

	public int getX() {
		return (int) x();
	}

	public int getY() {
		return (int) y();
	}

	public void x(double x) {
		location(x, y());
	}

	public void y(double y) {
		location(x(), y);
	}

	public double minX() {
		return x() - width() / 2;
	}

	public double minY() {
		return y() - height() / 2;
	}

	public double maxX() {
		return x() + width() / 2;
	}

	public double maxY() {
		return y() + height() / 2;
	}

	/***************************************************************************
	 * Rotated(回転後)Position関連
	 **************************************************************************/

	public Point2D rotatedLocation() {
		return new Point2D.Double(rotatedX(), rotatedY());
	}

	public double rotatedX() {
		return shape.getBounds().getCenterX();
	}

	public double rotatedY() {
		return shape.getBounds().getCenterY();
	}

	public double rotatedMinX() {
		return rotatedX() - rotatedWidth() / 2;
	}

	public double rotatedMinY() {
		return rotatedY() - rotatedHeight() / 2;
	}

	public double rotatedMaxX() {
		return rotatedX() + rotatedWidth() / 2;
	}

	public double rotatedMaxY() {
		return rotatedY() + rotatedHeight() / 2;
	}

	/***************************************************************************
	 * Size関連
	 **************************************************************************/

	public Dimension2D size() {
		return new DoubleDimension(size);
	}

	public void size(Dimension2D dimension) {
		size(dimension.getWidth(), dimension.getHeight());
	}

	public void size(double width, double height) {

		if (width <= 0.1) {
			width = 0.1;
		}
		if (height <= 0.1) {
			height = 0.1;
		}

		size.setSize(width, height);
		dirty = true;

	}

	public double width() {
		return size.getWidth();
	}

	public double height() {
		return size.getHeight();
	}

	public int getWidth() {
		return (int) width();
	}

	public int getHeight() {
		return (int) height();
	}

	public void width(double width) {
		size(width, height());
	}

	public void height(double height) {
		size(width(), height);
	}

	public void large(double width, double height) {
		size(width() + width, height() + height);
	}

	public void large(double length) {
		large(length, length);
	}

	public void wide(double length) {
		large(length, 0);
	}

	public void tall(double length) {
		large(0, length);
	}

	public void small(double width, double height) {
		large(-width, -height);
	}

	public void small(double length) {
		large(-length);
	}

	public void narrow(double length) {
		wide(-length);
	}

	public void little(double length) {
		tall(-length);
	}

	/***************************************************************************
	 * Rotated(回転後)Size関連
	 **************************************************************************/

	public double rotatedWidth() {
		return shape.getBounds().getWidth();
	}

	public double rotatedHeight() {
		return shape.getBounds().getHeight();
	}

	/***************************************************************************
	 * Bounds関連
	 **************************************************************************/

	public Rectangle2D bounds() {
		return new Rectangle2D.Double(minX(), minY(), width(), height());
	}

	public void bounds(Rectangle2D r) {
		location(r.getCenterX(), r.getCenterY());
		size(r.getWidth(), r.getHeight());
	}

	public void bounds(double x, double y, double width, double height) {
		bounds(new Rectangle2D.Double(x, y, width, height));
	}

	public Rectangle2D rotatedBounds() {
		return shape.getBounds();
	}

	/***************************************************************************
	 * RotatePoint関連
	 **************************************************************************/

	public Point2D balance() {
		return balance;
	}

	public double balanceX() {
		return balance().getX();
	}

	public double balanceY() {
		return balance().getY();
	}

	public int getBalanceX() {
		return (int) balanceX();
	}

	public int getBalanceY() {
		return (int) balanceY();
	}

	public void balance(double percentX, double percentY) {
		double xMag = percentX / 100d;
		double yMag = percentY / 100d;

		RelativePoint rp = new RelativePoint(xMag, yMag);
		balance = rp.getPoint(bounds());
	}

	/***************************************************************************
	 * move関連
	 **************************************************************************/

	public void fd(double length) {
		if (delegator != null) {
			delegator.fd(length);
		} else {
			move(length, 0);
		}
	}

	public void bk(double length) {
		if (delegator != null) {
			delegator.bk(length);
		} else {
			move(length, 180);
		}
	}

	public void right(double length) {
		if (delegator != null) {
			delegator.right(length);
		} else {
			move(length, 90);
		}
	}

	public void left(double length) {
		if (delegator != null) {
			delegator.right(length);
		} else {
			move(length, -90);
		}
	}

	public void move(double length, double direction) {

		double theta = theta(angle() + direction() + direction);
		double xLength = Math.sin(theta) * length;
		double yLength = -Math.cos(theta) * length;

		double newX = x() + xLength;
		double newY = y() + yLength;

		warp(newX, newY);
	}

	public void warp(double x, double y) {
		location(x, y);
	}

	/***************************************************************************
	 * scale関連
	 **************************************************************************/

	public void scale(double scale) {
		scale(scale, scale);
	}

	public void scale(double scaleX, double scaleY) {
		size(width() * scaleX, height() * scaleY);
	}

	public void resetScale(double scale) {
		Rectangle2D org = originalBounds();
		size(org.getWidth() * scale, org.getHeight() * scale);
	}

	public void resetScale() {
		resetScale(1d);
	}

	public Scale scale() {
		Rectangle2D org = originalBounds();
		return new Scale(org.getWidth(), org.getHeight(), width(), height());
	}

	public double scaleX() {
		return scale().x();
	}

	public double scaleY() {
		return scale().y();
	}

	/***************************************************************************
	 * angle関連
	 **************************************************************************/

	public void angle(double angle) {
		this.angle = angle;
		dirty = true;
	}

	public double angle() {
		return angle;
	}

	public void rt(double angle) {
		if (delegator != null) {
			delegator.rt(angle);
		} else {
			rotate(angle);
		}
	}

	public void lt(double angle) {
		if (delegator != null) {
			delegator.lt(angle);
		} else {
			rotate(-angle);
		}
	}

	public void rotate(double angle) {
		angle(this.angle + angle);
	}

	/***************************************************************************
	 * direction関連
	 **************************************************************************/

	public void direction(double direction) {
		this.direction = direction;
	}

	public double direction() {
		return direction;
	}

	public void directionRt(double direction) {
		rotateDirection(direction);
	}

	public void directionLt(double direction) {
		rotateDirection(-direction);
	}

	public void rotateDirection(double direction) {
		direction(this.direction + direction);
	}

	/***************************************************************************
	 * thetaを求めるUtility
	 **************************************************************************/

	protected double theta() {
		return theta(angle());
	}

	protected double theta(double angle) {
		return angle / 360d * 2d * Math.PI;
	}

	/***************************************************************************
	 * pen関連
	 **************************************************************************/

	public void up() {
		if (delegator != null) {
			delegator.up();
		} else {
			penDown = false;
		}
	}

	public void down() {
		if (delegator != null) {
			delegator.down();
		} else {
			penDown = true;
		}
	}

	/***************************************************************************
	 * color関連
	 **************************************************************************/

	public void color(Color penColor) {
		if (delegator != null) {
			delegator.color(penColor);
		} else {
			this.penColor = penColor;
		}
	}

	public Color color() {
		return penColor;
	}

	/***************************************************************************
	 * あたり判定関連
	 **************************************************************************/

	public boolean intersects(Turtle target) {
		if (!isShow() || !target.isShow()) {
			return false;
		}

		this.reshape();
		target.reshape();

		Rectangle2D r1 = this.rotatedBounds();
		Rectangle2D r2 = target.rotatedBounds();
		// return r1.intersects(r2);

		Rectangle2D intersection = r1.createIntersection(r2);
		if (intersection.isEmpty()) {
			return false;
		}
		return this.intersectsBounds(intersection)
				&& target.intersectsBounds(intersection);
	}

	public boolean contains(double x, double y) {
		if (!isShow()) {
			return false;
		}

		reshape();
		Rectangle2D rect = shape().getBounds();
		return rect.contains(x, y);
	}

	protected boolean intersectsBounds(Rectangle2D bounds) {
		if (looks() instanceof ImageTurtle) {
			return true;
		}
		return shape().intersects(bounds);
	}

	protected Shape rotatedShape() {
		AffineTransform transform = new AffineTransform();
		transform.rotate(theta(), balanceX(), balanceY());
		return transform.createTransformedShape(bounds());
	}

	/***************************************************************************
	 * キー入力関連
	 **************************************************************************/

	public void keyPressed(KeyEvent e) {
		keyEvent = e;
		inputCaptured = false;
		keyPressed(e.getKeyCode());
		for (InputTurtle input : inputTurtles) {
			input.captureText(e);
		}
	}

	void keyPressed(int keyCode) {
	}

	public void keyReleased(KeyEvent e) {
		keyReleased(e.getKeyCode());
	}

	void keyReleased(int keyCode) {
	}

	public void keyTyped(KeyEvent e) {
	}

	public int key() {
		inputCaptured = true;
		if (keyEvent != null) {
			return keyEvent.getKeyCode();
		} else {
			return -1;
		}
	}

	public boolean keyDown() {
		inputCaptured = true;
		return keyEvent != null;
	}

	private void resetKey() {
		if (inputCaptured) {
			keyEvent = null;
		}
	}

	/***************************************************************************
	 * マウス入力関連
	 **************************************************************************/

	// --- implements MouseListener ---
	public void mouseClicked(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		mouseEvent = e;
		inputCaptured = false;
		mousePressed(e.getX(), e.getY());
	}

	public void mouseReleased(MouseEvent e) {
		mouseReleased(e.getX(), e.getY());
	}

	// --- implements MouseMotionListener ---

	public void mouseDragged(MouseEvent e) {
		mouseMotionEvent = e;
	}

	public void mouseMoved(MouseEvent e) {
		mouseMotionEvent = e;
	}

	// --- original method ---

	void mousePressed(int x, int y) {
	}

	void mouseReleased(int x, int y) {
	}

	public int mouseX() {
		inputCaptured = true;
		if (mouseEvent != null) {
			return mouseEvent.getX();
		} else if (mouseMotionEvent != null) {
			return mouseMotionEvent.getX();
		} else {
			return 0;
		}
	}

	public int mouseY() {
		inputCaptured = true;
		if (mouseEvent != null) {
			return mouseEvent.getY();
		} else if (mouseMotionEvent != null) {
			return mouseMotionEvent.getY();
		} else {
			return 0;
		}
	}

	public boolean mouseDown() {
		inputCaptured = true;
		return mouseEvent != null;
	}

	public boolean leftMouseDown() {
		inputCaptured = true;
		return mouseEvent == null
				? false
				: mouseEvent.getButton() == MouseEvent.BUTTON1;
	}

	public boolean rightMouseDown() {
		inputCaptured = true;
		return mouseEvent == null
				? false
				: mouseEvent.getButton() == MouseEvent.BUTTON3;
	}

	public boolean doubleClick() {
		inputCaptured = true;
		return mouseEvent == null ? false : mouseEvent.getClickCount() == 2;
	}

	private void resetMouse() {
		if (inputCaptured) {
			mouseEvent = null;
		}
	}

	/***************************************************************************
	 * 標準入出力関連
	 **************************************************************************/

	public String inputString() {
		InputStream input;
		if (window.console != null) {
			input = window.console.in;
		} else {
			input = System.in;
		}
		String returnString = null;
		try {
			InputStreamReader isr = new InputStreamReader(input);
			BufferedReader br = new BufferedReader(isr);
			returnString = br.readLine();
			return returnString;
		} catch (IOException ex) {
			ex.printStackTrace();
			return null;
		}
	}

	// added by macchan 10/8
	public double inputDouble() {
		double returnDouble = 0;
		returnDouble = Double.parseDouble(inputString());
		return returnDouble;
	}

	public int input() {
		int returnInt = 0;
		returnInt = Integer.parseInt(inputString());
		return returnInt;
	}

	public void print(Object o) {
		printInternal(o);
	}

	public void print(int x) {
		printInternal(new Integer(x));
	}

	public void print(double x) {
		printInternal(new Double(x));
	}

	public void print(boolean x) {
		printInternal(new Boolean(x));
	}

	private void printInternal(Object o) {
		if (window.console != null) {
			window.console.out.println(o);
		} else {
			System.out.println(o);
		}
	}

	/***************************************************************************
	 * ランダム関連
	 **************************************************************************/

	public int random(int max) {
		return (int) (Math.random() * (double) max);
	}

	/***************************************************************************
	 * Sleep関連
	 **************************************************************************/

	public void sleep(double second) {
		synchronized (instanciationLock) {
			if (isinstanciation) {
				throw new RuntimeException("タートル生成中にアニメーションが呼ばれました");
			}

			try {
				Thread.sleep((long) (second * 1000d));
			} catch (InterruptedException ex) {
				throw new RuntimeException("Interrupted By User");
			}
		}
	}

	/***************************************************************************
	 * paint関連
	 **************************************************************************/

	public void update() {
		window.canvas().repaint();
		resetKey();
		resetMouse();
	}

	public void paint(Graphics2D g) {
		if (show) {
			reshape();
			draw(g);
		}
	}

	public void draw(Graphics2D g) {
		locus.paint(g);

		if (looks instanceof ImageTurtle) {
			ImageTurtle it = (ImageTurtle) looks;
			g.drawImage(it.image(), it.createTransformOp(theta(), width(),
					height(), rotatedWidth(), rotatedHeight()),
					(int) rotatedMinX(), (int) rotatedMinY());
			return;
		}

		shape.paint(g);
	}

	/***************************************************************************
	 * Start関連
	 **************************************************************************/

	void start() {
	}

	/***************************************************************************
	 * Transform 形状変換関連
	 **************************************************************************/

	protected boolean dirty() {
		return dirty;
	}

	protected void dirty(boolean dirty) {
		this.dirty = dirty;
	}

	private Rectangle2D originalBounds() {
		return originalShape.getBounds();
	}

	protected void reshape() {
		if (shape() != null && dirty) {
			doTransform();
			dirty = false;
		}
	}

	protected synchronized void doTransform() {

		// 既に現在変換済みなら一旦元に戻す
		if (currentTransform != null) {
			try {
				AffineTransform reverse = currentTransform.createInverse();
				shape.transform(reverse);
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}

		// 処理
		AffineTransform transform = createTransform();
		shape.transform(transform);
		currentTransform = transform;
	}

	protected AffineTransform createTransform() {
		AffineTransform transform = new AffineTransform();

		Rectangle2D r = shape.getBounds();
		Scale scale = new Scale(r.getWidth(), r.getHeight(), width(), height());

		// 逆順になっています
		transform.rotate(theta(), balanceX(), balanceY());
		transform.translate(balanceX(), balanceY());
		transform.scale(scale.x(), scale.y());
		transform.translate(-balanceX(), -balanceY());
		transform.translate(x(), y());
		transform.translate(-r.getCenterX(), -r.getCenterY());

		return transform;
	}

}

/*******************************************************************************
 * 
 * Class TurtleFrame.
 * 
 ******************************************************************************/

class TurtleFrame extends JFrame implements ActionListener, ItemListener {
	// class TurtleFrame extends Frame implements ActionListener, ItemListener {

	/***************************************************************************
	 * Constants
	 **************************************************************************/
	private static final boolean CONSOLE_APPLET = true;

	private static final String[] speedMenuString = {"no kame", "very fast",
			"fast", "normal", "slow"};

	private static final int[] speedStep = {100000, 1000, 20, 5, 2};

	/***************************************************************************
	 * Variables
	 **************************************************************************/

	private TurtleCanvas canvas = null;

	private Thread thread = null;

	private Object restartLock = "Restart Lock";

	CheckboxMenuItem[] speedMenu = null;

	public ConsoleTextArea console;

	public JSplitPane split;

	/***************************************************************************
	 * Constructors.
	 **************************************************************************/

	// for AWT Frame
	// public TurtleFrame(int x, int y, int width, int height) {
	//
	// MenuBar menubar = new MenuBar();
	// setMenuBar(menubar);
	//
	// //file menu
	// Menu file = new Menu("File", true);
	// menubar.add(file);
	// MenuItem restart =
	// new MenuItem("Restart", new MenuShortcut(KeyEvent.VK_S));
	// restart.addActionListener(this);
	// restart.setActionCommand("restart");
	// file.add(restart);
	// MenuItem quit = new MenuItem("Quit", new MenuShortcut(KeyEvent.VK_Q));
	// quit.addActionListener(this);
	// quit.setActionCommand("quit");
	// file.add(quit);
	//
	// //speed menu
	// Menu speed = new Menu("Speed", true);
	// speedMenu = new CheckboxMenuItem[speedMenuString.length];
	// for (int i = 0; i < speedMenuString.length; i++) {
	// speedMenu[i] = new CheckboxMenuItem(speedMenuString[i], false);
	// speedMenu[i].addItemListener(this);
	// speed.add(speedMenu[i]);
	// }
	// menubar.add(speed);
	// speedMenu[2].setState(true); //最初はNormalなので
	//
	// // canvas
	// canvas = new TurtleCanvas();
	// add(BorderLayout.CENTER, canvas());
	//
	// // size
	// setSize(width, height + 30);
	// show();
	// canvas.mappedWait();
	//
	// }
	// for swing JFrame
	public TurtleFrame(int x, int y, int width, int height) {

		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		MenuBar menubar = new MenuBar();
		setMenuBar(menubar);

		// file menu
		Menu file = new Menu("File", true);
		menubar.add(file);
		MenuItem restart = new MenuItem("Restart", new MenuShortcut(
				KeyEvent.VK_S));
		restart.addActionListener(this);
		restart.setActionCommand("restart");
		file.add(restart);
		MenuItem quit = new MenuItem("Quit", new MenuShortcut(KeyEvent.VK_Q));
		quit.addActionListener(this);
		quit.setActionCommand("quit");
		file.add(quit);

		// speed menu
		Menu speed = new Menu("Speed", true);
		speedMenu = new CheckboxMenuItem[speedMenuString.length];
		for (int i = 0; i < speedMenuString.length; i++) {
			speedMenu[i] = new CheckboxMenuItem(speedMenuString[i], false);
			speedMenu[i].addItemListener(this);
			speed.add(speedMenu[i]);
		}
		menubar.add(speed);
		speedMenu[3].setState(true); // 最初はNormalなので
		changeSpeed(speedMenuString[3]);

		// canvas
		if (Turtle.applet != null && CONSOLE_APPLET) {
			// For Applet 2007/06/11

			split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

			// Main
			canvas = new TurtleCanvas();
			canvas.setPreferredSize(new Dimension(300, 300));
			canvas.setMinimumSize(new Dimension(1, 1));
			canvas.setMaximumSize(new Dimension(2000, 2000));

			// South
			console = new ConsoleTextArea();
			console.setBackground(Color.BLACK);
			console.setForeground(Color.WHITE);
			JScrollPane scroll = new JScrollPane();
			scroll.setViewportView(console);
			scroll.setPreferredSize(new Dimension(1, 1));
			scroll.setMinimumSize(new Dimension(1, 1));
			scroll.setMaximumSize(new Dimension(2000, 2000));

			split.setLeftComponent(canvas);
			split.setRightComponent(scroll);
			getContentPane().add(BorderLayout.CENTER, split);
			split.setDividerLocation(height - 50);
		} else {
			canvas = new TurtleCanvas();
			getContentPane().add(BorderLayout.CENTER, canvas());
			// add(BorderLayout.CENTER, canvas());

		}

		// size
		setSize(width, height + 30);

	}

	/***************************************************************************
	 * Listener
	 **************************************************************************/

	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();
		if (cmd.equals("quit")) {
			try {
				System.exit(0);
			} catch (Exception ex) {
				dispose();
			}
		} else if (cmd.equals("restart")) {
			restart();
		}
	}

	public void itemStateChanged(ItemEvent e) {
		String s = (String) e.getItem();
		changeSpeed(s);
	}

	private void changeSpeed(String s) {
		for (int i = 0; i < speedMenuString.length; i++) {
			if (s.equals(speedMenuString[i])) {
				if (TurtleTurtle.speed(speedStep[i])) {
					for (int j = 0; j < speedMenuString.length; j++) {
						speedMenu[j].setState(i == j);
					}
				}
				break;
			}
		}
	}

	/***************************************************************************
	 * restart and thread management
	 **************************************************************************/

	void restart() {

		stopThread();
		thread = new Thread() {
			public void run() {
				try {
					Turtle.resetAllInstances();
					Turtle.defaultTurtle.delegator(new TurtleTurtle());
					Turtle.defaultTurtle.start();
					synchronized (restartLock) {
						thread = null;
						restartLock.notify();
					}
				} catch (Exception ex) {
					if (!ex.getMessage().equals("Interrupted By User")) {
						ex.printStackTrace();
					}
					synchronized (restartLock) {
						thread = null;
						restartLock.notify();
					}
				}
			}
		};
		thread.start();

	}

	/***************************************************************************
	 * Canvas
	 **************************************************************************/

	public TurtleCanvas canvas() {
		return this.canvas;
	}

	/***************************************************************************
	 * 座標関連
	 **************************************************************************/

	public int x() {
		return getLocation().x;
	}

	public void x(int x) {
		location(x, y());
	}

	public int y() {
		return getLocation().y;
	}

	public void y(int y) {
		location(x(), y);
	}

	public void location(int x, int y) {
		setLocation(x, y);
	}

	public void warp(int x, int y) {
		location(x, y);
	}

	public int width() {
		return getSize().width;
	}

	public void width(int width) {
		size(width, height());
		validate();
	}

	public int height() {
		return getSize().height;
	}

	public void height(int height) {
		size(width(), height);
	}

	public void size(int width, int height) {
		setSize(width, height);
		validate();
	}

	/***************************************************************************
	 * dispose関連
	 **************************************************************************/

	public void dispose() {
		stopThread();
		super.dispose();
	}

	private void stopThread() {
		synchronized (restartLock) {
			if (thread != null) {
				thread.interrupt();
				try {
					restartLock.wait();
				} catch (Exception ex) {
				}

			}
		}
	}

}

/*******************************************************************************
 * 
 * Class TurtleCanvas.
 * 
 ******************************************************************************/

// class TurtleCanvas extends JPanel {
class TurtleCanvas extends Canvas {

	private Object mappedLock = "mappedLock";

	private boolean mapped = false;

	private Image offScreen = null;

	public TurtleCanvas() {
		setBackground(Color.white);
		setForeground(Color.black);
	}

	public void setBounds(int x, int y, int w, int h) {
		super.setBounds(x, y, w, h);
		offScreen = createImage(w, h);
	}

	// for canvas

	public void update(Graphics g) {
		paint(g);
	}

	public void paint(Graphics g) {

		// public void paintComponent(Graphics g) {
		mappedNotify();

		if (offScreen != null) {

			Graphics2D offG2d = (Graphics2D) offScreen.getGraphics();
			Color defaultColor = offG2d.getColor();
			offG2d.setColor(Color.white);
			offG2d.fillRect(0, 0, getWidth(), getHeight());
			offG2d.setColor(defaultColor);

			Iterator i = Turtle.getAllInstances().iterator();
			while (i.hasNext()) {
				Turtle sprite = (Turtle) i.next();
				sprite.paint(offG2d);
			}
			offG2d.dispose();

			g.drawImage(offScreen, 0, 0, null);

		}
	}

	// for swing panel

	// public void paintComponent(Graphics g) {
	// super.paintComponent(g);
	// mappedNotify();
	//
	// Iterator i = Turtle.getAllInstances().iterator();
	// while (i.hasNext()) {
	// Turtle sprite = (Turtle) i.next();
	// sprite.paint((Graphics2D) g);
	// }
	// }

	public void mappedWait() {
		synchronized (mappedLock) {
			if (!mapped) {
				try {
					mappedLock.wait();
				} catch (InterruptedException e) {
				}
			}
		}
	}

	public void mappedNotify() {
		synchronized (mappedLock) {
			if (!mapped) {
				mapped = true;
				mappedLock.notify();
			}
		}
	}

}

/*******************************************************************************
 * 
 * Class DefaultTurtle.
 * 
 ******************************************************************************/

class DefaultTurtle extends Turtle {
	public DefaultTurtle() {
		down();
	}
}

/*******************************************************************************
 * 
 * Class ImageTurtle.
 * 
 ******************************************************************************/

class ImageTurtle extends Turtle {

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	private BufferedImage image = null;

	private AffineTransformOp transformOp = null;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public ImageTurtle() {
		setImage(createTextImage("名称未設定"));
	}

	public ImageTurtle(String filename) {
		image(filename);
	}

	/***************************************************************************
	 * getter
	 **************************************************************************/

	public BufferedImage image() {
		return image;
	}

	public AffineTransformOp transformOp() {
		return transformOp;
	}

	/***************************************************************************
	 * Image関連
	 **************************************************************************/

	public void image(String filename) {
		setImage(loadBufferedImage(filename));
	}

	protected void setImage(BufferedImage image) {
		this.image = image;
		super.initializeLooks();
	}

	private BufferedImage loadBufferedImage(String filename) {
		Image image = loadImage(filename);
		if (image != null) {
			return createBufferedImage(image);
		} else {
			return createTextImage(filename);
		}
	}

	private Image loadImage(String filename) {
		try {
			if (applet == null && !new File(filename).exists()) {
				print("エラー: 画像が見つかりません " + filename);
				return null;
			}

			Toolkit toolkit = Toolkit.getDefaultToolkit();
			MediaTracker mt = new MediaTracker(window);
			Image image = null;
			if (applet == null) {
				image = toolkit.getImage(filename);
			} else {
				image = applet.getImage(applet.getDocumentBase(), filename);
				System.out.println(applet.getDocumentBase());
			}
			mt.addImage(image, 0);
			mt.waitForAll();
			if (mt.isErrorID(0)) {
				if (new File(filename).exists()) {
					print("エラー: 画像が読み込めない形式です " + filename);
				}
				return null;
			}
			return image;

		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	private BufferedImage createBufferedImage(Image image) {
		int width = image.getWidth(null);
		int height = image.getHeight(null);

		BufferedImage bufferedImage = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics g = bufferedImage.getGraphics();
		g.drawImage(image, 0, 0, null);
		g.dispose();
		return bufferedImage;
	}

	private BufferedImage createTextImage(String text) {
		int width = 100;
		int height = 100;

		BufferedImage bufferedImage = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics g = bufferedImage.getGraphics();
		g.setColor(Color.black);
		g.drawRect(0, 0, width - 1, height - 1);
		g.drawString(text, 20, height / 2);
		g.dispose();
		return bufferedImage;
	}

	/***************************************************************************
	 * start
	 **************************************************************************/

	void start() {
		if (image != null) {
			int w = image.getWidth();
			int h = image.getHeight();
			for (int i = 0; i < 2; i++) {
				rt(90);
				fd(w);
				rt(90);
				fd(h);
			}
		}
	}

	/***************************************************************************
	 * doTransform の オーバーライド
	 **************************************************************************/

	protected synchronized void doTransform() {
		super.doTransform();

		if (image != null) {
			AffineTransform transform = new AffineTransform();
			double centerX = image.getWidth() / 2;
			double centerY = image.getHeight() / 2;
			double rotatedCenterX = rotatedWidth() / 2;
			double rotatedCenterY = rotatedHeight() / 2;

			transform.translate(rotatedCenterX, rotatedCenterY);
			transform.rotate(theta());
			transform.scale(scaleX(), scaleY());
			transform.translate(-centerX, -centerY);

			this.transformOp = new AffineTransformOp(transform,
					AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
		}
	}

	// temp for Turtle
	// 上と重複コード
	protected AffineTransformOp createTransformOp(double theta, double width,
			double height, double rotatedWidth, double rotatedHeight) {

		AffineTransform transform = new AffineTransform();
		double imageWidth = image.getWidth();
		double imageHeight = image.getHeight();
		double centerX = imageWidth / 2;
		double centerY = imageHeight / 2;
		double scaleX = width / imageWidth;
		double scaleY = height / imageHeight;
		double rotatedCenterX = rotatedWidth / 2;
		double rotatedCenterY = rotatedHeight / 2;

		transform.translate(rotatedCenterX, rotatedCenterY);
		transform.rotate(theta);
		transform.scale(scaleX, scaleY);
		transform.translate(-centerX, -centerY);

		AffineTransformOp transformOp = new AffineTransformOp(transform,
				AffineTransformOp.TYPE_NEAREST_NEIGHBOR);

		return transformOp;
	}

	/***************************************************************************
	 * paint
	 **************************************************************************/

	public void draw(Graphics2D g) {

		if (looks() == this) {
			locus().paint(g);
			if (image != null) {
				g.drawImage(image, transformOp, (int) rotatedMinX(),
						(int) rotatedMinY());
			}
			// shape().paint(g);
			return;
		} else {
			super.draw(g);
			return;
		}

	}

}

/*******************************************************************************
 * 
 * Class TextTurtle.
 * 
 ******************************************************************************/

class TextTurtle extends ImageTurtle {

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	private int fontsize = 32;

	private String text = null;

	// private boolean textDirty = false;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public TextTurtle() {
		this("Text Turtle");
	}

	public TextTurtle(int text) {
		this(new Integer(text));
	}

	public TextTurtle(double text) {
		this(new Double(text));
	}

	public TextTurtle(boolean text) {
		this(new Boolean(text));
	}

	public TextTurtle(String text) {
		this((Object) text);
	}

	public TextTurtle(Object text) {
		super();
		text(text.toString());
	}

	/***************************************************************************
	 * setter & getter
	 **************************************************************************/

	public void text(int text) {
		text(new Integer(text));
	}

	public void text(double text) {
		text(new Double(text));
	}

	public void text(boolean text) {
		text(new Boolean(text));
	}

	public void text(String text) {
		text((Object) text);
	}

	public void text(Object text) {
		if (text != null) {
			this.text = text.toString();
		} else {
			this.text = "null";
		}

		resetImage();
	}

	public String text() {
		return text;
	}

	public void fontsize(int fontsize) {
		this.fontsize = fontsize;
		resetImage();
	}

	public int fontsize() {
		return fontsize;
	}

	/**
	 * override
	 */
	public void color(Color penColor) {
		super.color(penColor);
		resetImage();
	}

	/***************************************************************************
	 * Override
	 **************************************************************************/
	//
	// public void draw(Graphics2D g) {
	// if (textDirty) {
	// resetImage();
	// textDirty = false;
	// }
	// super.draw(g);
	// }
	/***************************************************************************
	 * reset image
	 **************************************************************************/

	private synchronized void resetImage() {

		// calculate size
		int height = fontsize;
		Font f = new Font("Dialog", Font.PLAIN, height);
		FontMetrics fm = window.getFontMetrics(f);
		int width = SwingUtilities.computeStringWidth(fm, text);
		if (width == 0) {
			width = 1;
		}
		if (height == 0) {
			height = 1;
		}

		// draw
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) image.getGraphics();
		g.setColor(color());
		g.setFont(f);
		g.drawString(text, 0, height * 4 / 5);
		g.dispose();

		// set new image
		super.setImage(image);

	}

}

/*******************************************************************************
 * 
 * Class TurtleTurtle.
 * 
 ******************************************************************************/

class TurtleTurtle extends Turtle {

	/***************************************************************************
	 * かめ定数
	 **************************************************************************/

	private static final double kameScale = 0.4;

	private static final Color kameColor = Color.green;

	/***************************************************************************
	 * for animation
	 **************************************************************************/

	private static final int rotateWait = 20;

	private static final int moveWait = 20;

	private static int rotateStep = 5;

	private static int moveStep = 5;

	private static boolean withKame = true;

	/***************************************************************************
	 * インスタンス変数
	 **************************************************************************/

	// turtle segments history
	private Vector history;

	private int angle; // turtle current angle (degree)

	private double x, y; // turtle current position

	private double dx, dy; // dx = sin(angle), dy = -cos(angle)

	// private double x0;
	// private double y0; // center

	private boolean penDown; // pen status (up or down)

	private Color c; // pen color

	// turtle animation rubber line
	private int rx, ry;

	private boolean rubber = false;

	// turtle
	private int kameType = 0;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public TurtleTurtle() {
		init();
	}

	/***************************************************************************
	 * 初期化
	 **************************************************************************/

	private void init() {
		x = 100;
		y = 100;
		angle = 0;
		dx = 0;
		dy = -1;
		penDown = true;
		history = new Vector(10);
		c = Color.black;
	}

	/***************************************************************************
	 * スピードの調整
	 **************************************************************************/

	public static boolean speed(int step) {
		if (step <= 0) {
			return false;
		}
		rotateStep = step;
		moveStep = step;
		withKame = (step < 10000);
		return true;
	}

	/***************************************************************************
	 * カメの描画
	 **************************************************************************/

	// set turtle angle
	void kameAngle(int a) {
		dx = Math.sin((double) a * Math.PI / 180.0);
		dy = -Math.cos((double) a * Math.PI / 180.0);
		kameShow(rotateWait);
	}

	// turtle animation update
	void kameShow(int wait) {
		kameType++;
		update();
		if (withKame) {
			sleep(wait / 1000d); // macchan
		}
	}

	// redraw method
	public void paint(Graphics g) {
		for (int i = 0; i < history.size(); i++) {
			Line line = (Line) history.elementAt(i);
			g.setColor(line.c);
			g.drawLine(line.bx, line.by, line.ex, line.ey);
		}
		if (rubber) {
			g.setColor(c);
			g.drawLine(rx, ry, (int) x, (int) y);
		}
		if (withKame) {
			switch ((kameType / 2) % 4) {
				case 0 :
				case 2 :
					kameDraw(g, kame);
					break;
				case 1 :
					kameDraw(g, kameR);
					break;
				case 3 :
					kameDraw(g, kameL);
					break;
			}
		}
	}

	// draw animation turtle
	void kameDraw(Graphics g, short data[][]) {
		int ix = (int) x, iy = (int) y;
		g.setColor(kameColor);
		for (int i = 0; i < data.length; i++) {
			int px = 0, py = 0;
			for (int j = 0; j < data[i].length; j += 2) {
				int kx = data[i][j], ky = data[i][j + 1];
				int nx = (int) ((kx * (-dy) + ky * (-dx)) * kameScale);
				int ny = (int) ((kx * dx + ky * (-dy)) * kameScale);
				if (j > 0)
					g.drawLine(ix + px, iy + py, ix + nx, iy + ny);
				px = nx;
				py = ny;
			}
		}
	}

	/***************************************************************************
	 * Start
	 **************************************************************************/

	// default start method
	void start() {
		System.out.println("Turtle start method called");
	}

	/***************************************************************************
	 * 基本コマンド
	 **************************************************************************/

	// forward n step
	public void fd(int n) {
		double xx = x;
		double yy = y;
		if (penDown) {
			rx = (int) x;
			ry = (int) y;
			rubber = true;
		}
		for (int i = moveStep; i < n; i += moveStep) {
			x = xx + dx * i;
			y = yy + dy * i;
			kameShow(moveWait);
		}
		x = xx + dx * n;
		y = yy + dy * n;
		if (penDown) {
			Line line = new Line((int) xx, (int) yy, (int) x, (int) y, c);
			history.addElement(line);
			rubber = false;
		}
		kameShow(moveWait);
	}

	// backward n step
	public void bk(int n) {
		double xx = x;
		double yy = y;
		if (penDown) {
			rx = (int) x;
			ry = (int) y;
			rubber = true;
		}
		for (int i = moveStep; i < n; i += moveStep) {
			x = xx - dx * i;
			y = yy - dy * i;
			kameShow(moveWait);
		}
		x = xx - dx * n;
		y = yy - dy * n;
		if (penDown) {
			Line line = new Line((int) xx, (int) yy, (int) x, (int) y, c);
			history.addElement(line);
			rubber = false;
		}
		kameShow(moveWait);
	}

	// right turn n degree
	public void rt(int n) {
		for (int i = rotateStep; i < n; i += rotateStep)
			kameAngle(angle + i);
		angle = (angle + n) % 360;
		kameAngle(angle);
	}

	// left turn n degree
	public void lt(int n) {
		for (int i = rotateStep; i < n; i += rotateStep)
			kameAngle(angle - i);
		angle = (angle - n) % 360;
		kameAngle(angle);
	}

	// pen up
	public void up() {
		penDown = false;
	}

	// pen down
	public void down() {
		penDown = true;
	}

	// change color
	public void color(Color nc) {
		c = nc;
	}

	/***************************************************************************
	 * Turtle とのつなぎ
	 **************************************************************************/

	public void rt(double angle) {
		rt((int) angle);
	}

	public void lt(double angle) {
		lt((int) angle);
	}

	public void fd(double angle) {
		fd((int) angle);
	}

	public void bk(double angle) {
		bk((int) angle);
	}

	public void paint(Graphics2D g) {
		if (isShow()) {
			paint((Graphics) g);
		}
	}

	/***************************************************************************
	 * Class Turtle.Line
	 **************************************************************************/

	class Line {
		public int bx;

		public int by;

		public int ex;

		public int ey;

		public Color c;

		public Line(int bx, int by, int ex, int ey, Color c) {
			this.bx = bx;
			this.by = by;
			this.ex = ex;
			this.ey = ey;
			this.c = c;
		}
	}

	/***************************************************************************
	 * カメ
	 **************************************************************************/

	final static short kame[][] = {
			{-12, -6, -12, 6, 0, 18, 12, 6, 12, -6, 0, -18, -12, -6},
			{-18, -12, -12, -6},
			{-6, -24, 0, -18, 6, -24},
			{12, -6, 18, -12},
			{12, 6, 18, 12},
			{-6, 24, 0, 18, 6, 24},
			{-18, 12, -12, 6},
			{-18, 12, -18, -12, -6, -24, 6, -24, 18, -12, 18, 12, 6, 24, -6,
					24, -18, 12}, {-15, -15, -18, -24, -9, -21},
			{9, -21, 18, -24, 15, -15}, {15, 15, 18, 24, 9, 21},
			{-9, 21, -18, 24, -15, 15}, {-3, 24, 0, 30, 3, 24},
			{-6, -24, -12, -36, -6, -48, 6, -48, 12, -36, 6, -24}};

	final static short kameR[][] = {
			{-12, -6, -12, 6, 0, 18, 12, 6, 12, -6, 0, -18, -12, -6},
			{-18, -12, -12, -6},
			{-6, -24, 0, -18, 6, -24},
			{12, -6, 18, -12},
			{12, 6, 18, 12},
			{-6, 24, 0, 18, 6, 24},
			{-18, 12, -12, 6},
			{-18, 12, -18, -12, -6, -24, 6, -24, 18, -12, 18, 12, 6, 24, -6,
					24, -18, 12}, {-15, -15, -24, -18, -9, -21},
			{-9, 21, -24, 18, -15, 15}, {-3, 24, -3, 30, 3, 24},
			{-6, -24, -6, -36, 0, -48, 12, -48, 18, -36, 6, -24},
			{9, -21, 18, -30, 15, -15}, {15, 15, 18, 30, 9, 21}};

	final static short kameL[][] = {
			{-12, -6, -12, 6, 0, 18, 12, 6, 12, -6, 0, -18, -12, -6},
			{-18, -12, -12, -6},
			{-6, -24, 0, -18, 6, -24},
			{12, -6, 18, -12},
			{12, 6, 18, 12},
			{-6, 24, 0, 18, 6, 24},
			{-18, 12, -12, 6},
			{-18, 12, -18, -12, -6, -24, 6, -24, 18, -12, 18, 12, 6, 24, -6,
					24, -18, 12}, {-15, -15, -18, -30, -9, -21},
			{-9, 21, -18, 30, -15, 15}, {-3, 24, 3, 30, 3, 24},
			{-6, -24, -18, -36, -12, -48, 0, -48, 6, -36, 6, -24},
			{9, -21, 24, -18, 15, -15}, {15, 15, 24, 18, 9, 21}};

}

/*******************************************************************************
 * 
 * Class Scale.
 * 
 ******************************************************************************/

class Scale {

	private double x = 1.0;

	private double y = 1.0;

	public Scale(Dimension2D d1, Dimension2D d2) {
		this(d1.getWidth(), d1.getHeight(), d2.getWidth(), d2.getHeight());
	}

	public Scale(double w1, double h1, double w2, double h2) {
		this(w2 / w1, h2 / h1);
	}

	public Scale(double x, double y) {
		this.x = x;
		this.y = y;
	}

	public double x() {
		return x;
	}

	public void x(double x) {
		this.x = x;
	}

	public double y() {
		return y;
	}

	public void y(double y) {
		this.y = y;
	}

	public void add(Scale scale) {
		x = x * scale.x;
		y = y * scale.y;
	}

}

/*******************************************************************************
 * 
 * Class DoubleDimension.
 * 
 ******************************************************************************/

class DoubleDimension extends Dimension2D {

	private double width = 0d;

	private double height = 0d;

	/**
	 * Constructor.
	 */
	public DoubleDimension(double width, double height) {
		setSize(width, height);
	}

	/**
	 * Constructor for copy.
	 */
	public DoubleDimension(Dimension2D dimension) {
		this(dimension.getWidth(), dimension.getHeight());
	}

	public double getWidth() {
		return width;
	}

	public double getHeight() {
		return height;
	}

	public void setSize(double width, double height) {
		this.width = width;
		this.height = height;
	}

	public Object clone() {
		return new DoubleDimension(width, height);
	}

}

/*******************************************************************************
 * 
 * Class RelativePoint.
 * 
 ******************************************************************************/

class RelativePoint {

	private double xMagnification = 0.5d;

	private double yMagnification = 0.5d;

	/**
	 * Constructor.
	 */
	public RelativePoint(double xMagnification, double yMagnification) {
		setMagnification(xMagnification, yMagnification);
	}

	public Point2D getPoint(Rectangle2D rectangle) {
		double x = rectangle.getX();
		double y = rectangle.getY();
		double centerX = x + rectangle.getWidth() * xMagnification;
		double centerY = y + rectangle.getHeight() * yMagnification;
		return new Point2D.Double(centerX, centerY);
	}

	public void setMagnification(double xMagnification, double yMagnification) {
		this.xMagnification = xMagnification;
		this.yMagnification = yMagnification;
	}

}

/*******************************************************************************
 * 
 * Class Line.
 * 
 ******************************************************************************/

class Line {

	private Point2D p1 = null;

	private Point2D p2 = null;

	private Color color = null;

	/**
	 * Constructor.
	 */
	public Line(Point2D p1, Point2D p2, Color color) {
		this(p1.getX(), p1.getY(), p2.getX(), p2.getY(), color);
	}

	/**
	 * Constructor.
	 */
	public Line(double p1X, double p1Y, double p2X, double p2Y, Color color) {
		this.p1 = new Point2D.Double(p1X, p1Y);
		this.p2 = new Point2D.Double(p2X, p2Y);
		this.color = color;
	}

	/**
	 * Constructor for copy.
	 */
	public Line(Line line) {
		p1 = new Point2D.Double(line.p1.getX(), line.p1.getY());
		p2 = new Point2D.Double(line.p2.getX(), line.p2.getY());
		color = line.color;
	}

	public void transform(AffineTransform transform) {
		transform.transform(p1, p1);
		transform.transform(p2, p2);
	}

	public Line2D getLine2D() {
		return new Line2D.Double(p1, p2);
	}

	public void paint(Graphics2D g) {
		Color originalColor = g.getColor();
		g.setColor(color);
		g.draw(getLine2D());
		g.setColor(originalColor);
	}

}

/*******************************************************************************
 * 
 * Class LineList.
 * 
 ******************************************************************************/

class LineList {

	private boolean dirty = false;

	private Rectangle2D bounds = new Rectangle2D.Double(0, 0, 0, 0);

	private List lines = new ArrayList();

	/**
	 * Constructor.
	 */
	public LineList() {
	}

	/**
	 * Constructor for copy.
	 */
	public LineList(LineList lines) {
		Iterator i = lines.lines.iterator();
		while (i.hasNext()) {
			Line line = (Line) i.next();
			this.lines.add(new Line(line));
			dirty = true;
		}
	}

	public synchronized void add(Line line) {
		lines.add(line);
		dirty = true;
	}

	public synchronized void remove(Line line) {
		lines.remove(line);
		dirty = true;
	}

	public synchronized void transform(AffineTransform transform) {
		Iterator i = lines.iterator();
		while (i.hasNext()) {
			Line line = (Line) i.next();
			line.transform(transform);
		}
		dirty = true;
	}

	public synchronized void paint(Graphics2D g) {
		Iterator i = lines.iterator();
		while (i.hasNext()) {
			Line line = (Line) i.next();
			line.paint(g);
		}
	}

	public synchronized Rectangle2D getBounds() {

		if (dirty) {
			synchronized (this) {
				Rectangle2D allBounds = null;
				Iterator i = lines.iterator();
				while (i.hasNext()) {
					Line line = (Line) i.next();
					Rectangle2D bounds = line.getLine2D().getBounds2D();
					if (allBounds == null) {
						allBounds = bounds;
					} else {
						allBounds = allBounds.createUnion(bounds);
					}
				}
				if (allBounds == null) {
					allBounds = new Rectangle2D.Double(0, 0, 0, 0);
				}

				bounds = allBounds;
				dirty = false;
			}
		}

		return bounds;

	}

	public synchronized boolean intersects(Rectangle2D rectangle) {
		Iterator i = lines.iterator();
		while (i.hasNext()) {
			Line line = (Line) i.next();
			if (rectangle.intersectsLine(line.getLine2D())) {
				return true;
			}
		}
		return false;
	}

}

/**
 * コンソールをエミュレートするTextAreaクラス
 * 
 * @author macchan
 * @version $Id: Turtle.java,v 1.1 2009/03/08 21:54:19 hashiyaman Exp $
 */
class ConsoleTextArea extends JTextArea {

	String text;

	// 関連
	public JTextAreaPrintStream out = new JTextAreaPrintStream(this);

	public JTextAreaInputStream in = new JTextAreaInputStream(this);

	/**
	 * コンストラクタ System.out, System.errのつなぎかえを行います
	 */
	public ConsoleTextArea() {
		// Appletはだめだった
		// System.setOut(this.out);
		// System.setIn(this.in);
		this.initialize();
	}

	/**
	 * 初期化します
	 */
	private void initialize() {
		// キーによる、カーソルの移動を阻止する
		this.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				switch (e.getKeyCode()) {
					case KeyEvent.VK_UP :
					case KeyEvent.VK_DOWN :
					case KeyEvent.VK_LEFT :
					case KeyEvent.VK_RIGHT :
					case KeyEvent.VK_HOME :
					case KeyEvent.VK_END :
					case KeyEvent.VK_PAGE_UP :
					case KeyEvent.VK_PAGE_DOWN :
						e.consume();
				}
			}
		});

		// マウスによる、カーソルの移動を阻止する （consume()が動かない）
		// this.addMouseListener(new MouseListener() {
		// public void mouseClicked(MouseEvent e) {
		// e.consume();
		// }
		// public void mouseEntered(MouseEvent e) {
		// e.consume();
		// }
		// public void mouseExited(MouseEvent e) {
		// e.consume();
		// }
		// public void mousePressed(MouseEvent e) {
		// e.consume();
		// }
		// public void mouseReleased(MouseEvent e) {
		// e.consume();
		// }
		// });
	}

}

/**
 * JTextAreaをコンソールにするための System.in エミュレータ
 * 
 * 継承しているPrintableInputStreamのprintln機能を利用して書き込みます
 * Blockの仕組みはPrintableInputStream依存です
 * 
 * @author macchan
 * @version $Id: Turtle.java,v 1.1 2009/03/08 21:54:19 hashiyaman Exp $
 */
class JTextAreaInputStream extends PrintableInputStream {

	// 定数
	private static final int NULL = -1;

	// 状態
	private int inputStartCaretPosition = NULL; // 入力が始まったカーソル位置を覚えておく

	// 関連
	private JTextArea textArea = null;

	/**
	 * Constructor for JTextAreaInputStream.
	 */
	public JTextAreaInputStream(JTextArea textArea) {
		this.textArea = textArea;
		this.initialize();
	}

	/**
	 * 初期化します
	 */
	private void initialize() {
		this.textArea.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				handleKeyPressed(e);
			}
		});
		this.textArea.addInputMethodListener(new InputMethodListener() {
			public void caretPositionChanged(InputMethodEvent event) {
				// donothing
			}

			public void inputMethodTextChanged(InputMethodEvent event) {
				memorizeStartPosition();
			}
		});
	}

	public synchronized void memorizeStartPosition() {
		// 最初の入力なら、位置を覚えておく
		if (this.inputStartCaretPosition == NULL) {
			this.inputStartCaretPosition = this.textArea.getCaretPosition();
		}
	}

	/**
	 * キーが押された時の処理
	 */
	public synchronized void handleKeyPressed(KeyEvent e) {
		this.memorizeStartPosition();

		// Enterなら、書き込み動作を行う
		if (e.getKeyCode() == KeyEvent.VK_ENTER) {
			e.consume();
			this.writeLine();
			this.inputStartCaretPosition = NULL;
			this.textArea.repaint();
		}
	}

	private void writeLine() {
		try {
			int inputEndCaretPosition = this.textArea.getCaretPosition();
			int len = inputEndCaretPosition - this.inputStartCaretPosition;
			String lineString = null;
			if (len <= 0) {
				lineString = "";
			} else {
				lineString = this.textArea.getText(
						this.inputStartCaretPosition, len);
			}

			// 書き込みます
			super.println(lineString);
		} catch (BadLocationException ex) {
			ex.printStackTrace();
		}
	}

}

/**
 * JTextAreaをコンソールにするための System.out エミュレータ
 * 
 * printlnメソッドと、printメソッド、flushメソッドのみオーバーライドします
 * その他は標準のPrintStreamの機能を使い、System.outにコネクトされます
 * 
 * @author macchan
 * @version $Id: Turtle.java,v 1.1 2009/03/08 21:54:19 hashiyaman Exp $
 */
class JTextAreaPrintStream extends PrintStream {

	// 定数
	private static final char CR = '\n';

	// 状態
	private StringBuffer buf = new StringBuffer();

	// フラグ
	private boolean invokeLater = false; // flush動作をSwingスレッドで処理するかどうか

	private boolean caretUpdate = true; // flush時にカーソルを移動するかどうか

	// 関連
	private JTextArea textArea = null;

	/**
	 * コンストラクタ
	 */
	public JTextAreaPrintStream(JTextArea textarea) {
		super(System.out); // System.outは仮コネクト
		this.textArea = textarea;
	}

	/***************************************************************************
	 * print関連 （オーバーライド）
	 **************************************************************************/

	public void print(Object o) {
		this.printImpl(o.toString());
	}

	public void print(String s) {
		this.printImpl(s);
	}

	public void print(int i) {
		this.printImpl(new Integer(i).toString());
	}

	public void print(long l) {
		this.printImpl(new Long(l).toString());
	}

	public void print(char c) {
		this.printImpl(new Character(c).toString());
	}

	public void print(char[] c) {
		this.printImpl(new String(c));
	}

	public void print(boolean b) {
		this.printImpl(new Boolean(b).toString());
	}

	public void print(float s) {
		this.printImpl(new Float(s).toString());
	}

	public void print(double d) {
		this.printImpl(new Double(d).toString());
	}

	/***************************************************************************
	 * println関連 （オーバーライド）
	 **************************************************************************/

	public void println() {
		this.printlnImpl("");
	}

	public void println(Object o) {
		this.printlnImpl(o.toString());
	}

	public void println(String s) {
		this.printlnImpl(s);
	}

	public void println(int i) {
		this.printlnImpl(new Integer(i).toString());
	}

	public void println(long l) {
		this.printlnImpl(new Long(l).toString());
	}

	public void println(char c) {
		this.printlnImpl(new Character(c).toString());
	}

	public void println(char[] c) {
		this.printlnImpl(new String(c));
	}

	public void println(boolean b) {
		this.printlnImpl(new Boolean(b).toString());
	}

	public void println(float s) {
		this.printlnImpl(new Float(s).toString());
	}

	public void println(double d) {
		this.printlnImpl(new Double(d).toString());
	}

	/***************************************************************************
	 * flush関連 （オーバーライド）
	 **************************************************************************/

	/**
	 * textareaにStreamを出力します。
	 */
	public synchronized void flush() {
		this.flushImpl();
	}

	/***************************************************************************
	 * 実装関連
	 **************************************************************************/

	/**
	 * print動作の実装です バッファにため,flushします(自動flush)
	 */
	private void printImpl(String s) {
		this.buf.append(s);
		this.flush();
	}

	/**
	 * println動作の実装です
	 */
	private void printlnImpl(String s) {
		this.printImpl(s + CR);
	}

	/**
	 * flush動作の実装です
	 */
	private void flushImpl() {
		// 前処理
		final String s = this.buf.toString();

		// 本処理(writeTextで書き込む)
		if (this.invokeLater) { // Swingスレッドで書き込み
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					writeText(s);
				}
			});
		} else { // このスレッドで書き込み(このまま実行)
			writeText(s);
		}

		// 後処理
		this.buf = new StringBuffer();

	}

	/***************************************************************************
	 * テキストエリアに書き込み関連
	 **************************************************************************/

	/**
	 * テキストをテキストエリアに書き込みます
	 */
	private void writeText(String s) {

		if (this.textArea == null) {
			throw new NullPointerException("textarea is null");
		}

		// テキストエリアに書き込む
		int caret = this.textArea.getCaretPosition();
		int len = this.textArea.getDocument().getLength();
		int pos = caret < len ? caret : len;
		this.textArea.insert(s, pos);

		// 自動カーソル移動処理
		if (this.caretUpdate) {
			this.textArea.setCaretPosition(pos + s.length());
			this.textArea.repaint();
		}
	}

	/***************************************************************************
	 * Setter, Getter関連
	 **************************************************************************/

	public void setInvokeLater(boolean invokeLater) {
		this.invokeLater = invokeLater;
	}

	public boolean isInvokeLater() {
		return this.invokeLater;
	}

	public void setCaretUpdate(boolean caretUpdate) {
		this.caretUpdate = caretUpdate;
	}

	public boolean isCaretUpdate() {
		return this.caretUpdate;
	}

}

/**
 * Stringを書き込めるInputStreamエミュレータ
 * 
 * write(String)メソッドで一行ずつ書き込むことができます。
 * このクラスを利用する読み手は、read()したときにbufferに書き込まれていなければ、 書き込まれるまでブロックします
 * 
 * @author macchan
 * @version $Id: Turtle.java,v 1.1 2009/03/08 21:54:19 hashiyaman Exp $
 */
class PrintableInputStream extends InputStream {

	// 定数
	private static final char CR = '\n';

	private static final char END_SYMBOL = '\0';

	private static final int END_CODE = -1;

	// 状態
	private byte[] buf = null; // 文字列をbyte列で表現するバッファ

	private int cursor = 0; // byte列をどこまで読んだか保存するカーソル

	/**
	 * コンストラクタ
	 */
	public PrintableInputStream() {
	}

	/**
	 * バッファに書き込みます。 もしブロック中のスレッドがいたら、ブロックを解除します
	 */
	public synchronized void println(String s) {
		// 改行と終了記号を加える
		s = s + CR + END_SYMBOL;

		// 対象文字列をbyte列のバッファに変換する
		byte[] stringBuf = s.getBytes();

		// バッファが終わりに達していたら、そのままバッファに。
		if (this.isBufferEnd()) {
			this.buf = stringBuf;
		}
		// バッファが終わりに達していなかったら、継ぎ足して、新しいバッファを作る
		else {
			int remain = buf.length - cursor; // 例えば8,4の場合、0,1,2,3と4つ終了しているから、残り４つ。
			int newbufsize = remain + stringBuf.length;
			byte[] newBuf = new byte[newbufsize];
			System.arraycopy(buf, cursor, newBuf, 0, remain);
			System.arraycopy(stringBuf, 0, newBuf, remain, stringBuf.length);
			// 例えば13,4の場合、13-4=9こコピーしてその次だから9番目。
			buf = newBuf;
		}

		// カーソルを戻して、ブロックを解除する
		cursor = 0;
		notify();
	}

	/***************************************************************************
	 * オーバーライド
	 **************************************************************************/

	/**
	 * ブロックせずに読み込めるバイト数を返します
	 */
	public synchronized int available() throws IOException {
		return buf == null ? 0 : buf.length - cursor;
	}

	/**
	 * バッファから、一文字読み込みます
	 */
	public synchronized int read() throws IOException {
		int readChar = END_CODE;

		try {
			// 読み込めなければブロック
			if (isBufferEnd()) {
				wait();
			}

			// 一文字読み込む
			readChar = this.buf[cursor];
			this.cursor++;

			// 終わりだったら-1
			if ((char) readChar == END_SYMBOL) {
				return END_CODE;
			}

		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}

		return readChar;
	}

	/***************************************************************************
	 * private
	 **************************************************************************/

	/**
	 * バッファが終了しているかどうか調べます
	 */
	private synchronized boolean isBufferEnd() {
		return this.buf == null ? true : this.cursor >= this.buf.length;
	}
}

class CardTurtle extends ImageTurtle {

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	private int fontsize = 18;

	private String text = null;

	// private boolean textDirty = false;

	protected HolderTurtle parent;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public CardTurtle() {
		this("Card Turtle");
	}

	public CardTurtle(int text) {
		this(new Integer(text));
	}

	public CardTurtle(double text) {
		this(new Double(text));
	}

	public CardTurtle(boolean text) {
		this(new Boolean(text));
	}

	public CardTurtle(String text) {
		this((Object) text);
	}

	public CardTurtle(Object text) {
		super();
		text(text.toString());
	}

	/***************************************************************************
	 * setter & getter
	 **************************************************************************/

	public void text(int text) {
		text(new Integer(text));
	}

	public void text(double text) {
		text(new Double(text));
	}

	public void text(boolean text) {
		text(new Boolean(text));
	}

	public void text(String text) {
		text((Object) text);
	}

	public void text(Object text) {
		if (text != null) {
			this.text = text.toString();
		} else {
			this.text = "null";
		}

		resetImage();
	}

	public String text() {
		return text;
	}

	public void fontsize(int fontsize) {
		this.fontsize = fontsize;
		resetImage();
	}

	public int fontsize() {
		return fontsize;
	}

	/**
	 * override
	 */
	public void color(Color penColor) {
		super.color(penColor);
		resetImage();
	}

	/***************************************************************************
	 * Override
	 **************************************************************************/
	//
	// public void draw(Graphics2D g) {
	// if (textDirty) {
	// resetImage();
	// textDirty = false;
	// }
	// super.draw(g);
	// }
	/***************************************************************************
	 * reset image
	 **************************************************************************/
	int margin = 5;

	private synchronized void resetImage() {

		// calculate size
		int height = fontsize;
		Font f = new Font("Dialog", Font.PLAIN, height);
		FontMetrics fm = window.getFontMetrics(f);
		int width = SwingUtilities.computeStringWidth(fm, text);

		// draw
		width += margin * 2;
		height += margin * 2;
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) image.getGraphics();
		g.setColor(color());

		// 枠線
		g.drawRect(1, 1, width - 2, height - 2);

		// 字
		g.setFont(f);
		g.drawString(text, margin, height * 4 / 5);

		g.dispose();

		// set new image
		super.setImage(image);

	}

}

class HolderTurtle extends ImageTurtle {

	private static final int MARGIN = 5;

	private LinkedList<ImageTurtle> children = new LinkedList<ImageTurtle>();
	private int cursor = 0;

	public HolderTurtle() {
		resetImage();
	}

	/***************************************************************************
	 * Cursor類
	 **************************************************************************/

	public int カーソル位置() {
		return cursor + 1;
	}

	public void カーソル位置を変える(int newCursor) {
		if (newCursor >= 1 && newCursor <= children.size()) {// 正常
			this.cursor = newCursor - 1;
		} else if (children.size() != 0) {// 範囲外
			this.cursor = (newCursor % children.size()) - 1;
		} else {// 0の場合
			this.cursor = 0;
		}
		resetImage();
	}

	public void カーソルを進める() {
		カーソル位置を変える(cursor + 2);
	}

	public void カーソルを戻す() {
		カーソル位置を変える(cursor);
	}

	public ImageTurtle カーソル位置にあるもの() {
		if (children.size() <= 0) {
			return NullTurtle.NULL_TURTLE;
		}
		return children.get(cursor);
	}

	public ImageTurtle 以下のカーソル位置にあるもの(int i) {
		return children.get(i - 1);
	}

	public int カーソル位置にあるものの数値() {
		try {
			return Integer.parseInt(カーソル位置にあるものの内容());
		} catch (Exception ex) {
			return -1;
		}
	}

	public String カーソル位置にあるものの内容() {
		try {
			ImageTurtle object = children.get(cursor);
			if (object instanceof CardTurtle) {
				return ((CardTurtle) object).text();
			} else {
				return ((TextTurtle) object).text();
			}
		} catch (Exception ex) {
			return "NULL";
		}
	}

	/***************************************************************************
	 * 追加と削除
	 **************************************************************************/

	public void 最後に追加する(ImageTurtle turtle) {
		parentCheck(turtle);
		children.addLast(turtle);
		doPostAddProcess(turtle);
	}

	public void 先頭に追加する(ImageTurtle turtle) {
		parentCheck(turtle);
		children.addFirst(turtle);
		doPostAddProcess(turtle);
	}

	public void カーソル位置に追加する(ImageTurtle turtle) {
		parentCheck(turtle);
		children.add(this.cursor, turtle);
		doPostAddProcess(turtle);
	}

	private void doPostAddProcess(ImageTurtle turtle) {
		turtle.hide();
		turtle.parent = this;
		resetImage();
	}

	private void parentCheck(ImageTurtle turtle) {
		if (turtle.parent != null) {
			turtle.parent.削除する(turtle);
		}
	}

	public void 入っている全てのものを以下の入れ物に移動する(HolderTurtle to) {
		int objectCount = this.入っているものの個数();
		for (int i = 0; i < objectCount; i++) {
			to.最後に追加する(this.カーソル位置にあるもの());
		}
	}

	public void 入っているものを全て捨てる() {
		for (ImageTurtle child : children) {
			child.die();
		}
		children.clear();
		resetImage();
	}

	public void カーソル位置のものを削除する() {
		削除する(カーソル位置にあるもの());
	}

	public void 削除する(ImageTurtle turtle) {
		removeObjectInternal(turtle);
	}

	private ImageTurtle removeObjectInternal(ImageTurtle turtle) {
		if (turtle == null || !children.contains(turtle)) {
			System.err.println("削除できません");
			return null;
		}

		children.remove(turtle);
		turtle.parent = null;
		turtle.show();
		resetImage();
		return turtle;
	}

	/***************************************************************************
	 * その他Public
	 **************************************************************************/

	public int 入っているものの個数() {
		return children.size();
	}

	public void かきまぜる() {
		Collections.shuffle(children);
		resetImage();
	}

	/***************************************************************************
	 * デバッグ用
	 * 
	 * @deprecated
	 **************************************************************************/
	void printChildren() {
		int objectCount = this.入っているものの個数();
		for (int i = 0; i < objectCount; i++) {
			カーソル位置を変える(i + 1);
			System.out.print(this.カーソル位置にあるものの数値() + ",");
		}
		カーソル位置を変える(1);
	}

	/***************************************************************************
	 * 描画Strategy
	 **************************************************************************/

	private synchronized void resetImage() {

		// save original location
		int orgX = getX() - getWidth() / 2;
		int orgY = getY() - getHeight() / 2;

		// calculate size
		int width = 0;
		int height = 0;
		if (children.size() != 0) {
			width = MARGIN;
			height = 0;
			for (ImageTurtle child : children) {
				width += (int) child.image().getWidth() + MARGIN;
				height = height > (int) child.height() ? height : (int) child
						.height();
			}
			height += MARGIN * 2;
		} else {
			// Default Size
			width = 60;
			height = 30;
		}

		// draw
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) image.getGraphics();

		// 枠線
		g.setColor(color());
		g.drawRect(1, 1, width - 2, height - 2);

		// children
		int x = MARGIN;
		int y = MARGIN;
		for (int i = 0; i < children.size(); i++) {
			ImageTurtle child = children.get(i);
			g.drawImage(child.image(), x, y, null);

			// カーソル
			if (cursor == i) {
				g.setColor(Color.red);
				Stroke original = g.getStroke();
				g.setStroke(new BasicStroke(3));
				g.drawRect(x, y, child.image().getWidth(), child.image()
						.getHeight());
				g.setStroke(original);
				g.setColor(color());
			}

			x += child.image().getWidth() + MARGIN;
		}

		g.dispose();

		// set new image
		super.setImage(image);

		// set to original x, y
		x(orgX + getWidth() / 2);
		y(orgY + getHeight() / 2);
	}
}

/**
 * InputTurtle
 */
class InputTurtle extends ImageTurtle {

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	private boolean active = true;
	private boolean isJapaneseMode = false;
	private int fontsize = 20;

	private String text = null;

	private TextConverter textConverter = new TextConverter();

	// private boolean textDirty = false;

	/***************************************************************************
	 * 定数
	 **************************************************************************/
	// キーコード
	private static int BIGIN_ALPHABET = 65;// aのキーコード
	private static int BIGIN_NUMBER = 48;// 0のキーコード

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public InputTurtle() {
		this("");
	}

	public InputTurtle(int text) {
		this(new Integer(text));
	}

	public InputTurtle(double text) {
		this(new Double(text));
	}

	public InputTurtle(boolean text) {
		this(new Boolean(text));
	}

	public InputTurtle(String text) {
		this((Object) text);
	}

	public InputTurtle(Object text) {
		super();
		text(text.toString());
		Turtle.defaultTurtle.inputTurtles.add(this);
	}

	/***************************************************************************
	 * 入力関連
	 **************************************************************************/

	// テキストの入力を受け付ける
	protected void captureText(KeyEvent evt) {
		if (!active) {
			return;
		}

		String newText = this.text;
		if (evt.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
			newText = choppedText();
		} else {
			// newText += getSingleByteCharacter(Turtle.defaultTurtle.key());
			// newText += evt.getKeyChar();
			newText += getSingleByteCharacter(evt.getKeyCode());
		}

		if (isJapaneseMode) {
			newText = textConverter.convert(newText);
		}

		text(newText);
		resetImage();
	}

	public void clearText() {
		text("");
	}

	private String choppedText() {
		String text = text();
		if (text.length() != 0) {
			return text = text.substring(0, text.length() - 1);
		} else {
			return text;
		}
	}

	private String getSingleByteCharacter(int keyCode) {
		if (keyCode == -1) {
			return "";
		}
		// String text = KeyEvent.getKeyText(keyCode);
		// return text.toLowerCase();

		if (keyCode >= BIGIN_ALPHABET && keyCode <= (BIGIN_ALPHABET + 25)) {// アルファベット
			char key = (char) (keyCode + 32);
			return String.valueOf(key);
		} else if (keyCode >= BIGIN_NUMBER && keyCode <= (BIGIN_NUMBER + 9)) {// 数字
			int number = keyCode - 48;
			return String.valueOf(number);
		} else if (keyCode == KeyEvent.VK_COMMA) {// コンマ
			return ",";
		} else if (keyCode == KeyEvent.VK_PERIOD) {// ピリオド
			return ".";
		} else if (keyCode == KeyEvent.VK_MINUS) {
			return "ー";
		} else if (keyCode == KeyEvent.VK_EXCLAMATION_MARK) {
			return "!";
		} else if (keyCode == KeyEvent.VK_SLASH) {
			return "?";
		} else if (keyCode == KeyEvent.VK_ESCAPE) {
			isJapaneseMode = !isJapaneseMode;
			return "";
		}
		// else if (keyCode == KeyEvent.VK_FULL_WIDTH) {
		// toJapaneseMode();
		// return "";
		// } else if (keyCode == KeyEvent.VK_HALF_WIDTH) {
		// toEnglishMode();
		// return "";
		// }
		else {
			return "";
		}
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public boolean isActive() {
		return this.active;
	}

	public void toJapaneseMode() {
		this.isJapaneseMode = true;
	}

	public void toEnglishMode() {
		this.isJapaneseMode = false;
	}

	/***************************************************************************
	 * setter & getter
	 **************************************************************************/

	public void text(int text) {
		text(new Integer(text));
	}

	public void text(double text) {
		text(new Double(text));
	}

	public void text(boolean text) {
		text(new Boolean(text));
	}

	public void text(String text) {
		text((Object) text);
	}

	public void text(Object text) {
		if (text != null) {
			this.text = text.toString();
		} else {
			this.text = "null";
		}

		resetImage();
	}

	public String text() {
		return text;
	}

	public void fontsize(int fontsize) {
		this.fontsize = fontsize;
		resetImage();
	}

	public int fontsize() {
		return fontsize;
	}

	/**
	 * override
	 */
	public void color(Color penColor) {
		super.color(penColor);
		resetImage();
	}

	/***************************************************************************
	 * Override
	 **************************************************************************/
	//
	// public void draw(Graphics2D g) {
	// if (textDirty) {
	// resetImage();
	// textDirty = false;
	// }
	// super.draw(g);
	// }
	/***************************************************************************
	 * reset image
	 **************************************************************************/
	int margin = 5;

	private synchronized void resetImage() {

		// save original location
		int orgX = getX() - getWidth() / 2;
		int orgY = getY() - getHeight() / 2;

		// calculate size
		int height = fontsize;
		Font f = new Font("Dialog", Font.PLAIN, height);
		FontMetrics fm = window.getFontMetrics(f);
		int width = SwingUtilities.computeStringWidth(fm, text);

		// draw
		width += margin * 2;
		height += margin * 2;
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) image.getGraphics();
		g.setColor(color());

		// 枠線
		g.drawRect(1, 1, width - 2, height - 2);

		// 字
		g.setFont(f);
		g.drawString(text, margin, height * 4 / 5);

		g.dispose();

		// set new image
		super.setImage(image);

		// set to original x, y
		x(orgX + getWidth() / 2);
		y(orgY + getHeight() / 2);

	}

	public void die() {
		Turtle.defaultTurtle.inputTurtles.remove(this);
	}
}
class NullTurtle extends CardTurtle {

	public static final NullTurtle NULL_TURTLE = new NullTurtle();

	private NullTurtle() {
		super("Null");
	}

	public void text(Object text) {
		// 何もしない
		System.err.println("NullTurtle#text() 不正な操作です．");
	}

	public void hide() {
		// 何もしない
		System.err.println("NullTurtle#hide() 不正な操作です．");
	}
}

/**
 * TextConverter
 */
class TextConverter {

	private static final Map<String, String> jptable = new LinkedHashMap<String, String>();

	static {

		jptable.put("ttsa", "っつぁ");
		jptable.put("ttsi", "っつぃ");
		jptable.put("ttsu", "っつ");
		jptable.put("ttse", "っつぇ");
		jptable.put("ttso", "っつぉ");

		jptable.put("ssha", "っしゃ");
		jptable.put("sshi", "っし");
		jptable.put("sshu", "っしゅ");
		jptable.put("sshe", "っしぇ");
		jptable.put("ssho", "っしょ");
		jptable.put("ssya", "っしゃ");
		jptable.put("ssyi", "っしぃ");
		jptable.put("ssyu", "っしゅ");
		jptable.put("ssye", "っしぇ");
		jptable.put("ssyo", "っしょ");

		jptable.put("ccha", "っちゃ");
		jptable.put("cchi", "っち");
		jptable.put("cchu", "っちゅ");
		jptable.put("cche", "っちぇ");
		jptable.put("ccho", "っちょ");
		jptable.put("ttya", "っちゃ");
		jptable.put("ttyi", "っちぃ");
		jptable.put("ttyu", "っちゅ");
		jptable.put("ttye", "っちぇ");
		jptable.put("ttyo", "っちょ");

		jptable.put("kka", "っか");
		jptable.put("kki", "っき");
		jptable.put("kku", "っく");
		jptable.put("kke", "っけ");
		jptable.put("kko", "っこ");
		jptable.put("ssa", "っさ");
		jptable.put("ssi", "っし");
		jptable.put("ssu", "っす");
		jptable.put("sse", "っせ");
		jptable.put("sso", "っそ");
		jptable.put("tta", "った");
		jptable.put("tti", "っち");
		jptable.put("ttu", "っつ");
		jptable.put("tte", "って");
		jptable.put("tto", "っと");
		jptable.put("hha", "っは");
		jptable.put("hhi", "っひ");
		jptable.put("hhu", "っふ");
		jptable.put("hhe", "っへ");
		jptable.put("hho", "っほ");
		jptable.put("ppa", "っぱ");
		jptable.put("ppi", "っぴ");
		jptable.put("ppu", "っぷ");
		jptable.put("ppe", "っぺ");
		jptable.put("ppo", "っぽ");

		jptable.put("tha", "てゃ");
		jptable.put("thi", "てぃ");
		jptable.put("thu", "てゅ");
		jptable.put("the", "てぇ");
		jptable.put("tho", "てょ");
		jptable.put("tsa", "つぁ");
		jptable.put("tsi", "つぃ");
		jptable.put("tsu", "つ");
		jptable.put("tse", "つぇ");
		jptable.put("tso", "つぉ");
		jptable.put("jya", "じゃ");
		jptable.put("jyi", "じぃ");
		jptable.put("jyu", "じゅ");
		jptable.put("jye", "じぇ");
		jptable.put("jyo", "じょ");
		jptable.put("bya", "びゃ");
		jptable.put("byi", "びぃ");
		jptable.put("byu", "びゅ");
		jptable.put("bye", "びぇ");
		jptable.put("byo", "びょ");
		jptable.put("sya", "しゃ");
		jptable.put("syi", "しぃ");
		jptable.put("syu", "しゅ");
		jptable.put("sye", "しぇ");
		jptable.put("syo", "しょ");
		jptable.put("sha", "しゃ");
		jptable.put("shi", "し");
		jptable.put("shu", "しゅ");
		jptable.put("she", "しぇ");
		jptable.put("sho", "しょ");
		jptable.put("mya", "みゃ");
		jptable.put("myi", "みぃ");
		jptable.put("myu", "みゅ");
		jptable.put("mye", "みぇ");
		jptable.put("myo", "みょ");
		jptable.put("rya", "りゃ");
		jptable.put("ryi", "りぃ");
		jptable.put("ryu", "りゅ");
		jptable.put("rye", "りぇ");
		jptable.put("ryo", "りょ");
		jptable.put("kya", "きゃ");
		jptable.put("kyi", "きぃ");
		jptable.put("kyu", "きゅ");
		jptable.put("kye", "きぇ");
		jptable.put("kyo", "きょ");
		jptable.put("gya", "ぎゃ");
		jptable.put("gyi", "ぎぃ");
		jptable.put("gyu", "ぎゅ");
		jptable.put("gye", "ぎぇ");
		jptable.put("gyo", "ぎょ");
		jptable.put("tya", "ちゃ");
		jptable.put("tyi", "ちぃ");
		jptable.put("tyu", "ちゅ");
		jptable.put("tye", "ちぇ");
		jptable.put("tyo", "ちょ");
		jptable.put("nya", "にゃ");
		jptable.put("nyi", "にぃ");
		jptable.put("nyu", "にゅ");
		jptable.put("nye", "にぇ");
		jptable.put("nyo", "にょ");
		jptable.put("hya", "ひゃ");
		jptable.put("hyi", "ひぃ");
		jptable.put("hyu", "ひゅ");
		jptable.put("hye", "ひぇ");
		jptable.put("hyo", "ひょ");
		jptable.put("ja", "じゃ");
		jptable.put("ji", "じ");
		jptable.put("ju", "じゅ");
		jptable.put("je", "じぇ");
		jptable.put("jo", "じょ");
		jptable.put("cha", "ちゃ");
		jptable.put("chi", "ち");
		jptable.put("chu", "ちゅ");
		jptable.put("che", "ちぇ");
		jptable.put("cho", "ちょ");
		jptable.put("fa", "ふぁ");
		jptable.put("fi", "ふぃ");
		jptable.put("fu", "ふ");
		jptable.put("fe", "ふぇ");
		jptable.put("fo", "ふぉ");

		jptable.put("xka", "ヵ");
		jptable.put("xke", "ヶ");
		jptable.put("xtu", "っ");
		jptable.put("xna", "んあ");
		jptable.put("xni", "ぃ");
		jptable.put("xnu", "んう");
		jptable.put("xne", "んえ");
		jptable.put("xno", "んお");
		jptable.put("xya", "ゃ");
		jptable.put("xyu", "ゅ");
		jptable.put("xyo", "ょ");
		jptable.put("xwa", "ゎ");
		jptable.put("xn", "ん");
		jptable.put("xa", "ぁ");
		jptable.put("xi", "ぃ");
		jptable.put("xu", "ぅ");
		jptable.put("xe", "ぇ");
		jptable.put("xo", "ぉ");

		jptable.put("ka", "か");
		jptable.put("ki", "き");
		jptable.put("ku", "く");
		jptable.put("ke", "け");
		jptable.put("ko", "こ");
		jptable.put("sa", "さ");
		jptable.put("si", "し");
		jptable.put("su", "す");
		jptable.put("se", "せ");
		jptable.put("so", "そ");
		jptable.put("ta", "た");
		jptable.put("ti", "ち");
		jptable.put("tu", "つ");
		jptable.put("te", "て");
		jptable.put("to", "と");
		jptable.put("na", "な");
		jptable.put("ni", "に");
		jptable.put("nu", "ぬ");
		jptable.put("ne", "ね");
		jptable.put("no", "の");
		jptable.put("ha", "は");
		jptable.put("hi", "ひ");
		jptable.put("hu", "ふ");
		jptable.put("he", "へ");
		jptable.put("ho", "ほ");
		jptable.put("ma", "ま");
		jptable.put("mi", "み");
		jptable.put("mu", "む");
		jptable.put("me", "め");
		jptable.put("mo", "も");
		jptable.put("ya", "や");
		jptable.put("yi", "い");
		jptable.put("yu", "ゆ");
		jptable.put("ye", "いぇ");
		jptable.put("yo", "よ");
		jptable.put("ra", "ら");
		jptable.put("ri", "り");
		jptable.put("ru", "る");
		jptable.put("re", "れ");
		jptable.put("ro", "ろ");
		jptable.put("wa", "わ");
		jptable.put("wi", "うぃ");
		jptable.put("wu", "う");
		jptable.put("we", "うぇ");
		jptable.put("wo", "を");

		jptable.put("nn", "ん");

		jptable.put("ga", "が");
		jptable.put("gi", "ぎ");
		jptable.put("gu", "ぐ");
		jptable.put("ge", "げ");
		jptable.put("go", "ご");
		jptable.put("za", "ざ");
		jptable.put("zi", "じ");
		jptable.put("zu", "ず");
		jptable.put("ze", "ぜ");
		jptable.put("zo", "ぞ");
		jptable.put("da", "だ");
		jptable.put("di", "ぢ");
		jptable.put("du", "づ");
		jptable.put("de", "で");
		jptable.put("do", "ど");
		jptable.put("ba", "ば");
		jptable.put("bi", "び");
		jptable.put("bu", "ぶ");
		jptable.put("be", "べ");
		jptable.put("bo", "ぼ");
		jptable.put("pa", "ぱ");
		jptable.put("pi", "ぴ");
		jptable.put("pu", "ぷ");
		jptable.put("pe", "ぺ");
		jptable.put("po", "ぽ");
		jptable.put("la", "ぁ");
		jptable.put("li", "ぃ");
		jptable.put("lu", "ぅ");
		jptable.put("le", "ぇ");
		jptable.put("lo", "ぉ");

		jptable.put("a", "あ");
		jptable.put("i", "い");
		jptable.put("u", "う");
		jptable.put("e", "え");
		jptable.put("o", "お");

		jptable.put("，", "、");
		jptable.put(",", "、");
		jptable.put("．", "。");
		jptable.put(".", "。");
	}

	public String convert(String input) {
		Set<String> keys = jptable.keySet();
		for (String key : keys) {
			int index = input.indexOf(key);
			if (index != -1) {
				return input.substring(0, index) + jptable.get(key);
			}
		}
		return input;
	}
}

class ButtonTurtle extends ImageTurtle {

	public enum State {
		REREASED, PRESSED, CLICKED
	};

	/***************************************************************************
	 * 変数
	 **************************************************************************/

	private int fontsize = 18;

	private String text = null;

	private State state = State.REREASED;

	// private boolean textDirty = false;

	/***************************************************************************
	 * コンストラクタ
	 **************************************************************************/

	public ButtonTurtle() {
		this("Button Turtle");
	}

	public ButtonTurtle(int text) {
		this(new Integer(text));
	}

	public ButtonTurtle(double text) {
		this(new Double(text));
	}

	public ButtonTurtle(boolean text) {
		this(new Boolean(text));
	}

	public ButtonTurtle(String text) {
		this((Object) text);
	}

	public ButtonTurtle(Object text) {
		super();
		text(text.toString());
	}

	/***************************************************************************
	 * ボタン関連
	 **************************************************************************/

	public boolean isClicked() {
		// int currentState = checkButtonStatus();
		return (state == State.CLICKED);
	}

	private void checkButtonStatus() {
		switch (state) {
			case REREASED :
				if (isPressed()) {
					state = State.PRESSED;
					resetImage();
				}
				break;
			case PRESSED :
				if (!isPressed()) {
					state = State.CLICKED;
					resetImage();
				}
				break;
			case CLICKED :
				if (isPressed()) {
					state = State.PRESSED;
					resetImage();
				} else {
					state = State.REREASED;
					resetImage();
				}
				break;
			default :
				throw new RuntimeException();
		}
	}

	private boolean isPressed() {
		return Turtle.defaultTurtle.mouseDown()
				&& this.contains(Turtle.defaultTurtle.mouseX(),
						Turtle.defaultTurtle.mouseY());
	}

	/***************************************************************************
	 * setter & getter
	 **************************************************************************/

	public void text(int text) {
		text(new Integer(text));
	}

	public void text(double text) {
		text(new Double(text));
	}

	public void text(boolean text) {
		text(new Boolean(text));
	}

	public void text(String text) {
		text((Object) text);
	}

	public void text(Object text) {
		if (text != null) {
			this.text = text.toString();
		} else {
			this.text = "null";
		}

		resetImage();
	}

	public String text() {
		return text;
	}

	public void fontsize(int fontsize) {
		this.fontsize = fontsize;
		resetImage();
	}

	public int fontsize() {
		return fontsize;
	}

	/**
	 * override
	 */
	public void color(Color penColor) {
		super.color(penColor);
		resetImage();
	}

	public void draw(Graphics2D g) {
		// System.out.println(this);
		super.draw(g);
		checkButtonStatus();
	}

	/***************************************************************************
	 * Override
	 **************************************************************************/
	//
	// public void draw(Graphics2D g) {
	// if (textDirty) {
	// resetImage();
	// textDirty = false;
	// }
	// super.draw(g);
	// }
	/***************************************************************************
	 * reset image
	 **************************************************************************/

	int margin = 5;

	private synchronized void resetImage() {

		// calculate size
		int height = fontsize;
		Font f = new Font("Dialog", Font.PLAIN, height);
		FontMetrics fm = window.getFontMetrics(f);
		int width = SwingUtilities.computeStringWidth(fm, text);

		// draw
		width += margin * 2;
		height += margin * 2;
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_4BYTE_ABGR);
		Graphics2D g = (Graphics2D) image.getGraphics();
		g.setColor(color());

		// 背景
		Color c = g.getColor();
		if (state == State.PRESSED) {
			g.setColor(Color.BLACK);
		} else if (state == State.REREASED) {
			g.setColor(Color.WHITE);
		} else if (state == State.CLICKED) {
			g.setColor(Color.RED);
		} else {
			throw new RuntimeException();
		}
		g.fillRect(1, 1, width - 2, height - 2);
		g.setColor(c);

		// 枠線
		g.drawRect(1, 1, width - 2, height - 2);

		// 字
		g.setFont(f);
		g.drawString(text, margin, height * 4 / 5);

		g.dispose();

		// set new image
		super.setImage(image);

	}

}
