/* 
 *    Copyright 2013 Mimisuke
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

import java.awt.*;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;

import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import javax.imageio.*;

import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

public class SimYukkuri extends JFrame {
	static final long serialVersionUID = 1L;
	static final String TITLE = "しむゆっくり/SimYukkuri";
	static final String VERSION = "Ver 1.13";
	static final Object lock = new Object();
	static boolean initialized = false;
/*---------------------------------------------------------------------------------------*/
	static final int TOOL = 0, FOOD = 1, CLEAN = 2, ACCESSORY = 3, PICKUP = 4, PANTS = 5, TOILET = 6, BARRIER = 7, TOYS = 8, BELTCONVEYOR = 9, BREEDINGPOOL = 10 ,GARBAGECHUTE = 11, MACHINEPRESS = 12;
/*---------------------------------------------------------------------------------------*/
	static final int PUNISH = 0, HAMMER = 1, VIBRATOR = 2, JUICE = 3, DECOPING = 4;
/*---------------------------------------------------------------------------------------*/
	static final int BACK = 0, THISSIDE = 1, RIGHT = 2, LEFT = 3;
/*---------------------------------------------------------------------------------------*/
	static final int NORMAL = 0, BITTER = 1, SELFFEEDER = 2, LEMONPOP = 3, HOT = 4, VIYUGRA = 5;
	static final int INDIVIDUAL = 0, ALL = 1;
	static final int SET = 0, DEL = 1;
	static int selectedTool = 0, selectedFood = 0, selectedCleaner = 0, selectedBarrier = 0, selectedBeltconveyor = 0, selectedGameSpeed = 1;
	JLabel title;
	JPanel rootPane	= new JPanel();
	JPanel buttonPane = new JPanel();
	JComboBox s1, s2, s3;
	static JLabel l1, l2, l3, l4, l5, l6, l7, l8;
/*---------------------------------------------------------------------------------------*/
	JButton saveButton, loadButton, addYukkuriButton, languageButton, pauseButton, showLogButton, nextPageLogButton, clearLogButton;

	static int clearLogTime = 0;
