import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import javax.imageio.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SimYukkuri extends JFrame {
	static final long serialVersionUID = 1L;
	static final String TITLE = "しむゆっくり/SimYukkuri";
	static final String VERSION = "Ver 1.10";
	
	JPanel		rootPane	= new JPanel();
	JPanel		buttonPane	= new JPanel();
	JComboBox	s1;
	JLabel		l1, l2;
	JButton		saveButton, loadButton;
	static myPane	mypane  = new myPane();
	static Thread	mythread;
	
	public SimYukkuri() {	
		super(TITLE);
		String[] choices = {"日本語","English"};
		String picked = (String) JOptionPane.showInputDialog(this, "言語/Language", TITLE, JOptionPane.QUESTION_MESSAGE, null, choices, choices[0]);
		
		buttonPane.setLayout(new GridLayout(20, 1));
		buttonPane.setBorder(new LineBorder(Color.gray, 2));
				
		if ( picked == choices[1] )
		{
			JLabel title = new JLabel("SimYukkuri " + VERSION + " ");
			buttonPane.add(title);
			
			String[] list1 = {"Needle", "Hammer", "Vibrator", "Juice", "Food", "Sweep", "Accessory", "Move"};
			s1 = new JComboBox(list1);

			l1 = new JLabel("Tools");
			l2 = new JLabel(" ");
			saveButton = new JButton("Save");
			loadButton = new JButton("Load");
			
			Body.setLanguage(Body.Language.ENGLISH);
		}
		else // default to Japanese
		{
			JLabel title = new JLabel("しむゆっくり " + VERSION + " ");
			buttonPane.add(title);
			
			String[] list1 = {"針", "ハンマー", "バイブ", "ジュース", "えさ", "ホウキ", "おかざり", "移動"};
			s1 = new JComboBox(list1);

			l1 = new JLabel("道具");
			l2 = new JLabel(" ");
			saveButton = new JButton("セーブ");
			loadButton = new JButton("ロード");
						
			Body.setLanguage(Body.Language.JAPANESE);
		}
		
		buttonPane.add(l1);
		buttonPane.add(s1);
		
		buttonPane.add(l2);
		SaveOrLoadListener saveOrLoadListener = new SaveOrLoadListener();

		saveButton.addActionListener(saveOrLoadListener);
		buttonPane.add(saveButton);
		loadButton.addActionListener(saveOrLoadListener);
		buttonPane.add(loadButton);
		// setup my pane
		MyMouseListener ml = new MyMouseListener();
		mypane.addMouseListener(ml);
		mypane.addMouseMotionListener(ml);
		// 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();
	}
		
	public class SaveOrLoadListener implements ActionListener {
		final private JFileChooser fc = new JFileChooser();

		@Override
		public void actionPerformed(ActionEvent e) {
			Object source = e.getSource();
			if(source.equals(saveButton)) {
				doSave();
			} else if(source.equals(loadButton)) {
				doLoad();
			}
		}
		
		public void doSave() {
			int result = fc.showSaveDialog(SimYukkuri.this);
			if(result != JFileChooser.APPROVE_OPTION) return;
			File file = fc.getSelectedFile();
			try {
				Yukkuri.saveState(file);
			} catch(IOException e) {
				System.out.println(e);
			}
		}
		
		public void doLoad() {
			int result = fc.showOpenDialog(SimYukkuri.this);
			if(result != JFileChooser.APPROVE_OPTION) return;
			File file = fc.getSelectedFile();
			try {
				Yukkuri.loadState(file);
			} catch(IOException e) {
				System.out.println(e);
			} catch(ClassNotFoundException e) {
				System.out.println(e);
			}
		}
	}
	
	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;
		int startX = -1, startY = -1, startZ = -1;
		int oX = 0, oY = 0, altitude = 0;

		/*
		BufferedImage bi = ImageIO.read(JSpreadUtilities.getUrl(resourcename));
		  Cursor cursor = Toolkit.getDefaultToolkit().createCustomCursor(
					 bi, new Point(x, y), cursorname);
		 */
		
		synchronized public void mouseClicked(MouseEvent e){
			Dimension size = mypane.getSize();
			int w = size.width, h = size.height;
			boolean hit = false;
			if (s1.getSelectedIndex() == 5) {//ホウキ
				for (Shit s:Yukkuri.shitList) {
					int ageState = s.getAgeState();
					int dia[] = {10, 20, 30};
					int offsetX = (myPane.maxSize - dia[ageState])/2;
					int offsetY = (myPane.maxSize - dia[ageState]);
					int dx = e.getX() - (Translate.transX(s.getX(), s.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
					int dy = e.getY() - (Translate.transY(s.getX(), s.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
					if (dx >= 0 && dx <= dia[ageState] && dy >= 0 && dy <= dia[ageState]) {
						s.remove();
						hit = true;
					}
				}
				for (Food f:Yukkuri.foodList) {
					int offsetX = (myPane.maxSize - myPane.foodSize)/2;
					int offsetY = (myPane.maxSize - myPane.foodSize/2);
					int dx = e.getX() - (Translate.transX(f.getX(), f.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
					int dy = e.getY() - (Translate.transY(f.getX(), f.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
					if (dx >= 0 && dx <= myPane.foodSize && dy >= 0 && dy <= myPane.foodSize/2) {
						f.remove();
						hit = true;
					}
				}
			}
			for (Body b:Yukkuri.bodyList) {
				int ageState = b.getAgeState().ordinal();
				int dia[] = {myPane.maxSize/4, myPane.maxSize/2, myPane.maxSize};
				int offsetX = (myPane.maxSize - dia[ageState])/2;
				int offsetY = (myPane.maxSize - dia[ageState]);
				int dx = e.getX() - (Translate.transX(b.getX(), b.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
				int dy = e.getY() - (Translate.transY(b.getX(), b.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
				if (dx >= 0 && dx <= dia[ageState] && dy >= 0 && dy <= dia[ageState]) {
					switch (s1.getSelectedIndex()) {
					case 0:
						//針
						b.strikeByNeedle();
						Yukkuri.setAlarm();
						break;
					case 1:
						//ハンマー
						b.strikeByHammer();
						Yukkuri.setAlarm();
						break;
					case 2:
						//バイブ
						b.forceSukkiri();
						break;
					case 3:
						//ジュース
						b.giveJuice();
						break;
					case 4:
						//えさ
						break;
					case 5:
						//ホウキ
						if (b.isDead()) {
							b.remove();
						}
						break;
					case 6:
						//おかざり
						if (b.isAccessory()) {
							b.takeAccessory();
						}
						else {
							b.giveAccessory();
						}
						break;
					case 7:
						//移動
						break;
					default:
						System.out.println("invalid tool");
					}
					hit = true;
				}
			}
			if (!hit && s1.getSelectedIndex() == 4) { //えさ
				int offsetX = (myPane.maxSize - myPane.foodSize)/2;
				int offsetY = (myPane.maxSize - myPane.foodSize/2);
				int X = (e.getX() - offsetX)*Box.maxX/(w-myPane.maxSize);
				int Y = (e.getY() - offsetY)*Box.maxY/(h-myPane.maxSize);				
				int x = Translate.invX(X, Y, Box.maxX, Box.maxY);
				int y = Translate.invY(X, Y, Box.maxX, Box.maxY);
				if (x >= 0 && x <= Box.maxX && y >= 0 && y <= Box.maxY) {
					//ゆっくりフード
					mypane.yukkuri.addYukkuriFood(x, y);
				}
			}
		}
		
		synchronized public void mousePressed(MouseEvent e) {
			if (s1.getSelectedIndex() != 7) { //移動
				return;
			}
			if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
				startZ = e.getY() + altitude;
				startY = e.getY();
			}
			if (grabbedObj != null) {
				return;
			}
			int maxY = -1;
			Dimension size = mypane.getSize();
			int w = size.width, h = size.height;
			for (Body b:Yukkuri.bodyList) {
				int ageState = b.getAgeState().ordinal();
				int dia[] = {myPane.maxSize/4, myPane.maxSize/2, myPane.maxSize};
				int offsetX = (myPane.maxSize - dia[ageState])/2;
				int offsetY = (myPane.maxSize - dia[ageState]);
				int dx = e.getX() - (Translate.transX(b.getX(), b.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
				int dy = e.getY() - (Translate.transY(b.getX(), b.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
				if (dx >= 0 && dx <= dia[ageState] && dy >= 0 && dy <= dia[ageState]) {
					if (maxY < b.getY()) {
						grabbedObj = b;
						startX = e.getX();
						startY = e.getY();
						oX = dx;
						oY = dy;
						maxY = b.getY();
					}
				}
			}
			for (Shit s:Yukkuri.shitList) {
				int ageState = s.getAgeState();
				int dia[] = {10, 20, 30};
				int offsetX = (myPane.maxSize - dia[ageState])/2;
				int offsetY = (myPane.maxSize - dia[ageState]);
				int dx = e.getX() - (Translate.transX(s.getX(), s.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
				int dy = e.getY() - (Translate.transY(s.getX(), s.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
				if (dx >= 0 && dx <= dia[ageState] && dy >= 0 && dy <= dia[ageState]) {
					if (maxY < s.getY()) {
						grabbedObj = s;
						startX = e.getX();
						startY = e.getY();
						oX = dx;
						oY = dy;
						maxY = s.getY();
					}
				}
			}
			for (Food f:Yukkuri.foodList) {
				int offsetX = (myPane.maxSize - myPane.foodSize)/2;
				int offsetY = (myPane.maxSize - myPane.foodSize/2);
				int dx = e.getX() - (Translate.transX(f.getX(), f.getY(), Box.maxX, Box.maxY)*(w-myPane.maxSize)/Box.maxX + offsetX);
				int dy = e.getY() - (Translate.transY(f.getX(), f.getY(), Box.maxX, Box.maxY)*(h-myPane.maxSize)/Box.maxY + offsetY);
				if (dx >= 0 && dx <= myPane.foodSize && dy >= 0 && dy <= myPane.foodSize/2) {
					if (maxY < f.getY()) {
						grabbedObj = f;
						startX = e.getX();
						startY = e.getY();
						oX = dx;
						oY = dy;
						maxY = f.getY();
					}
				}
			}
			if (grabbedObj != null) {
				grabbedObj.grab();
			}
		}
		
		synchronized public void mouseReleased(MouseEvent e) {
			if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == 0) {
				startZ = e.getY() + altitude;
				startY = e.getY();
			}
			if ((e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK|MouseEvent.BUTTON3_DOWN_MASK)) != 0) {
				return;
			}
			if (grabbedObj != null) {
				grabbedObj.release();
				grabbedObj = null;
				startX = -1;
				startY = -1;
				startZ = -1;
				oX = 0;
				oY = 0;
				altitude = 0;
			}
		}
		
		synchronized public void mouseDragged(MouseEvent e) {
			if (grabbedObj != null) {
				int button = 1;
				if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
					button = 2;
				}
				Dimension size = mypane.getSize();
				int w = size.width, h = size.height;
				int offsetX = 0;
				int offsetY = 0;
				if (grabbedObj.objType == Obj.YUKKURI) {
					int ageState = ((Body)grabbedObj).getAgeState().ordinal();
					int dia[] = {myPane.maxSize/4, myPane.maxSize/2, myPane.maxSize};
					offsetX = (myPane.maxSize - dia[ageState])/2;
					offsetY = (myPane.maxSize - dia[ageState]);
				}
				else if (grabbedObj.objType == Obj.SHIT) {
					int ageState = ((Shit)grabbedObj).getAgeState();
					int dia[] = {10, 20, 30};
					offsetX = (myPane.maxSize - dia[ageState])/2;
					offsetY = (myPane.maxSize - dia[ageState]);
				}
				else if (grabbedObj.objType == Obj.FOOD) {
					offsetX = (myPane.maxSize - myPane.foodSize)/2;
					offsetY = (myPane.maxSize - myPane.foodSize/2);
				}
				if (button == 2) {
					int X = (e.getX() - oX - offsetX)*Box.maxX/(w-myPane.maxSize);
					int Y = (startY	- oY - offsetY)*Box.maxY/(h-myPane.maxSize);				
					int x = Translate.invX(X, Y, Box.maxX, Box.maxY);
					grabbedObj.setX(x);
					if (startZ - e.getY() > 0) {
						altitude = startZ - e.getY();
						grabbedObj.setZ((startZ - e.getY())*Box.maxZ/h);
					}
				}
				else {
					int X = (e.getX() - oX - offsetX)*Box.maxX/(w-myPane.maxSize);
					int Y = (e.getY() - oY - offsetY + altitude)*Box.maxY/(h-myPane.maxSize);				
					int x = Translate.invX(X, Y, Box.maxX, Box.maxY);
					int y = Translate.invY(X, Y, Box.maxX, Box.maxY);
					grabbedObj.setX(x);
					grabbedObj.setY(y);					
				}
			}
		}
		
		synchronized public void mouseMoved(MouseEvent e) {
			if (s1.getSelectedIndex() == 0 || s1.getSelectedIndex() == 1) { // 針 or ハンマー
				if (Yukkuri.getAlarm() == false) {
					return;
				}
				Dimension size = mypane.getSize();
				int w = size.width, h = size.height;
				for (Body b:Yukkuri.bodyList) {
					int ageState = b.getAgeState().ordinal();
					int dia[] = {myPane.maxSize/4, myPane.maxSize/2, myPane.maxSize};
					int offsetX = (myPane.maxSize - dia[ageState])/2;
					int offsetY = (myPane.maxSize - dia[ageState]);
					int X = (e.getX() - offsetX)*Box.maxX/(w-myPane.maxSize);
					int Y = (e.getY() - offsetY)*Box.maxY/(h-myPane.maxSize);
					int x = Translate.invX(X, Y, Box.maxX, Box.maxY);
					int y = Translate.invY(X, Y, Box.maxX, Box.maxY);
					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 = 1L;
	
	public boolean isRunning = false;
	public Yukkuri yukkuri = new Yukkuri();
	public static final int maxSize = 128;	// max size of icons
	public static final int foodSize = 64;	// size of food
	private Image[][][][] bodyImage = new Image[3][17][2][3];
	private Image[] foodImage = new Image[2];
	private Image[] shitImage = new Image[2];
	private Image shadowImage, foodShadow, shitShadow;
	private Image backGroundImage;
	private List <Obj>list4sort = new ArrayList<Obj>();
	
	public void run() {
		//load images
		final int babyHeight = maxSize/4, babyWidth = maxSize/4;
		final int childHeight = maxSize/2, childWidth = maxSize/2;
		final int babyIndex = Body.AgeState.BABY.ordinal();
		final int childIndex = Body.AgeState.CHILD.ordinal();
		final int adultIndex = Body.AgeState.ADULT.ordinal();
		try {
			ClassLoader loader = this.getClass().getClassLoader();
			bodyImage[Marisa.type][0][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa1.png"));
			bodyImage[Marisa.type][1][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2.png"));
			bodyImage[Marisa.type][2][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa3.png"));
			bodyImage[Marisa.type][3][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa4.png"));
			bodyImage[Marisa.type][4][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa5.png"));
			bodyImage[Marisa.type][5][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa6.png"));
			bodyImage[Marisa.type][6][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa7.png"));
			bodyImage[Marisa.type][7][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa8.png"));
			bodyImage[Marisa.type][8][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa9.png"));
			bodyImage[Marisa.type][9][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa10.png"));
			bodyImage[Marisa.type][10][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa11.png"));
			bodyImage[Marisa.type][11][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2-1.png"));
			bodyImage[Marisa.type][12][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2-2.png"));
			bodyImage[Marisa.type][13][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa1-2.png"));
			bodyImage[Marisa.type][14][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa3-2.png"));
			bodyImage[Marisa.type][15][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa12.png"));
			bodyImage[Marisa.type][16][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa13.png"));
			bodyImage[Marisa.type][0][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa1-right.png"));
			bodyImage[Marisa.type][1][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2.png"));
			bodyImage[Marisa.type][2][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa3-right.png"));
			bodyImage[Marisa.type][3][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa4-right.png"));
			bodyImage[Marisa.type][4][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa5-right.png"));
			bodyImage[Marisa.type][5][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa6-right.png"));
			bodyImage[Marisa.type][6][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa7-right.png"));
			bodyImage[Marisa.type][7][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa8-right.png"));
			bodyImage[Marisa.type][8][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa9-right.png"));
			bodyImage[Marisa.type][9][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa10-right.png"));
			bodyImage[Marisa.type][10][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa11-right.png"));
			bodyImage[Marisa.type][11][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2-1.png"));
			bodyImage[Marisa.type][12][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa2-2.png"));
			bodyImage[Marisa.type][13][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa1-2-right.png"));
			bodyImage[Marisa.type][14][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa3-2-right.png"));
			bodyImage[Marisa.type][16][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/marisa13-right.png"));
			bodyImage[Reimu.type][0][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu1.png"));
			bodyImage[Reimu.type][1][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2.png"));
			bodyImage[Reimu.type][2][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu3.png"));
			bodyImage[Reimu.type][3][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu4.png"));
			bodyImage[Reimu.type][4][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu5.png"));
			bodyImage[Reimu.type][5][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu6.png"));
			bodyImage[Reimu.type][6][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu7.png"));
			bodyImage[Reimu.type][7][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu8.png"));
			bodyImage[Reimu.type][8][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu9.png"));
			bodyImage[Reimu.type][9][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu10.png"));
			bodyImage[Reimu.type][10][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu11.png"));
			bodyImage[Reimu.type][11][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2-1.png"));
			bodyImage[Reimu.type][12][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2-2.png"));
			bodyImage[Reimu.type][13][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu1-2.png"));
			bodyImage[Reimu.type][14][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu3-2.png"));
			bodyImage[Reimu.type][15][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu12.png"));
			bodyImage[Reimu.type][16][0][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu13.png"));
			bodyImage[Reimu.type][0][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu1-right.png"));
			bodyImage[Reimu.type][1][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2.png"));
			bodyImage[Reimu.type][2][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu3-right.png"));
			bodyImage[Reimu.type][3][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu4-right.png"));
			bodyImage[Reimu.type][4][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu5-right.png"));
			bodyImage[Reimu.type][5][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu6-right.png"));
			bodyImage[Reimu.type][6][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu7-right.png"));
			bodyImage[Reimu.type][7][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu8-right.png"));
			bodyImage[Reimu.type][8][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu9-right.png"));
			bodyImage[Reimu.type][9][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu10-right.png"));
			bodyImage[Reimu.type][10][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu11-right.png"));
			bodyImage[Reimu.type][11][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2-1.png"));
			bodyImage[Reimu.type][12][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu2-2.png"));
			bodyImage[Reimu.type][13][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu1-2-right.png"));
			bodyImage[Reimu.type][14][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu3-2-right.png"));
			bodyImage[Reimu.type][16][1][adultIndex] = ImageIO.read(loader.getResourceAsStream("images/reimu13-right.png"));
			for(Image[][][] array3d : bodyImage) {
				for(Image[][] array2d : array3d) {
					for(Image[] array : array2d) {
						if(array[adultIndex] == null)
							continue;
						//Bilinear filtering and bicubic filtering are okay for
						//upscaling, but they are not good for downscaling.
						array[babyIndex] = array[adultIndex].getScaledInstance(babyWidth, babyHeight, Image.SCALE_AREA_AVERAGING);
						array[childIndex] = array[adultIndex].getScaledInstance(childWidth, childHeight, Image.SCALE_AREA_AVERAGING);
					}
				}
			}
			foodImage[0] = ImageIO.read(loader.getResourceAsStream("images/gohan1.png"));
			foodImage[1] = ImageIO.read(loader.getResourceAsStream("images/gohan2.png"));
			shitImage[0] = ImageIO.read(loader.getResourceAsStream("images/unun.png"));
			shitImage[1] = ImageIO.read(loader.getResourceAsStream("images/unun2.png"));
			shadowImage = ImageIO.read(loader.getResourceAsStream("images/shadow.png"));
			foodShadow = ImageIO.read(loader.getResourceAsStream("images/gohan-shadow.png"));
			shitShadow = ImageIO.read(loader.getResourceAsStream("images/unun-shadow.png"));
			backGroundImage = ImageIO.read(loader.getResourceAsStream("images/back.jpg"));
		} catch (IOException e1) {System.out.println("File I/O error");}
		// make initial bodies
		initBodies();
		// run animation
		while (isRunning) {
			yukkuri.run();
			repaint();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e2) {e2.printStackTrace();}
		}
	}
	
	void initBodies ()
	{
		ArrayList<Body> bodies = new ArrayList<Body>();
		String[] names;
		String[] options;
		String[] ages;
		String mess1, mess2, mess3, mess4;
		
		switch (Body.getLanguage())
		{
		case JAPANESE:
		{
			String[] tempNames = {Marisa.nameJ,Reimu.nameJ};
			names = tempNames;
			String[] tempAges = {"赤ちゃん", "子供", "大人"};
			ages = tempAges;
			String[] tempo = {"はい","いいえ"};
			options = tempo;
			mess1 = "どのゆっくりを追加しますか？";
			mess2 = "もっと追加しますか？";
			mess3 = "（あと";
			mess4 = "匹追加出来ます）";
			break;
		}
		case ENGLISH:
		{
			String[] tempNames = {Marisa.nameE,Reimu.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 = 7; 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; }
			bodies.add(Body.fromType(cb1.getSelectedIndex(), cb2.getSelectedIndex()));
			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 ) { yukkuri.addBody(b); }
	}
		
	synchronized public void paint(Graphics g) {
		list4sort.clear();
		list4sort.addAll(Yukkuri.bodyList);
		list4sort.addAll(Yukkuri.foodList);
		list4sort.addAll(Yukkuri.shitList);
		Collections.sort(list4sort, new objComparator());
		Dimension size = getSize();
		int w = size.width, h = size.height;
		g.drawImage(backGroundImage,  0, 0, w, h, this);
		for (Obj o : list4sort) {
			switch (o.objType) {
			case Obj.YUKKURI: {
				Body b = (Body)o;
				int ageState = b.getAgeState().ordinal();
				int dia[] = {maxSize/4, maxSize/2, maxSize};
				int offsetX = (maxSize - dia[ageState])/2;
				int offsetY = (maxSize - dia[ageState]);
				int drX = Translate.transX(b.getX(), b.getY(), Box.maxX, Box.maxY)*(w-maxSize)/Box.maxX + offsetX;
				int drY = Translate.transY(b.getX(), b.getY(), Box.maxX, Box.maxY)*(h-maxSize)/Box.maxY + offsetY;
				int jump[] = {0, 8, 12, 14, 15, 14, 12, 8, 0};
				int jumpLevel[] = {2, 2, 1};
				// draw face
				g.drawImage(shadowImage, drX, drY, dia[ageState], dia[ageState], this);
				drY -= b.getZ()*h/Box.maxZ; // considering z axis
				int bodyType = b.getBodyType();
				int direction = b.getDirection().ordinal();
				if (!b.isAccessory()) {
					if (b.isDead()) {
						g.drawImage(bodyImage[bodyType][10][direction][ageState], drX, drY, this);
					}
					else if (b.isDamaged()) {
						g.drawImage(bodyImage[bodyType][9][direction][ageState], drX, drY, this);
					}
					else {
						if (!b.isGrabbed() && b.getZ() == 0) {
							drY -= jump[(int)b.getAge() % 9]/2/jumpLevel[ageState];
						}
						g.drawImage(bodyImage[bodyType][8][direction][ageState], drX, drY, this);
					}
				}
				else if (b.isStrike()) {
					g.drawImage(bodyImage[bodyType][7][direction][ageState] ,drX, drY, this);
				}
				else if (b.isShitting() || b.isBirth()) {
					g.drawImage(bodyImage[bodyType][1][direction][ageState], drX, drY, this);				
				}
				else if (b.isFurifuri()) {
					if (b.getAge() % 8 <= 3) {
						g.drawImage(bodyImage[bodyType][11][direction][ageState], drX, drY, this);
					}
					else if (b.getAge() % 8 <= 7) {
						g.drawImage(bodyImage[bodyType][12][direction][ageState], drX, drY, this);
					}
				}
				else if (b.isExciting()) {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[bodyType][2][direction][ageState], drX, drY, this);
				}
				else if (b.isSleeping()) {
					g.drawImage(bodyImage[bodyType][3][direction][ageState], drX, drY, this);
				}
				else if (b.isPeroPero() || b.isEating()) {
					if (b.isDamaged()) {
						g.drawImage(bodyImage[bodyType][16][direction][ageState], drX, drY, this);						
					}
					else {
						g.drawImage(bodyImage[bodyType][13][direction][ageState], drX, drY, this);
					}
				}
				else if (b.isEatingShit()) {
					g.drawImage(bodyImage[bodyType][16][direction][ageState], drX, drY, this);
				}
				else if (b.isSukkiri()) {
					g.drawImage(bodyImage[bodyType][14][direction][ageState], drX, drY, this);
				}
				else if (b.isCrashed()) {
					g.drawImage(bodyImage[bodyType][15][0][ageState], drX, drY, this);
				}
				else if (b.isDead()) {
						g.drawImage(bodyImage[bodyType][4][direction][ageState], drX, drY, this);
				}
				else if (b.isDamaged()) {
					g.drawImage(bodyImage[bodyType][5][direction][ageState], drX, drY, this);
				}
				else if (b.isScare() || b.isSadness()) {
					g.drawImage(bodyImage[bodyType][7][direction][ageState], drX, drY, this);
				}
				else if (b.isAngry()) {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[bodyType][6][direction][ageState], drX, drY, this);
				}
				else {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/2/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[bodyType][0][direction][ageState], drX, drY, this);
				}
				// draw script
				String message = b.getMessage();
				if (message != null) {
					g.setColor(Color.white);
					g.fillRoundRect(drX+14, drY-14, message.length()*yukkuri.fontWidth()+4, 16, 8, 8);
					g.setColor(Color.black);
					g.drawRoundRect(drX+14, drY-14, message.length()*yukkuri.fontWidth()+4, 16, 8, 8);
					g.drawString(message, drX+14+2, drY);
				}
			}
			break;
			case Obj.FOOD: {
				Food f = (Food)o;
				int offsetX = (maxSize - foodSize)/2;
				int offsetY = (maxSize - foodSize/2);
				int drX = Translate.transX(f.getX(), f.getY(), Box.maxX, Box.maxY)*(w-maxSize)/Box.maxX + offsetX;
				int drY = Translate.transY(f.getX(), f.getY(), Box.maxX, Box.maxY)*(h-maxSize)/Box.maxY + offsetY;
				g.drawImage(foodShadow, drX, drY, foodSize, foodSize/2, this);
				drY -= f.getZ()*h/Box.maxZ; // considering z axis
				if (f.isEmpty()) {
					g.drawImage(foodImage[0], drX, drY, foodSize, foodSize/2, this);				
				}
				else {
					g.drawImage(foodImage[1], drX, drY, foodSize, foodSize/2, this);
				}
			}
			break;
			case Obj.SHIT: {
				Shit s = (Shit)o;
				int ageState = s.getAgeState();
				int dia[] = {10, 20, 30};
				int offsetX = (maxSize - dia[ageState])/2;
				int offsetY = (maxSize - dia[ageState]);
				int drX = Translate.transX(s.getX(), s.getY(), Box.maxX, Box.maxY)*(w-maxSize)/Box.maxX + offsetX;
				int drY = Translate.transY(s.getX(), s.getY(), Box.maxX, Box.maxY)*(h-maxSize)/Box.maxY + offsetY;
				g.drawImage(shitShadow, drX, drY, dia[ageState], dia[ageState], this);
				drY -= s.getZ()*h/Box.maxZ; // considering z axis
				g.drawImage(shitImage[s.getShitState()], drX, drY, dia[ageState], dia[ageState], this);
			}
			break;
			}
		}
	}
}

class objComparator implements Comparator<Obj> {
	public int compare(Obj arg0, Obj arg1) {
		Obj o1 = (Obj)arg0;
		Obj o2 = (Obj)arg1;
		if (o1.getY() > o2.getY()) {
			return 1;
		} else if (o1.getY() < o2.getY()) {
			return -1;
		} else {
			return 0;
		}
	}
}

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