/*---------------------------------------------------------------------------------------*/
	static myPane mypane  = new myPane();
	static Thread mythread;
	static int barrierSX = -1, barrierSY = -1;
	static int barrierEX = -1, barrierEY = -1;

	public SimYukkuri() {	
		super(TITLE);

		buttonPane.setLayout(new GridLayout(20, 1));
		buttonPane.setBorder(new LineBorder(Color.gray, 2));

		title = new JLabel();
		buttonPane.add(title);
		s1 = new JComboBox();
		s2 = new JComboBox();
		s3 = new JComboBox();
		l1 = new JLabel(" ");
		l2 = new JLabel(" ");
		l3 = new JLabel(" ");
		l4 = new JLabel(" ");
		l5 = new JLabel(" ");
		l6 = new JLabel(" ");
		l7 = new JLabel(" ");
		l8 = new JLabel(" ");
		saveButton = new JButton();
		loadButton = new JButton();
		addYukkuriButton = new JButton();
		languageButton = new JButton();
/*---------------------------------------------------------------------------------------*/
		showLogButton = new JButton();
		nextPageLogButton = new JButton();
		clearLogButton = new JButton();
/*---------------------------------------------------------------------------------------*/	
		if (java.util.Locale.getDefault().getLanguage().startsWith("en"))
			setLanguage(Body.Language.ENGLISH);
		else
			setLanguage(Body.Language.JAPANESE);
		MyItemListener mil = new MyItemListener();
		s1.addItemListener(mil);
		s2.addItemListener(mil);
		s3.addItemListener(mil);
		MyKeyListener mkl = new MyKeyListener();
		s1.addKeyListener(mkl);
		s2.addKeyListener(mkl);
		s3.addKeyListener(mkl);
		buttonPane.add(s1);
		buttonPane.add(s2);
		buttonPane.add(s3);
		ButtonListener buttonListener = new ButtonListener();
		addYukkuriButton.addActionListener(buttonListener);
		buttonPane.add(addYukkuriButton);
		saveButton.addActionListener(buttonListener);
		buttonPane.add(saveButton);
		loadButton.addActionListener(buttonListener);
		buttonPane.add(loadButton);
		languageButton.addActionListener(buttonListener);
		buttonPane.add(languageButton);
/*---------------------------------------------------------------------------------------*/
		showLogButton.addActionListener(buttonListener);
		buttonPane.add(showLogButton);
		nextPageLogButton.addActionListener(buttonListener);
		buttonPane.add(nextPageLogButton);
		clearLogButton.addActionListener(buttonListener);
		buttonPane.add(clearLogButton);
/*---------------------------------------------------------------------------------------*/
		buttonPane.add(l2);
		buttonPane.add(l3);
		buttonPane.add(l4);
		buttonPane.add(l5);
		buttonPane.add(l6);
		buttonPane.add(l7);
		buttonPane.add(l8);

		// setup my pane
		MyMouseListener mml = new MyMouseListener();
		mypane.addMouseListener(mml);
		mypane.addMouseMotionListener(mml);

		// setup root pane
		rootPane.setLayout(new BorderLayout());
		rootPane.add("Center", mypane);
		rootPane.add("East", buttonPane);
		// setup my frame
		setSize(1024, 600);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLocation(new Point(100, 100));
		setContentPane(rootPane);
		setResizable(false);
		setVisible(true);
	}

	public static void main(String[] args) {
		new SimYukkuri();
		mypane.isRunning = true;
		mythread = new Thread(mypane);
		mythread.start();
	}

	//言語 / Language
	private void setLanguage(Body.Language language) {
		synchronized(lock) {
			Body.setLanguage(language);
		}
		if(language == Body.Language.ENGLISH) {
			title.setText("SimYukkuri " + VERSION + " ");
			final int selectedIndex = s1.getSelectedIndex();
			s1.setModel(new DefaultComboBoxModel(new String[]{"Tool", "Food", "Clean", "Accessory", "Pick up", "Pants", "Toilet", "Barrier", "Toys",  "Belt conveyor", "Breeding pool", "Garbage chute", "Machine press"}));
			if(selectedIndex > -1)
				s1.setSelectedIndex(selectedIndex);
			s3.setModel(new DefaultComboBoxModel(new String[]{"Pause", "Speed: x1", "Speed: x5", "Speed: x10", "Speed: MAX"}));
			s3.setSelectedIndex(selectedGameSpeed);
			addYukkuriButton.setText("Add Yukkuri");
			saveButton.setText("Save");
			loadButton.setText("Load");
			languageButton.setText("English");
/*---------------------------------------------------------------------------------------*/
			showLogButton.setText("Show Log");
			nextPageLogButton.setText("Next Page");
			clearLogButton.setText("Clear Log");
/*---------------------------------------------------------------------------------------*/
			l2.setText("Status of Yukkuri");
			l3.setText(" Stress: - ");
			l4.setText(" Damage: - ");
			l5.setText(" Hungry: - ");
			l6.setText(" Shit: - ");
			l7.setText(" Rapist: - ");
			l8.setText(" Rude: - ");
		}
		else if(language == Body.Language.JAPANESE) {
			title.setText("しむゆっくり " + VERSION + " ");
			final int selectedIndex = s1.getSelectedIndex();
/*---------------------------------------------------------------------------------------*/
			s1.setModel(new DefaultComboBoxModel(new String[]{"道具", "えさ", "清掃", "おかざり", "持ち上げる", "おくるみ", "トイレ", "壁", "おもちゃ", "ベルトコンベア", "養殖プール", "ダストシュート", "プレス機"}));
/*---------------------------------------------------------------------------------------*/
			if(selectedIndex > -1)
				s1.setSelectedIndex(selectedIndex);
			s3.setModel(new DefaultComboBoxModel(new String[]{"一時停止", "速度: x1", "速度: x5", "速度: x10", "速度: 最大"}));
			s3.setSelectedIndex(selectedGameSpeed);
			addYukkuriButton.setText("ゆっくりを追加");
			saveButton.setText("セーブ");
			loadButton.setText("ロード");
			languageButton.setText("日本語");
/*---------------------------------------------------------------------------------------*/
			showLogButton.setText("ログ表示");
			nextPageLogButton.setText("次のページ");
			clearLogButton.setText("ログ削除");
/*---------------------------------------------------------------------------------------*/
			l2.setText("ゆっくりの状態");
			l3.setText("　ストレス: - ");
			l4.setText("　ダメージ: - ");
			l5.setText("　空腹度: - ");
			l6.setText("　うんうん: - ");
			l7.setText("　レイパー: - ");
			l8.setText("　ゲス: - ");
		}
		setSubMenu(language);
	}
	
	private void setSubMenu(Body.Language language) {
		if (language == Body.Language.ENGLISH) {
			switch (s1.getSelectedIndex()) {
			case TOOL:
				s2.setModel(new DefaultComboBoxModel(new String[]{"Punish", "Hammer", "Vibrator", "Juice", "Snapping"}));
				s2.setSelectedIndex(selectedTool);
				break;
			case FOOD:
				s2.setModel(new DefaultComboBoxModel(new String[]{"Normal", "Bitter", "Auto-feeder", "Lemon pop", "Hot", "Viyugra"}));
				s2.setSelectedIndex(selectedFood);
				break;
			case CLEAN:
				s2.setModel(new DefaultComboBoxModel(new String[]{"Individual", "All"}));
				s2.setSelectedIndex(selectedCleaner);
				break;
			case BARRIER:
				s2.setModel(new DefaultComboBoxModel(new String[]{"Set", "Delete"}));
				s2.setSelectedIndex(selectedBarrier);
				break;
/*---------------------------------------------------------------------------------------*/
			case BELTCONVEYOR:
				s2.setModel(new DefaultComboBoxModel(new String[]{"back", "thisside", "right", "left"}));
				s2.setSelectedIndex(selectedBeltconveyor);
				break;
/*---------------------------------------------------------------------------------------*/				
			default:
				s2.setModel(new DefaultComboBoxModel(new String[]{" "}));
				s2.setSelectedIndex(0);
				break;
			}
		}
		else if (language == Body.Language.JAPANESE) {
			switch (s1.getSelectedIndex()) {
			case TOOL:
				s2.setModel(new DefaultComboBoxModel(new String[]{"おしおき", "ハンマー", "バイブ", "ジュース", "でこぴん"}));
				s2.setSelectedIndex(selectedTool);
				break;
			case FOOD:
				s2.setModel(new DefaultComboBoxModel(new String[]{"ふつう", "苦い", "自動給餌", "ラムネ", "辛い", "バイゆグラ"}));
				s2.setSelectedIndex(selectedFood);
				break;
			case CLEAN:
				s2.setModel(new DefaultComboBoxModel(new String[]{"個別", "全部"}));
				s2.setSelectedIndex(selectedCleaner);
				break;
			case BARRIER:
				s2.setModel(new DefaultComboBoxModel(new String[]{"設置", "撤去"}));
				s2.setSelectedIndex(selectedBarrier);
				break;
/*---------------------------------------------------------------------------------------*/
			case BELTCONVEYOR:
				s2.setModel(new DefaultComboBoxModel(new String[]{"奥", "手前", "右", "左"}));
				s2.setSelectedIndex(selectedBeltconveyor);
				break;
/*---------------------------------------------------------------------------------------*/
			default:
				s2.setModel(new DefaultComboBoxModel(new String[]{" "}));
				break;
			}
		}
	}
	
	private void showStatus(Body b) {
		int damage = 100 * b.getDamage() / b.getDamageLimit();
		int hungry = 100 * b.getHungry() / b.getHungryLimit();
		int shit = 100 * b.getShit() / b.getShitLimit();
		String rapist = (b.isRaper() ? "Yes" : "No");
		String rude = (b.isRude() ? "Yes" : "No");
		if (Body.getLanguage() == Body.Language.ENGLISH) {
			l4.setText(" Damage: " + damage + "%");
			l5.setText(" Hungry: " + hungry + "%");
			l6.setText(" Shit:   " + shit + "%");
			l7.setText(" Rapist: " + rapist);
			l8.setText(" Rude: " + rude);
		}
		else {
			l4.setText("　ダメージ: " + damage + "%");
			l5.setText("　空腹度: " + hungry + "%");
			l6.setText("　うんうん: " + shit + "%");
			l7.setText("　レイパー: " + rapist);
			l8.setText("　ゲス: " + rude);
		}
	}

	public class MyItemListener implements ItemListener {
		public void itemStateChanged(ItemEvent e) {
		    if (e.getStateChange() == ItemEvent.SELECTED){
		    	if (e.getSource() == s1) {
			    	setSubMenu(Body.getLanguage());
		    	}
		    	else if (e.getSource() == s2) {
		    		switch (s1.getSelectedIndex()) {
		    		case TOOL:
		    			selectedTool = s2.getSelectedIndex();
		    			break;
		    		case FOOD:
		    			selectedFood = s2.getSelectedIndex();
		    			break;
		    		case CLEAN:
		    			selectedCleaner = s2.getSelectedIndex();
		    			break;
		    		}
		    	}
		    	else if (e.getSource() == s3) {
		    		selectedGameSpeed = s3.getSelectedIndex();
		    	}
		    }
		}		
	}
	
	public class MyKeyListener implements KeyListener {
		private int savedGameSpeed;
	
		public void keyPressed(KeyEvent e) {
			switch (e.getKeyCode()) {
			case KeyEvent.VK_ESCAPE:
				synchronized(SimYukkuri.lock) {
					if (selectedGameSpeed != 0) {
						savedGameSpeed = selectedGameSpeed;
						selectedGameSpeed = 0;
					}
					else {
						selectedGameSpeed = savedGameSpeed;
					}
					s3.setSelectedIndex(selectedGameSpeed);
				}
				break;
			case KeyEvent.VK_DELETE:
				synchronized(SimYukkuri.lock) {
					Terrarium.cleanAll();
				}
				break;
			default:
				break;
			}
		}

		public void keyReleased(KeyEvent e) {
		}

		public void keyTyped(KeyEvent e) {
		}
	}

	public class ButtonListener implements ActionListener {
		final private JFileChooser fc = new JFileChooser();

		@Override
		public void actionPerformed(ActionEvent e) {
			synchronized(lock) {
				if (!initialized) {
					return;
				}
			}
			Object source = e.getSource();
			if(source.equals(saveButton)) {
				doSave();
			}
			else if(source.equals(loadButton)) {
				doLoad();
			}
			else if(source.equals(languageButton)) {
				setLanguage(Body.getLanguage() == Body.Language.JAPANESE
						? Body.Language.ENGLISH : Body.Language.JAPANESE);
			}
			else if(source.equals(addYukkuriButton)) {
				mypane.initBodies();
			}			else if(source.equals(showLogButton)) {
				myPane.showLog = !myPane.showLog;
			}
			else if(source.equals(nextPageLogButton)) {
				if ( myPane.showPage > 2 ){
					myPane.showPage = 0;
				}else{
					myPane.showPage++;
				}
			}else if(source.equals(clearLogButton)) {
				Logger.clearLog();
				SimYukkuri.clearLogTime = Terrarium.operationTime;
			}
		}

		public void doSave() {
			synchronized(lock) {
				int result = fc.showSaveDialog(SimYukkuri.this);
				if(result != JFileChooser.APPROVE_OPTION) return;
				File file = fc.getSelectedFile();
				try {
					Terrarium.saveState(file);
				} catch(IOException e) {
					System.out.println(e);
					JOptionPane.showMessageDialog(SimYukkuri.this, e.getLocalizedMessage(), SimYukkuri.TITLE, JOptionPane.ERROR_MESSAGE);
				}
			}
		}

		public void doLoad() {
			synchronized(lock) {
				int result = fc.showOpenDialog(SimYukkuri.this);
				if(result != JFileChooser.APPROVE_OPTION) return;
				File file = fc.getSelectedFile();
				try {
					Terrarium.loadState(file);
				} catch(IOException e) {
					System.out.println(e);
					JOptionPane.showMessageDialog(SimYukkuri.this, e.getLocalizedMessage(), SimYukkuri.TITLE, JOptionPane.ERROR_MESSAGE);
				} catch(ClassNotFoundException e) {
					System.out.println(e);
					JOptionPane.showMessageDialog(SimYukkuri.this, e.getLocalizedMessage(), SimYukkuri.TITLE, JOptionPane.ERROR_MESSAGE);
				}
			}
		}
	}

	public class MyMouseListener extends MouseAdapter {
		private Cursor cr = new Cursor(Cursor.HAND_CURSOR);
		private Cursor defCr = new Cursor(Cursor.DEFAULT_CURSOR);
		private Obj grabbedObj = null;
		private int startY = -1, startZ = -1;
		private int oX = 0, oY = 0, altitude = 0;
		
		private Obj getUpFront(MouseEvent e) {
			// Sort the objects according as Y position.
			List <Obj>list4sort = new ArrayList<Obj>();
			list4sort.addAll(Terrarium.bodyList);
			list4sort.addAll(Terrarium.shitList);
			list4sort.addAll(Terrarium.foodList);
			list4sort.addAll(Terrarium.toyList);
/*---------------------------------------------------------------------------------------*/
			list4sort.addAll(Terrarium.machinePressList);
/*---------------------------------------------------------------------------------------*/
			Collections.sort(list4sort, ObjComp.INSTANCE);
			// Check whether hit or not.
			Dimension size = mypane.getSize();
			int w = size.width, h = size.height;
			Obj found = null;
			for (Obj o : list4sort) {
				switch (o.objType) {
				case YUKKURI:
				{
					Body b = (Body)o;
					if (b.getZ() != 0) {
						continue;
					}
					int offsetX = (Body.MAXSIZE - b.getSize())/2;
					int offsetY = (Body.MAXSIZE - b.getSize());
					int dx = e.getX() - (Translate.transX(b.getX(), b.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(b.getX(), b.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= b.getSize() && dy >= 0 && dy <= b.getSize()) {
						found = b;
						oX = dx;
						oY = dy;
					}
					break;
				}
				case SHIT:
				{
					Shit s = (Shit)o;
					if (s.getZ() != 0) {
						continue;
					}
					int offsetX = (Body.MAXSIZE - s.getSize())/2;
					int offsetY = (Body.MAXSIZE - s.getSize());
					int dx = e.getX() - (Translate.transX(s.getX(), s.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(s.getX(), s.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= s.getSize() && dy >= 0 && dy <= s.getSize()) {
						found = s;
						oX = dx;
						oY = dy;
					}
					break;
				}
				case FOOD:
				{
					Food f = (Food)o;
					if (f.getZ() != 0) {
						continue;
					}
					int offsetX = (Body.MAXSIZE - f.getSize())/2;
					int offsetY = (Body.MAXSIZE - f.getSize()/2);
					int dx = e.getX() - (Translate.transX(f.getX(), f.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(f.getX(), f.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= f.getSize() && dy >= 0 && dy <= f.getSize()/2) {
						found = f;
						oX = dx;
						oY = dy;
					}
					break;
				}
				case TOY:
				{
					Toy t = (Toy)o;
					if (t.getZ() != 0) {
						continue;
					}
					int offsetX = (Body.MAXSIZE - t.getSize())/2;
					int offsetY = (Body.MAXSIZE - t.getSize());
					int dx = e.getX() - (Translate.transX(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= t.getSize() && dy >= 0 && dy <= t.getSize()) {
						found = t;
						oX = dx;
						oY = dy;
					}					
					break;
				}
/*---------------------------------------------------------------------------------------*/
				case PLATFORM:
				{
					MachinePress mp = (MachinePress)o;
					if (mp.getZ() != 0) {
						continue;
					}
					int offsetX = (Body.MAXSIZE - mp.getSize())/2;
					int offsetY = (Body.MAXSIZE - mp.getSize()/2)-128;
					int dx = e.getX() - (Translate.transX(mp.getX(), mp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(mp.getX(), mp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= mp.getSize() && dy >= 0 && dy <= mp.getSize()) {
						found = mp;
						oX = dx;
						oY = dy;
					}					
					break;
				}
/*---------------------------------------------------------------------------------------*/
				default:
					break;
				}
			}
			if (found == null) { // Toilet has lower priority.
				for (Toilet t:Terrarium.toiletList) {
					int offsetX = (Body.MAXSIZE - t.getSize())/2;
					int offsetY = (Body.MAXSIZE - t.getSize()*2/3);
					int dx = e.getX() - (Translate.transX(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= t.getSize() && dy >= 0 && dy <= t.getSize()*2/3) {
						found = t;
						oX = dx;
						oY = dy;
					}
				}
			}
/*---------------------------------------------------------------------------------------*/
			if (found == null) {// belt has lowest priority.
				for (Beltconveyor bc:Terrarium.beltconveyorList) {
					int offsetX = (Body.MAXSIZE - bc.getSize())/2;
					int offsetY = (Body.MAXSIZE - bc.getSize()/2);
					int dx = e.getX() - (Translate.transX(bc.getX(), bc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(bc.getX(), bc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= bc.getSize()*9/4/2 && dy >= 0 && dy <= bc.getSize()*3/2/2) {
						found = bc;
						oX = dx;
						oY = dy;
					}
				}
			}
			
			if (found == null) {// pool has lowest priority.
				for (BreedingPool bp:Terrarium.breedingPoolList) {
					int offsetX = (Body.MAXSIZE - bp.getSize())/2;
					int offsetY = (Body.MAXSIZE - bp.getSize()/2);
					int dx = e.getX() - (Translate.transX(bp.getX(), bp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(bp.getX(), bp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= bp.getSize()*9/4/2 && dy >= 0 && dy <= bp.getSize()*3/2/2) {
						found = bp;
						oX = dx;
						oY = dy;
					}
				}
			}
			
			if (found == null) {// pool has lowest priority.
				for (GarbageChute gc:Terrarium.garbageChuteList) {
					int offsetX = (Body.MAXSIZE - gc.getSize())/2;
					int offsetY = (Body.MAXSIZE - gc.getSize()/2);
					int dx = e.getX() - (Translate.transX(gc.getX(), gc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX);
					int dy = e.getY() - (Translate.transY(gc.getX(), gc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY);
					if (dx >= 0 && dx <= gc.getSize()*9/4/2 && dy >= 0 && dy <= gc.getSize()*3/2/2) {
						found = gc;
						oX = dx;
						oY = dy;
					}
				}
			}
/*---------------------------------------------------------------------------------------*/
			return found;
		}

		public void mouseClicked(MouseEvent e){
			if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) == 0) {
				return;
			}
			synchronized(lock) {
				if ((s1.getSelectedIndex() == BARRIER) && (s2.getSelectedIndex() == DEL)) {
					Dimension size = mypane.getSize();
					int w = size.width, h = size.height;
					int X = (e.getX() - Body.MAXSIZE/2)*Terrarium.MAX_X/(w-Body.MAXSIZE);
					int Y = (e.getY() - Body.MAXSIZE)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
					int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
					int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
					Barrier found = Terrarium.getBarrier(x, y, 10);
					if (found != null) {
						Terrarium.clearBarrier(found);
					}
					return;
				}
				// If all is selected for clean, clean all shit, empty food and dead body.
				if ((s1.getSelectedIndex() == CLEAN) && (s2.getSelectedIndex() == ALL)) {
					Terrarium.cleanAll();
					return;
				}
				// get up-front object
				Obj found = getUpFront(e);
				// If found, apply action to it.
				if (found != null) {
					switch (s1.getSelectedIndex()) {
					case TOOL:
						switch (s2.getSelectedIndex()) {
						case PUNISH:
							if (found instanceof Body) { 
								((Body)found).strikeByPunish();
								Terrarium.setAlarm();
							}
							break;
						case HAMMER:
							if (found instanceof Body) { 
								Body b = (Body)found;
								b.strikeByHammer();
								if (!b.hasPants() && !b.isDead()) {
									mypane.terrarium.addCrushedShit(b.getX(), b.getY(), b.getZ(), b.getAgeState());
								}
								b.setDirty(true);
								Terrarium.setAlarm();
							}
							break;
						case VIBRATOR:
							if (found instanceof Body) { 
								((Body)found).forceToSukkiri();
							}
							break;
						case JUICE:
							if (found instanceof Body){
								((Body)found).giveJuice();
							}
							break;
						case DECOPING:
							found.kick();
							break;
						default:
							break;
						}
						break;
					case CLEAN:
						if (found instanceof Body) {
							if (((Body)found).isDead())
								found.remove();
							else
								((Body)found).setDirty(false);
						}
						else {
							found.remove();
						}
						break;
					case ACCESSORY:
						if (found instanceof Body) { 
							if (((Body)found).hasAccessory())
								((Body)found).takeAccessory();
							else
								((Body)found).giveAccessory();
						}
						break;
					case PANTS:
						if (found instanceof Body) { 
							if (((Body)found).hasPants())
								((Body)found).takePants();
							else
								((Body)found).givePants();
						}
						break;
					default://Other tool
						break;
					}
					if (found instanceof Body) {
						showStatus((Body)found);
					}
				}
				if (found == null) {
					Dimension size = mypane.getSize();
					int w = size.width, h = size.height;
					switch (s1.getSelectedIndex()) {
					case FOOD: {
						int offsetX = (Body.MAXSIZE - Food.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - Food.getSizeS()/2);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							Food.type foodType;
							switch (s2.getSelectedIndex()) {
							case NORMAL:
							default:
								foodType = Food.type.YUKKURIFOOD;
								break;
							case SELFFEEDER:
								foodType = Food.type.SELFFEEDER;
								break;
							case BITTER:
								foodType = Food.type.BITTER;
								break;
							case LEMONPOP:
								foodType = Food.type.LEMONPOP;
								break;
							case HOT:
								foodType = Food.type.HOT;
								break;
							case VIYUGRA:
								foodType = Food.type.VIYUGRA;
								break;
							}
							mypane.terrarium.addFood(x, y, foodType);
						}
					}
					break;
					case TOILET: {
						int offsetX = (Body.MAXSIZE - Toilet.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - Toilet.getSizeS()*2/3);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							mypane.terrarium.addToilet(x, y);
						}
					}
					break;
					case TOYS: {
						int offsetX = (Body.MAXSIZE - Toy.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - Toy.getSizeS());
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							mypane.terrarium.addToy(x, y);
						}						
					}
					break;
/*---------------------------------------------------------------------------------------*/
					case BELTCONVEYOR: {
						int offsetX = (Body.MAXSIZE - Beltconveyor.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - Beltconveyor.getSizeS()/2);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							Platform.Direction dir;
							switch (s2.getSelectedIndex()) {
								case BACK:
								default:
									dir = Platform.Direction.BACK;
									break;
								case THISSIDE:
									dir = Platform.Direction.THISSIDE;
									break;
								case RIGHT:
									dir = Platform.Direction.RIGHT;
									break;
								case LEFT:
									dir = Platform.Direction.LEFT;
									break;
							}
							mypane.terrarium.addBeltconveyor(x, y, dir);
						}
					}
					break;
					
					case BREEDINGPOOL: {
						int offsetX = (Body.MAXSIZE - BreedingPool.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - BreedingPool.getSizeS()/2);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							mypane.terrarium.addBreedingPool(x, y, Platform.Direction.BACK);
						}
					}
					break;
					
					case GARBAGECHUTE: {
						int offsetX = (Body.MAXSIZE - GarbageChute.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - GarbageChute.getSizeS()/2);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							mypane.terrarium.addGarbageChute(x, y, Platform.Direction.BACK);
						}
					}
					break;

					case MACHINEPRESS: {
						int offsetX = (Body.MAXSIZE - MachinePress.getSizeS())/2;
						int offsetY = (Body.MAXSIZE - MachinePress.getSizeS()/2);
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (x >= 0 && x <= Terrarium.MAX_X && y >= 0 && y <= Terrarium.MAX_Y) {
							mypane.terrarium.addMachinePress(x, y, Platform.Direction.BACK);
						}
					}
					break;
/*---------------------------------------------------------------------------------------*/
					default://Other tool
						break;
					}
				}
			}
		}

		public void mousePressed(MouseEvent e) {
			synchronized(lock) {
				if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
					if ((s1.getSelectedIndex() == BARRIER) && (s2.getSelectedIndex() == SET)) {
						Dimension size = mypane.getSize();
						int w = size.width, h = size.height;
						int X = (e.getX() - Body.MAXSIZE/2)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - Body.MAXSIZE)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						barrierSX = Math.max(0, Math.min(x, Terrarium.MAX_X));
						barrierSY = Math.max(0, Math.min(y, Terrarium.MAX_Y));
						barrierEX = barrierSX;
						barrierEY = barrierSY;
						return;
					}
					if ((s1.getSelectedIndex() != PICKUP)) {
						return;
					}
				}
				if (grabbedObj != null) {
					return;
				}
				// get up-front yukkuri
				grabbedObj = getUpFront(e);
				if (grabbedObj != null) {
					// yukkuri has been grabbed.
					startY = e.getY();
					startZ = e.getY();
					grabbedObj.grab();
					if (grabbedObj instanceof Body) {
						showStatus((Body)grabbedObj);
					}
				}
			}
		}

		public void mouseReleased(MouseEvent e) {
			synchronized(lock) {
				if ((e.getModifiers() & (MouseEvent.BUTTON1_MASK|MouseEvent.BUTTON3_MASK)) == 0) {
					return;
				}
				if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
					if ((s1.getSelectedIndex() == BARRIER) && (s2.getSelectedIndex() == SET)) {
						Dimension size = mypane.getSize();
						int w = size.width, h = size.height;
						int X = (e.getX() - Body.MAXSIZE/2)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - Body.MAXSIZE)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						barrierEX = Math.max(0, Math.min(x, Terrarium.MAX_X));
						barrierEY = Math.max(0, Math.min(y, Terrarium.MAX_Y));
						if ((barrierSX != barrierEX) || (barrierSY != barrierEY)) {
							Terrarium.setBarrier(barrierSX, barrierSY, barrierEX, barrierEY);
						}
						barrierSX = -1;
						barrierSY = -1;
						barrierEX = -1;
						barrierEY = -1;
						return;
					}
				}
				if (grabbedObj != null) {
					grabbedObj.release();
					grabbedObj = null;
					startY = -1;
					startZ = -1;
					altitude = 0;
				}
			}
		}

		public void mouseDragged(MouseEvent e) {
			synchronized(lock) {
				if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
					if ((s1.getSelectedIndex() == BARRIER) && (s2.getSelectedIndex() == SET)) {
						Dimension size = mypane.getSize();
						int w = size.width, h = size.height;
						int X = (e.getX() - Body.MAXSIZE/2)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - Body.MAXSIZE)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						barrierEX = Math.max(0, Math.min(x, Terrarium.MAX_X));
						barrierEY = Math.max(0, Math.min(y, Terrarium.MAX_Y));
						return;
					}
				}
				if (grabbedObj != null) {
					int button = 1;
					if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
						button = 1;
					}
					else if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
						button = 2;
					}
					else {
						return;
					}
					Dimension size = mypane.getSize();
					int w = size.width, h = size.height;
					int offsetX = 0;
					int offsetY = 0;

					switch (grabbedObj.objType) {
					case YUKKURI: {
						Body b = (Body)grabbedObj;
						offsetX = (Body.MAXSIZE - b.getSize())/2;
						offsetY = (Body.MAXSIZE - b.getSize());
						break;
					}
					case SHIT: {
						Shit s = (Shit)grabbedObj;
						offsetX = (Body.MAXSIZE - s.getSize())/2;
						offsetY = (Body.MAXSIZE - s.getSize());
						break;
					}
					case FOOD: {
						Food f = (Food)grabbedObj;
						offsetX = (Body.MAXSIZE - f.getSize())/2;
						offsetY = (Body.MAXSIZE - f.getSize()/2);
						break;
					}
					case TOILET: {
						Toilet t = (Toilet)grabbedObj;
						offsetX = (Body.MAXSIZE - t.getSize())/2;
						offsetY = (Body.MAXSIZE - t.getSize()*2/3);
						break;
					}
					case TOY: {
						Toy t = (Toy)grabbedObj;
						offsetX = (Body.MAXSIZE - t.getSize())/2;
						offsetY = (Body.MAXSIZE - t.getSize());
						break;
					}
/*---------------------------------------------------------------------------------------*/
					case PLATFORM:
						Platform p = (Platform)grabbedObj;
						offsetX = (Body.MAXSIZE - p.getSize())/2;
						offsetY = (Body.MAXSIZE - p.getSize()/2)-128;
						break;
/*---------------------------------------------------------------------------------------*/
					default:
						break;
					}

					if ((button == 1) && (s1.getSelectedIndex() == PICKUP)) {
						int X = (e.getX() - oX - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (startY	- oY - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						grabbedObj.setX(x);
						if (startZ - e.getY() > 0) {
							altitude = startZ - e.getY();
							grabbedObj.setZ((startZ - e.getY())*Terrarium.MAX_Z/h);
						}
					}
					else if (button == 2) {
						int X = (e.getX() - oX - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - oY - offsetY + altitude)*Terrarium.MAX_Y/(h-Body.MAXSIZE);				
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						grabbedObj.setX(x);
						grabbedObj.setY(y);					
					}
				}
			}
		}

		public void mouseMoved(MouseEvent e) {
			synchronized(lock) {
				if ((s1.getSelectedIndex() == TOOL) && (s2.getSelectedIndex() == HAMMER)) {
					if (Terrarium.getAlarm() == false) {
						return;
					}
					Dimension size = mypane.getSize();
					int w = size.width, h = size.height;
					for (Body b:Terrarium.bodyList) {
						int offsetX = (Body.MAXSIZE - b.getSize())/2;
						int offsetY = (Body.MAXSIZE - b.getSize());
						int X = (e.getX() - offsetX)*Terrarium.MAX_X/(w-Body.MAXSIZE);
						int Y = (e.getY() - offsetY)*Terrarium.MAX_Y/(h-Body.MAXSIZE);
						int x = Translate.invX(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						int y = Translate.invY(X, Y, Terrarium.MAX_X, Terrarium.MAX_Y);
						if (b.isAdult()) {
							b.setAngry();
							b.lookTo(x, y);
						}
						else {
							b.runAway(x, y);
						}
						if (b.isAngry()) {
							b.showAlarm();
						}
						else if (b.isScare()) {
							b.showScare();
						}
					}
				}
			}
		}

		public void mouseEntered(MouseEvent e) {
			setCursor(cr);
		}

		public void mouseExited(MouseEvent e) {
			setCursor(defCr);
		}
	}
}

class myPane extends JPanel implements Runnable {
	static final long serialVersionUID = 4L;
	public boolean isRunning = false;
	public Terrarium terrarium = new Terrarium();
	private Image backGroundImage;
/*---------------------------------------------------------------------------------------*/
	static boolean showLog = false;
	static int showPage = 0;
/*---------------------------------------------------------------------------------------*/
	static final Random rnd = new Random();
	static final int PAUSE = -1, MAX = 1, DECUPLE = 10, QUINTUPLE = 20, NORMAL = 100;
	static final int gameSpeed[]={PAUSE, NORMAL, QUINTUPLE, DECUPLE, MAX};

	public void run() {
		//load images
		try {
			ClassLoader loader = this.getClass().getClassLoader();
			// load common images
			backGroundImage = ImageIO.read(loader.getResourceAsStream("images/back.jpg"));
			Food.loadImages(loader);
			Shit.loadImages(loader);
			Toilet.loadImages(loader);
			Toy.loadImages(loader);
/*---------------------------------------------------------------------------------------*/
			Beltconveyor.loadImages(loader);
			BreedingPool.loadImages(loader);
			GarbageChute.loadImages(loader);
			MachinePress.loadImages(loader);
/*---------------------------------------------------------------------------------------*/
			// load body images
			Marisa.loadImages(loader);
			Reimu.loadImages(loader);
			WasaReimu.loadImages(loader);
			MarisaReimu.loadImages(loader);
			ReimuMarisa.loadImages(loader);
			Alice.loadImages(loader);
			Tarinai.loadImages(loader);
			Body.loadShadowImages(loader);
		} catch (IOException e1) {
			System.out.println("File I/O error");
		}
		// make initial bodies
		initBodies();
		synchronized(SimYukkuri.lock) {
			SimYukkuri.initialized = true;
		}
		// run animation
		while (isRunning) {
			int stress, speed;
			Body.Language lang;
			synchronized(SimYukkuri.lock) {
				stress = 100 * Terrarium.bodyList.size() / Body.getHeadageLimit();
				speed = gameSpeed[SimYukkuri.selectedGameSpeed];
				lang = Body.getLanguage();
			}
			if (lang == Body.Language.ENGLISH) {
				SimYukkuri.l3.setText(" Stress: " + stress + "%");
			}
			else {
				SimYukkuri.l3.setText("　ストレス: " + stress + "%");
			}
			if(speed != PAUSE) {
				synchronized(SimYukkuri.lock) {
					terrarium.run();
				}
/*---------------------------------------------------------------------------------------*/
				if( Terrarium.operationTime % 10 == 0 ){
					Logger.run();
				}
/*---------------------------------------------------------------------------------------*/
			}
			repaint();
			try {
				if (speed >= 0) {
					Thread.sleep(speed);
				}
				else {
					Thread.sleep(NORMAL);
				}
			} catch (InterruptedException e2) {
				e2.printStackTrace();
			}
		}
	}

	void initBodies ()
	{
		final int addYukkuriLimit = 8 - Terrarium.bodyList.size();
		if(addYukkuriLimit < 1) {
			String msg;
			if(Body.getLanguage() == Body.Language.ENGLISH)
				msg = "You cannot add more yukkuri right now.  If you want more, try breeding some.";
			else
				msg = "追加出来る上限を超えてます。すっきりさせて増やしてください。";
			JOptionPane.showMessageDialog(this, msg, SimYukkuri.TITLE, JOptionPane.INFORMATION_MESSAGE);
			return;
		}

		ArrayList<Body> bodies = new ArrayList<Body>();
		String[] names;
		String[] options;
		String[] ages;
		String mess1, mess2, mess3, mess4;
		final int BABY = 0, CHILD = 1, ADULT = 2;
		switch (Body.getLanguage()) {
		case JAPANESE:
		{
			String[] tempNames = {Marisa.nameJ, Reimu.nameJ, Alice.nameJ};
			names = tempNames;
			String[] tempAges = {"赤ちゃん", "子供", "大人"};
			ages = tempAges;
			String[] tempo = {"はい","いいえ"};
			options = tempo;
			mess1 = "どのゆっくりを追加しますか？";
			mess2 = "もっと追加しますか？";
			mess3 = "（あと";
			mess4 = "匹追加出来ます）";
			break;
		}
		case ENGLISH:
		{
			String[] tempNames = {Marisa.nameE, Reimu.nameE, Alice.nameE};
			names = tempNames;
			String[] tempAges = {"Baby", "Child", "Adult"};
			ages = tempAges;
			String[] tempo = {"Yes","No"};
			options = tempo;
			mess1 = "What kind of yukkuri?";
			mess2 = "More yukkuri?";
			mess3 = "(";
			mess4 = " yukkuris remain)";
			break;
		}
		default:
			throw new LanguageException();
		}
		for (int choice = 0, i = addYukkuriLimit - 1; choice == 0; i--) {
			JPanel panel = new JPanel();
			JPanel panel2 = new JPanel();
			JComboBox cb1 = new JComboBox(names);
			cb1.setSelectedIndex(0);
			panel2.add(cb1);
			JComboBox cb2 = new JComboBox(ages);
			cb2.setSelectedIndex(2);
			panel2.add(cb2);
			JLabel label = new JLabel(mess1);
			panel.add(label);
			panel.add(panel2);
			panel.setLayout(new GridLayout(2,1));
			int ret = JOptionPane.showConfirmDialog(this, panel, SimYukkuri.TITLE, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
			if (ret == 2) {
				break;
			}
			int type = cb1.getSelectedIndex();
			if(type == Reimu.type && rnd.nextBoolean())
				type = WasaReimu.type;
			Body.AgeState age;
			switch (cb2.getSelectedIndex()) {
			case BABY:
				age = Body.AgeState.BABY;
				break;
			case CHILD:
				age = Body.AgeState.CHILD;
				break;
			case ADULT:
			default:
				age = Body.AgeState.ADULT;
				break;
			}
			bodies.add(terrarium.makeBody(rnd.nextInt(Terrarium.MAX_X), rnd.nextInt(Terrarium.MAX_Y), 0, type, age, null, null));
			choice = 1;
			if (i != 0) { // At most 8 yukkuri can be added.
				choice = JOptionPane.showOptionDialog(this, mess2 + System.getProperty("line.separator") + mess3 + i + mess4, SimYukkuri.TITLE, JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
			}
		}
		for (Body b: bodies) {
			synchronized(SimYukkuri.lock) {
				terrarium.addBody(b);
			}
		}
	}

	public void paint(Graphics g) {
		synchronized(SimYukkuri.lock) {
			List <Obj>list4sort = new ArrayList<Obj>();
			list4sort.addAll(Terrarium.bodyList);
			list4sort.addAll(Terrarium.foodList);
			list4sort.addAll(Terrarium.shitList);
			list4sort.addAll(Terrarium.toyList);
			list4sort.addAll(Terrarium.machinePressList);
			Collections.sort(list4sort, ObjComp.INSTANCE);
			Dimension size = getSize();
			int w = size.width, h = size.height;
			// draw background and toilet
			g.drawImage(backGroundImage,  0, 0, w, h, this);
/*---------------------------------------------------------------------------------------*/
			for (Obj o: Terrarium.beltconveyorList) {
				Beltconveyor bc = (Beltconveyor)o;
				int offsetX = (Body.MAXSIZE - bc.getSize())/2;
				int offsetY = (Body.MAXSIZE - bc.getSize()/2);
				int drX = Translate.transX(bc.getX(), bc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
				int drY = Translate.transY(bc.getX(), bc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
				g.drawImage(bc.getImage(), drX, drY, this);				
			}
			
			for (Obj o: Terrarium.breedingPoolList) {
				BreedingPool bp = (BreedingPool)o;
				int offsetX = (Body.MAXSIZE - bp.getSize())/2;
				int offsetY = (Body.MAXSIZE - bp.getSize()/2);
				int drX = Translate.transX(bp.getX(), bp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
				int drY = Translate.transY(bp.getX(), bp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
				g.drawImage(bp.getImage(), drX, drY, this);				
			}
			
			for (Obj o: Terrarium.garbageChuteList) {
				GarbageChute gc = (GarbageChute)o;
				int offsetX = (Body.MAXSIZE - gc.getSize())/2;
				int offsetY = (Body.MAXSIZE - gc.getSize()/2);
				int drX = Translate.transX(gc.getX(), gc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
				int drY = Translate.transY(gc.getX(), gc.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
				g.drawImage(gc.getImage(), drX, drY, this);				
			}

/*---------------------------------------------------------------------------------------*/
			for (Obj o: Terrarium.toiletList) {
				Toilet t = (Toilet)o;
				int offsetX = (Body.MAXSIZE - t.getSize())/2;
				int offsetY = (Body.MAXSIZE - t.getSize()*2/3);
				int drX = Translate.transX(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
				int drY = Translate.transY(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
				g.drawImage(t.getImage(), drX, drY, this);				
			}
			// draw barriers
			for (Barrier b: Terrarium.getBarriers()) {
				int SX = Translate.transX(b.getSX(), b.getSY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + Body.MAXSIZE/2;
				int SY = Translate.transY(b.getSX(), b.getSY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + Body.MAXSIZE;
				int EX = Translate.transX(b.getEX(), b.getEY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + Body.MAXSIZE/2;
				int EY = Translate.transY(b.getEX(), b.getEY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + Body.MAXSIZE;
				Graphics2D g2 = (Graphics2D)g;
				BasicStroke BStroke = new BasicStroke(6.0f);
				Stroke DefStroke = g2.getStroke();
				g2.setStroke(BStroke);
				g2.setColor(Color.GRAY);
				g2.drawLine(SX, SY, EX, EY);
				BStroke = new BasicStroke(1.0f);
				g2.setStroke(DefStroke);
			}
			if ((SimYukkuri.barrierSX >= 0) && (SimYukkuri.barrierSY >= 0) && (SimYukkuri.barrierEX >= 0) && (SimYukkuri.barrierEY >= 0)) {
				int SX = Translate.transX(SimYukkuri.barrierSX, SimYukkuri.barrierSY, Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + Body.MAXSIZE/2;
				int SY = Translate.transY(SimYukkuri.barrierSX, SimYukkuri.barrierSY, Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + Body.MAXSIZE;
				int EX = Translate.transX(SimYukkuri.barrierEX, SimYukkuri.barrierEY, Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + Body.MAXSIZE/2;
				int EY = Translate.transY(SimYukkuri.barrierEX, SimYukkuri.barrierEY, Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + Body.MAXSIZE;
				Graphics2D g2 = (Graphics2D)g;
				BasicStroke BStroke = new BasicStroke(6.0f);
				Stroke DefStroke = g2.getStroke();
				g2.setStroke(BStroke);
				g2.setColor(Color.WHITE);
				g2.drawLine(SX, SY, EX, EY);
				BStroke = new BasicStroke(1.0f);
				g2.setStroke(DefStroke);
			}
			// draw yukkuri, food and shit
			for (Obj o: list4sort) {
				switch (o.objType) {
				case YUKKURI:
				{
					Body b = (Body)o;
					Body.AgeState ageState = b.getAgeState();
					int offsetX = (Body.MAXSIZE - b.getSize())/2;
					int offsetY = (Body.MAXSIZE - b.getSize());
					int drX = Translate.transX(b.getX(), b.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
					int drY = Translate.transY(b.getX(), b.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
					int jump[] = {0, 8, 12, 14, 15, 14, 12, 8, 0};
					int jumpLevel[] = {2, 2, 1};

					// draw shadow
					g.drawImage(b.getShadowImage(), drX, drY, this);
					// draw body
					drY -= b.getZ()*h/Terrarium.MAX_Z; // considering z axis
					int direction = b.getDirection().ordinal();
					if ((!b.hasPants() && b.isShitting()) || b.isBirth()) {
						g.drawImage(b.getImage(Body.SHIT,Body.LEFT), drX, drY, this);
						if (b.hasPants()) {
							g.drawImage(b.getImage(Body.PANTS2,Body.LEFT), drX, drY, this);
						}
						if (b.hasAccessory()) {
							g.drawImage(b.getImage(Body.ROLL_ACCESSORY,Body.LEFT), drX, drY, this);							
						}
					}
					else if (b.isFurifuri()) {
						int dir = Body.LEFT;
						if (b.getAge() % 8 <= 3) {
							dir = Body.LEFT;
						}
						else if (b.getAge() % 8 <= 7) {
							dir = Body.RIGHT;
						}
						g.drawImage(b.getImage(Body.ROLL_SHIT,dir), drX, drY, this);
						if (b.hasPants()) {
							g.drawImage(b.getImage(Body.PANTS2_ROLL,dir), drX, drY, this);
						}
						if (b.hasAccessory()) {
							g.drawImage(b.getImage(Body.ROLL_ACCESSORY,Body.LEFT), drX, drY, this);							
						}
					}
					else if (b.isCrashed()) {
						if (b.hasAccessory()) {
							g.drawImage(b.getImage(Body.CRUSHED,Body.LEFT), drX, drY, this);
						}
						else {
							g.drawImage(b.getImage(Body.CRUSHED2,Body.LEFT), drX, drY, this);							
						}
					}
					else {
						// Selecting the face
						Image img;
						if (b.isDead()) {
							img = b.getImage(Body.DEAD,direction);
						}
						else if (b.isExciting()) {
							img = b.getImage(Body.EXCITING,direction);
							if (!b.isGrabbed() && b.getZ() == 0) {
								drY -= jump[(int)b.getAge() % 9]/jumpLevel[ageState.ordinal()];
							}
						}
						else if (b.isSleeping()) {
							img = b.getImage(Body.SLEEPING,direction);
						}
						else if (b.isPeroPero() || b.isEating()) {
							if (b.isStrike() || b.isVerySad())
								img = b.getImage(Body.CRYING,direction);
							else if (b.isSad() || b.isEatingShit())
								img = b.getImage(Body.TIRED, direction);
							else
								img = b.getImage(Body.SMILE, direction);
						}
						else if (b.isSukkiri()) {
							img = b.getImage(Body.REFRESHED,direction);
						}
						else if (b.isDamaged() || b.isSick()) {
							if (b.isStrike() || b.isVerySad())
								img = b.getImage(Body.CRYING,direction);
							else
								img = b.getImage(Body.TIRED,direction);
						}
						else {
							if (b.getZ() != 0)
								img = b.getImage(Body.CHEER,direction);
							else if (b.isStrike() || b.isVerySad())
								img = b.getImage(Body.CRYING,direction);
							else if (b.isAngry())
								img = b.getImage(Body.PUFF,direction);
							else if (b.isSad() || b.isOld())
								img = b.getImage(Body.TIRED,direction);
							else if (b.isHappy())
								img = b.getImage(Body.SMILE, direction);
							else if (b.isTalking() && b.isRude())
								img = b.getImage(Body.RUDE,direction);
							else if (b.isTalking() && !b.isRude())
								img = b.getImage(Body.CHEER,direction);
							else
								img = b.getImage(Body.NORMAL,direction);
							if (!b.isGrabbed() && b.getZ() == 0)
								drY -= jump[(int)b.getAge() % 9]/2/jumpLevel[ageState.ordinal()];
						}
						// Draw body
						g.drawImage(b.getImage(Body.BODY,direction), drX, drY, this);
						// Draw accessory
						if (b.hasAccessory()) {
							g.drawImage(b.getImage(Body.ACCESSORY,direction), drX, drY, this);
						}
						// Draw face
						g.drawImage(img,  drX, drY,  this);
						if (b.isDamaged() || b.isOld()) {
							g.drawImage(b.getImage(Body.DAMAGED,direction), drX, drY, this);
						}
						// Draw pants and stain
						if (b.hasPants()) {
							g.drawImage(b.getImage(Body.PANTS,direction), drX, drY, this);
						}
						if (b.isDirty()) {
							g.drawImage(b.getImage(Body.STAIN,direction), drX, drY, this);
						}
						if (b.isSick()) {
							g.drawImage(b.getImage(Body.SICK,direction), drX, drY, this);
						}
						// Draw braid
						g.drawImage(b.getImage(Body.BRAID,direction), drX, drY, this);
						// Draw tongue
						if (b.isPeroPero() || b.isEating() || b.isEatingShit()) {
							if (b.getMessage() != null) {
								g.drawImage(b.getImage(Body.LICK,direction), drX, drY, this);
							}
						}
					}
					// draw script
					String message = b.getMessage();
					if (message != null)
					{	
						final int FONTWIDTH=12;
						final int NUMOFCHAR=12;
						g.setFont(new Font(Font.MONOSPACED, Font.PLAIN, FONTWIDTH));
						int width = Math.min(message.length(), NUMOFCHAR)*FONTWIDTH;
						int hight = drawStringMultiLine(g, message, 0, 0, width, false);
						g.setColor(new Color(255, 255, 255, 200)); // transparent white.
						g.fillRoundRect(drX+14, drY-hight-4, width+8, hight+8, 8, 8);
						g.setColor(new Color(0, 0, 0, 255)); // no transparent black.
						g.drawRoundRect(drX+14, drY-hight-4, width+8, hight+8, 8, 8);
						drawStringMultiLine(g, message, drX+18, drY-hight, width, true);
					}
				}
				break;
				case FOOD: {
					Food f = (Food)o;
					int offsetX = (Body.MAXSIZE - f.getSize())/2;
					int offsetY = (Body.MAXSIZE - f.getSize()/2);
					int drX = Translate.transX(f.getX(), f.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
					int drY = Translate.transY(f.getX(), f.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
					g.drawImage(f.getShadowImage(), drX, drY, this);
					drY -= f.getZ()*h/Terrarium.MAX_Z; // considering z axis
					g.drawImage(f.getImage(), drX, drY, this);				
				}
				break;
				case SHIT: {
					Shit s = (Shit)o;
					int offsetX = (Body.MAXSIZE - s.getSize())/2;
					int offsetY = (Body.MAXSIZE - s.getSize());
					int drX = Translate.transX(s.getX(), s.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
					int drY = Translate.transY(s.getX(), s.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
					g.drawImage(s.getShadowImage(), drX, drY, this);
					drY -= s.getZ()*h/Terrarium.MAX_Z; // considering z axis
					g.drawImage(s.getImage(), drX, drY, this);
				}
				break;
				case TOY: {
					Toy t = (Toy)o;
					int offsetX = (Body.MAXSIZE - t.getSize())/2;
					int offsetY = (Body.MAXSIZE - t.getSize());
					int drX = Translate.transX(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
					int drY = Translate.transY(t.getX(), t.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
					g.drawImage(t.getShadowImage(), drX, drY, this);
					drY -= t.getZ()*h/Terrarium.MAX_Z; // considering z axis
					g.drawImage(t.getImage(), drX, drY, this);					
				}
				break;
				case PLATFORM: {
					MachinePress mp = (MachinePress)o;
					int offsetX = (Body.MAXSIZE - mp.getSize())/2;
					int offsetY = (Body.MAXSIZE - mp.getSize());
					int drX = Translate.transX(mp.getX(), mp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(w-Body.MAXSIZE)/Terrarium.MAX_X + offsetX;
					int drY = Translate.transY(mp.getX(), mp.getY(), Terrarium.MAX_X, Terrarium.MAX_Y)*(h-Body.MAXSIZE)/Terrarium.MAX_Y + offsetY;
					g.drawImage(mp.getImage(), drX, drY, this);				
				}
				default:
					break;
				}
			}
			
/*---------------------------------------------------------------------------------------*/
			if ( myPane.showLog ){
				Color backColor = new Color( 0, 0, 0, 160 );
				Color textColor1 = new Color( 255, 255, 255, 255 );
				Font textFontTitle = new Font( "Dialog", Font.BOLD, 40);
				Font textFonttext = new Font( "Dialog", Font.PLAIN, 20);
				g.setColor( backColor );
				g.fillRect( 0, 0, 900, 600 );

				g.setColor( textColor1 );
				g.setFont( textFontTitle );
				int numOfLogData = Logger.getNumOfLogData();
				int NUM_OF_GRAPH_DATA = 120;
				int GRAPH_WIDTH = 700;
				int GRAPH_HEIGHT = 200;
				int GRAPH_OFFSETX = 100;
				int GRAPH_OFFSETY = 300;
				int LEGEND_OFFSETX = 120;
	
				int[] logData = new int[Logger.NUM_OF_LOGDATA_TYPE * NUM_OF_GRAPH_DATA];
				int[] numOfObjNowLog = Logger.getNumOfObjNowLog();
				int numOfMaxYukkuri = 0;
				int numOfMaxUnun = 0;
				int[] logDataTmp;
				int operationTime = Terrarium.operationTime / 10;

				for( int i = 0 ; i < NUM_OF_GRAPH_DATA ; i++ ){ //グラフの最大値を求めるため描写する一定時間のデータ処理
					logDataTmp = Logger.getLog( ( Terrarium.operationTime - (SimYukkuri.clearLogTime - ( SimYukkuri.clearLogTime % 10 ))  ) / 10 - NUM_OF_GRAPH_DATA + i);

					if ( logDataTmp != null ){
						int numOfSumYukkuri = 0;
						for ( int j = 0 ; j < Logger.NUM_OF_ETC_YUKKURI_TYPE + 1 ; j++ ){
							numOfSumYukkuri += logDataTmp[j];
						}
						if ( numOfMaxYukkuri < numOfSumYukkuri ){
							numOfMaxYukkuri = numOfSumYukkuri;
						}
						if ( numOfMaxUnun < logDataTmp[Logger.NUM_OF_SHIT] ){
							numOfMaxUnun = logDataTmp[Logger.NUM_OF_SHIT];
						}
						for( int j = 0 ; j < Logger.NUM_OF_LOGDATA_TYPE ; j++ ){
							logData[ Logger.NUM_OF_LOGDATA_TYPE * i + j ] = logDataTmp[j];
						}
					}else{
						for( int j = 0 ; j < Logger.NUM_OF_LOGDATA_TYPE ; j++ ){
							logData[ Logger.NUM_OF_LOGDATA_TYPE * i + j ] = 0;
						}
					}
				}
				int[] xp = new int[ NUM_OF_GRAPH_DATA * 2 ];
				int[] yp = new int[ NUM_OF_GRAPH_DATA * 2 ];
				for ( int i = 0 ; i < NUM_OF_GRAPH_DATA; i++ ){
					xp[ i ] = GRAPH_WIDTH * i / ( NUM_OF_GRAPH_DATA - 1 ) + GRAPH_OFFSETX;
					xp[ NUM_OF_GRAPH_DATA * 2 - i - 1 ] = GRAPH_WIDTH * i / ( NUM_OF_GRAPH_DATA - 1 ) + GRAPH_OFFSETX;
					yp[ i ] = GRAPH_HEIGHT + GRAPH_OFFSETY;
					yp[ NUM_OF_GRAPH_DATA + i ] = GRAPH_HEIGHT + GRAPH_OFFSETY;
				}

				int   opetmp = operationTime % NUM_OF_GRAPH_DATA;

				switch(showPage){
					case 0:
						g.drawString( "現在のゆっくりの個体数(種族別)", 100, 100 );
						g.setFont( textFonttext );

						for ( int i = 0 ; i < Logger.NUM_OF_ETC_YUKKURI_TYPE + 1 ; i++ ){
							g.setColor( Color.WHITE );
							g.drawString( Integer.toString( numOfObjNowLog[i] ), LEGEND_OFFSETX + 130, 30 * i + 140);
							switch ( i ){
								case 0:
									g.drawString( "まりさ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.LIGHT_GRAY );
									break;
								case 1:
									g.drawString( "れいむ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.RED );
									break;
								case 2:
									g.drawString( "ありす", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.YELLOW );
									break;
								case 3:
									g.drawString( "足りないゆ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.ORANGE );
									break;
								case 4:
									g.drawString( "わされいむ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.MAGENTA );
									break;
								default:
									g.drawString( "その他", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.GRAY );
								}
							g.fillRect(LEGEND_OFFSETX + 180, 30 * i + 130 , 10, 10);
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								if ( logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i ] != 0 ){
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i ] * GRAPH_HEIGHT / numOfMaxYukkuri;
								} else {
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i ] * GRAPH_HEIGHT / 1;
								}
							}
							g.fillPolygon( xp , yp, NUM_OF_GRAPH_DATA * 2);	
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								yp[ NUM_OF_GRAPH_DATA * 2 - k - 1 ] = yp[ k ];
							}
						}
						g.setColor( Color.WHITE );
						g.drawRect( GRAPH_OFFSETX , GRAPH_OFFSETY, GRAPH_WIDTH, GRAPH_HEIGHT);
						g.drawString( Integer.toString( numOfMaxYukkuri ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY );
						g.drawString( Integer.toString( numOfMaxYukkuri  / 2 ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY + GRAPH_HEIGHT / 2 );
						g.drawString( "0", GRAPH_OFFSETX - 12 , GRAPH_OFFSETY + GRAPH_HEIGHT );
						

						break;
						
					case 1:
						g.drawString( "現在のゆっくりの個体数(年代別)", 100, 100 );
						g.setFont( textFonttext );
						for ( int i = 0 ; i < 3 ; i++ ){
							g.setColor( Color.WHITE );
							g.drawString( Integer.toString( numOfObjNowLog[ i + Logger.NUM_OF_BABY ] ), LEGEND_OFFSETX + 130, 30 * i + 140);
							switch ( i ){
								case 0:
									g.drawString( "赤ゆ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.ORANGE );
									break;
								case 1:
									g.drawString( "子ゆ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.YELLOW );
									break;
								case 2:
									g.drawString( "成ゆ", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.GREEN );
									break;
							}
							g.fillRect(LEGEND_OFFSETX + 180, 30 * i + 130 , 10, 10);
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								if ( logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i + Logger.NUM_OF_BABY  ] != 0 ){
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i + Logger.NUM_OF_BABY ] * GRAPH_HEIGHT / numOfMaxYukkuri;
								} else {
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i + Logger.NUM_OF_BABY ] * GRAPH_HEIGHT / 1;
								}
							}
							g.fillPolygon( xp , yp, NUM_OF_GRAPH_DATA * 2);	
							
							
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								yp[ NUM_OF_GRAPH_DATA * 2 - k - 1 ] = yp[ k ];
							}
						}
						g.setColor( Color.WHITE );
						g.drawRect( GRAPH_OFFSETX, GRAPH_OFFSETY, GRAPH_WIDTH, GRAPH_HEIGHT);
						g.drawString( Integer.toString( numOfMaxYukkuri ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY );
						g.drawString( Integer.toString( numOfMaxYukkuri  / 2 ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY + GRAPH_HEIGHT / 2 );
						g.drawString( "0", GRAPH_OFFSETX - 12 , GRAPH_OFFSETY + GRAPH_HEIGHT );
						
						break;
					
					case 2:
						g.drawString( "ゆかび感染状況", 100, 100 );
						g.setFont( textFonttext );
						for ( int i = 0 ; i < 2 ; i++ ){
							g.setColor( Color.WHITE );
							switch ( i ){
								case 0:
									g.drawString( Integer.toString( numOfObjNowLog[ Logger.NUM_OF_SICK ] ), LEGEND_OFFSETX + 130, 30 * i + 140);
									g.drawString( "発症/感染", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.GREEN );
									break;
								case 1:
									g.drawString( Integer.toString( numOfMaxYukkuri - numOfObjNowLog[ Logger.NUM_OF_SICK ] ), LEGEND_OFFSETX + 130, 30 * i + 140);
									g.drawString( "未感染", LEGEND_OFFSETX, 30 * i + 140);
									g.setColor( Color.LIGHT_GRAY );
									break;
							}
							g.fillRect(LEGEND_OFFSETX + 180, 30 * i + 130 , 10, 10);
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								if ( numOfMaxYukkuri == 0 ){
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i + Logger.NUM_OF_SICK ] * GRAPH_HEIGHT / 1;
								}else if ( i == 0 ){
									yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + i + Logger.NUM_OF_SICK ] * GRAPH_HEIGHT / numOfMaxYukkuri;
								} else {
									yp[k] -= (logData[ Logger.NUM_OF_LOGDATA_TYPE * k ] + logData[ Logger.NUM_OF_LOGDATA_TYPE * k + 1 ] + logData[ Logger.NUM_OF_LOGDATA_TYPE * k + 2 ] + logData[ Logger.NUM_OF_LOGDATA_TYPE * k + 3 ] + logData[ Logger.NUM_OF_LOGDATA_TYPE * k + 4 ] - logData[ Logger.NUM_OF_LOGDATA_TYPE * k+ Logger.NUM_OF_SICK ]) * GRAPH_HEIGHT / numOfMaxYukkuri;

								}
							}
							g.fillPolygon( xp , yp, NUM_OF_GRAPH_DATA * 2);	
							for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
								yp[ NUM_OF_GRAPH_DATA * 2 - k - 1 ] = yp[ k ];
							}
						}
						g.setColor( Color.WHITE );
						g.drawRect( GRAPH_OFFSETX, GRAPH_OFFSETY, GRAPH_WIDTH, GRAPH_HEIGHT);
						g.drawString( Integer.toString( numOfMaxYukkuri ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY );
						g.drawString( Integer.toString( numOfMaxYukkuri  / 2 ), GRAPH_OFFSETX - String.valueOf(numOfMaxYukkuri).length() * 10 - 2 , GRAPH_OFFSETY + GRAPH_HEIGHT / 2 );
						g.drawString( "0", GRAPH_OFFSETX - 12 , GRAPH_OFFSETY + GRAPH_HEIGHT );
						
						break;

					case 3:
						g.drawString( "うんうんの数", 100, 100 );
						g.setFont( textFonttext );
						g.drawString( "うんうん", LEGEND_OFFSETX, 140);
						g.drawString( Integer.toString( numOfObjNowLog[Logger.NUM_OF_SHIT] ), LEGEND_OFFSETX + 130, 140);
						g.setColor( Color.GRAY );
						for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
							if ( logData[ Logger.NUM_OF_LOGDATA_TYPE * k + Logger.NUM_OF_SHIT ] != 0 ){
								yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + Logger.NUM_OF_SHIT ] * GRAPH_HEIGHT / numOfMaxUnun;
							} else {
								yp[k] -= logData[ Logger.NUM_OF_LOGDATA_TYPE * k + Logger.NUM_OF_SHIT ] * GRAPH_HEIGHT / 1;
							}
						}
						g.fillRect( LEGEND_OFFSETX + 180, 130 , 10, 10);
						g.fillPolygon( xp , yp, NUM_OF_GRAPH_DATA * 2);	
						for( int k = 0 ; k < NUM_OF_GRAPH_DATA ; k++ ){
							yp[ NUM_OF_GRAPH_DATA * 2 - k - 1 ] = yp[ k ];
						}
						g.setColor( Color.WHITE );
						g.drawRect( GRAPH_OFFSETX , GRAPH_OFFSETY, GRAPH_WIDTH, GRAPH_HEIGHT);
						g.drawString( Integer.toString( numOfMaxUnun ), GRAPH_OFFSETX - String.valueOf(numOfMaxUnun).length() * 10 - 2 , GRAPH_OFFSETY );
						g.drawString( Integer.toString( numOfMaxUnun  / 2 ), GRAPH_OFFSETX - String.valueOf(numOfMaxUnun).length() * 10 - 2 , GRAPH_OFFSETY + GRAPH_HEIGHT / 2 );
						g.drawString( "0", GRAPH_OFFSETX - 12 , GRAPH_OFFSETY + GRAPH_HEIGHT );

						break;
					default:
						break;
				}
				for ( int i = 0 ; i < NUM_OF_GRAPH_DATA / 30 + 1 ; i++ ){
					int graphx = GRAPH_WIDTH - ( (( GRAPH_WIDTH ) * 30 * i ) / NUM_OF_GRAPH_DATA ) - ( ( opetmp % 30 ) * GRAPH_WIDTH / NUM_OF_GRAPH_DATA ) + GRAPH_OFFSETX;
					if ( GRAPH_WIDTH - ( GRAPH_WIDTH * 30 * i / NUM_OF_GRAPH_DATA + opetmp ) >= 0 && operationTime - 30 * i >= 0 ){
						g.drawLine(  graphx, GRAPH_OFFSETY, graphx, GRAPH_OFFSETY + GRAPH_HEIGHT);
						g.drawString(( ( Integer.toString(( operationTime - 30 * i ) / 3600 )) + ":" + ( Integer.toString(( operationTime - 30 * i ) / 60 )) + ":" + Integer.toString(( operationTime + 30 * i ) % 60 / 30 * 30 )), graphx - String.valueOf( ( ( Integer.toString(( operationTime - 30 * i ) / 3600 )) + ":" + ( Integer.toString(( operationTime - 30 * i ) / 60 )) + ":" + Integer.toString(( operationTime + 30 * i ) % 60 / 30 * 30 )) ).length() * 5 - 4  , GRAPH_OFFSETY + GRAPH_HEIGHT + 18 );
					}
				}
			}
/*---------------------------------------------------------------------------------------*/
		}
	}

	
	
	private int drawStringMultiLine(Graphics g, String str, int posX, int posY, int width, boolean flag) {
		Graphics2D g2d = (Graphics2D)g;
		AttributedString as = new AttributedString(str);
		as.addAttribute(TextAttribute.FONT, g2d.getFont());
		AttributedCharacterIterator asiterator = as.getIterator();
		FontRenderContext context = g2d.getFontRenderContext();
		LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(as.getIterator(), context);
		float formatWidth = (float)width;
		float drawPosX = 0;
		float drawPosY = posY;
		int beginIndex = asiterator.getBeginIndex();
		int endIndex   = asiterator.getEndIndex();
		lineMeasurer.setPosition(beginIndex);
		while (lineMeasurer.getPosition() < endIndex) {
			TextLayout layout = lineMeasurer.nextLayout(formatWidth);
			drawPosY += layout.getAscent();
			if (layout.isLeftToRight()) {
				drawPosX = posX;
			}
			else {
				drawPosX = posX + formatWidth - layout.getAdvance();
			}
			if (flag) {
				layout.draw(g2d, drawPosX, drawPosY);
			}
			drawPosY += layout.getDescent() + layout.getLeading();
		}
		return (int)Math.ceil(drawPosY);
	}
}

final class ObjComp implements Comparator<Obj> {
	final static ObjComp INSTANCE = new ObjComp();
	//Painter's Algorithm / 画家のアルゴリズム
	@Override final public int compare(Obj o1, Obj o2) {
		int c = o1.y - o2.y;
		if(c == 0) {
			//Improve visibility: at the same y-coordinate, draw small
			//objects after large ones.
			c = (o2 instanceof Body ? ((Body)o2).getAgeState().ordinal() : 1) -
					(o1 instanceof Body ? ((Body)o1).getAgeState().ordinal() : 1);
		}
		return c;
	}
}

class Translate {
	static final private double m = 1.0 / 8.0;
	static final private double n = 7.0 / 8.0;

	static public int transX(int x, int y, int X, int Y) {
		return (int)(((n-m)*Y*x - m*X*y + m*X*Y)/((n-m-1)*y + Y));
	}

	static public int transY(int x, int y, int X, int Y) {
		return (int)(((n-m)*Y*y)/((n-m-1)*y + Y));
	}

	static public int invX(int x, int y, int X, int Y) {
		return (int)((Y*x + m*X*y - m*X*Y)/((n-m)*Y - (n-m-1)*y));
	}

	static public int invY(int x, int y, int X, int Y) {
		return (int)(Y*y/((n-m)*Y - (n-m-1)*y));
	}
}
