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 implements ActionListener {
	static final long serialVersionUID = 1L;
	
	JPanel		rootPane	= new JPanel();
	JPanel		buttonPane	= new JPanel();
	JComboBox	s1, s2;
	JLabel		l1, l2, l3;
	JButton		saveButton, loadButton;
	static myPane	mypane  = new myPane();
	static Thread	mythread;
	
	public SimYukkuri() {	
		super("しむゆっくり");
		// setup button pane
		buttonPane.setLayout(new GridLayout(20, 1));
		buttonPane.setBorder(new LineBorder(Color.gray, 2));
		JLabel title = new JLabel("しむゆっくり Ver1.7　");
		buttonPane.add(title);
		l1 = new JLabel("道具/Tools");
		buttonPane.add(l1);
		String[] list1 = {"針", "ハンマー", "バイブ", "ジュース", "えさ", "ホウキ", "おかざり", "移動"};
		s1 = new JComboBox(list1);
		buttonPane.add(s1);
		l2 = new JLabel("言語/Language");
		buttonPane.add(l2);
		String[] list2 = {"日本語", "英語"};
		s2 = new JComboBox(list2);
		s2.addActionListener(this);
		buttonPane.add(s2);
		l3 = new JLabel(" ");
		buttonPane.add(l3);
		SaveOrLoadListener saveOrLoadListener = new SaveOrLoadListener();
		saveButton = new JButton("セーブ/Save");
		saveButton.addActionListener(saveOrLoadListener);
		buttonPane.add(saveButton);
		loadButton = new JButton("ロード/Load");
		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) {
		SimYukkuri myyukkuri = new SimYukkuri();
		mypane.isRunning = true;
		mythread = new Thread(mypane);
		mythread.start();
	}
	
	public void actionPerformed(ActionEvent e) {
		switch (s2.getSelectedIndex()) {
		case 0:
			mypane.yukkuri.setLanguage(Body.JAPANESE);
			s1.removeAllItems();
			s1.addItem("針");
			s1.addItem("ハンマー");
			s1.addItem("バイブ");
			s1.addItem("ジュース");
			s1.addItem("えさ");
			s1.addItem("ホウキ");
			s1.addItem("おかざり");
			s1.addItem("移動");
			s2.removeAllItems();
			s2.addItem("日本語");
			s2.addItem("英語");
			s2.setSelectedIndex(0);
			break;
		case 1:
			mypane.yukkuri.setLanguage(Body.ENGLISH);
			s1.removeAllItems();
			s1.addItem("Needle");
			s1.addItem("Hammer");
			s1.addItem("Vibrator");
			s1.addItem("Juice");
			s1.addItem("Food");
			s1.addItem("Sweep");
			s1.addItem("Accessory");
			s1.addItem("Move");
			s2.removeAllItems();
			s2.addItem("Japanese");
			s2.addItem("English");
			s2.setSelectedIndex(1);
			break;
		}
	}
	
	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();
				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();
				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();
					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();
					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][15][2];
	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
		try {
			ClassLoader loader = this.getClass().getClassLoader();
			bodyImage[1][0][0] = ImageIO.read(loader.getResourceAsStream("images/marisa1.png"));
			bodyImage[1][1][0] = ImageIO.read(loader.getResourceAsStream("images/marisa2.png"));
			bodyImage[1][2][0] = ImageIO.read(loader.getResourceAsStream("images/marisa3.png"));
			bodyImage[1][3][0] = ImageIO.read(loader.getResourceAsStream("images/marisa4.png"));
			bodyImage[1][4][0] = ImageIO.read(loader.getResourceAsStream("images/marisa5.png"));
			bodyImage[1][5][0] = ImageIO.read(loader.getResourceAsStream("images/marisa6.png"));
			bodyImage[1][6][0] = ImageIO.read(loader.getResourceAsStream("images/marisa7.png"));
			bodyImage[1][7][0] = ImageIO.read(loader.getResourceAsStream("images/marisa8.png"));
			bodyImage[1][8][0] = ImageIO.read(loader.getResourceAsStream("images/marisa9.png"));
			bodyImage[1][9][0] = ImageIO.read(loader.getResourceAsStream("images/marisa10.png"));
			bodyImage[1][10][0] = ImageIO.read(loader.getResourceAsStream("images/marisa11.png"));
			bodyImage[1][11][0] = ImageIO.read(loader.getResourceAsStream("images/marisa2-1.png"));
			bodyImage[1][12][0] = ImageIO.read(loader.getResourceAsStream("images/marisa2-2.png"));
			bodyImage[1][13][0] = ImageIO.read(loader.getResourceAsStream("images/marisa1-2.png"));
			bodyImage[1][14][0] = ImageIO.read(loader.getResourceAsStream("images/marisa3-2.png"));
			bodyImage[1][0][1] = ImageIO.read(loader.getResourceAsStream("images/marisa1-right.png"));
			bodyImage[1][1][1] = ImageIO.read(loader.getResourceAsStream("images/marisa2.png"));
			bodyImage[1][2][1] = ImageIO.read(loader.getResourceAsStream("images/marisa3-right.png"));
			bodyImage[1][3][1] = ImageIO.read(loader.getResourceAsStream("images/marisa4-right.png"));
			bodyImage[1][4][1] = ImageIO.read(loader.getResourceAsStream("images/marisa5-right.png"));
			bodyImage[1][5][1] = ImageIO.read(loader.getResourceAsStream("images/marisa6-right.png"));
			bodyImage[1][6][1] = ImageIO.read(loader.getResourceAsStream("images/marisa7-right.png"));
			bodyImage[1][7][1] = ImageIO.read(loader.getResourceAsStream("images/marisa8-right.png"));
			bodyImage[1][8][1] = ImageIO.read(loader.getResourceAsStream("images/marisa9-right.png"));
			bodyImage[1][9][1] = ImageIO.read(loader.getResourceAsStream("images/marisa10-right.png"));
			bodyImage[1][10][1] = ImageIO.read(loader.getResourceAsStream("images/marisa11-right.png"));
			bodyImage[1][11][1] = ImageIO.read(loader.getResourceAsStream("images/marisa2-1.png"));
			bodyImage[1][12][1] = ImageIO.read(loader.getResourceAsStream("images/marisa2-2.png"));
			bodyImage[1][13][1] = ImageIO.read(loader.getResourceAsStream("images/marisa1-2-right.png"));
			bodyImage[1][14][1] = ImageIO.read(loader.getResourceAsStream("images/marisa3-2-right.png"));
			bodyImage[2][0][0] = ImageIO.read(loader.getResourceAsStream("images/reimu1.png"));
			bodyImage[2][1][0] = ImageIO.read(loader.getResourceAsStream("images/reimu2.png"));
			bodyImage[2][2][0] = ImageIO.read(loader.getResourceAsStream("images/reimu3.png"));
			bodyImage[2][3][0] = ImageIO.read(loader.getResourceAsStream("images/reimu4.png"));
			bodyImage[2][4][0] = ImageIO.read(loader.getResourceAsStream("images/reimu5.png"));
			bodyImage[2][5][0] = ImageIO.read(loader.getResourceAsStream("images/reimu6.png"));
			bodyImage[2][6][0] = ImageIO.read(loader.getResourceAsStream("images/reimu7.png"));
			bodyImage[2][7][0] = ImageIO.read(loader.getResourceAsStream("images/reimu8.png"));
			bodyImage[2][8][0] = ImageIO.read(loader.getResourceAsStream("images/reimu9.png"));
			bodyImage[2][9][0] = ImageIO.read(loader.getResourceAsStream("images/reimu10.png"));
			bodyImage[2][10][0] = ImageIO.read(loader.getResourceAsStream("images/reimu11.png"));
			bodyImage[2][11][0] = ImageIO.read(loader.getResourceAsStream("images/reimu2-1.png"));
			bodyImage[2][12][0] = ImageIO.read(loader.getResourceAsStream("images/reimu2-2.png"));
			bodyImage[2][13][0] = ImageIO.read(loader.getResourceAsStream("images/reimu1-2.png"));
			bodyImage[2][14][0] = ImageIO.read(loader.getResourceAsStream("images/reimu3-2.png"));
			bodyImage[2][0][1] = ImageIO.read(loader.getResourceAsStream("images/reimu1-right.png"));
			bodyImage[2][1][1] = ImageIO.read(loader.getResourceAsStream("images/reimu2.png"));
			bodyImage[2][2][1] = ImageIO.read(loader.getResourceAsStream("images/reimu3-right.png"));
			bodyImage[2][3][1] = ImageIO.read(loader.getResourceAsStream("images/reimu4-right.png"));
			bodyImage[2][4][1] = ImageIO.read(loader.getResourceAsStream("images/reimu5-right.png"));
			bodyImage[2][5][1] = ImageIO.read(loader.getResourceAsStream("images/reimu6-right.png"));
			bodyImage[2][6][1] = ImageIO.read(loader.getResourceAsStream("images/reimu7-right.png"));
			bodyImage[2][7][1] = ImageIO.read(loader.getResourceAsStream("images/reimu8-right.png"));
			bodyImage[2][8][1] = ImageIO.read(loader.getResourceAsStream("images/reimu9-right.png"));
			bodyImage[2][9][1] = ImageIO.read(loader.getResourceAsStream("images/reimu10-right.png"));
			bodyImage[2][10][1] = ImageIO.read(loader.getResourceAsStream("images/reimu11-right.png"));
			bodyImage[2][11][1] = ImageIO.read(loader.getResourceAsStream("images/reimu2-1.png"));
			bodyImage[2][12][1] = ImageIO.read(loader.getResourceAsStream("images/reimu2-2.png"));
			bodyImage[2][13][1] = ImageIO.read(loader.getResourceAsStream("images/reimu1-2-right.png"));
			bodyImage[2][14][1] = ImageIO.read(loader.getResourceAsStream("images/reimu3-2-right.png"));
			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
		yukkuri.addBody(10, 20, 0, Body.MARISA, Body.ADULT, null, null);
		yukkuri.addBody(120, 20, 0, Body.REIMU, Body.ADULT, null, null);
		// run animation
		while (isRunning) {
			yukkuri.run();
			repaint();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e2) {e2.printStackTrace();}
		}
	}
	
	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();
				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
				if (!b.isAccessory()) {
					if (b.isDead()) {
						g.drawImage(bodyImage[b.getBodyType()][10][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
					}
					else if (b.isDamaged()) {
						g.drawImage(bodyImage[b.getBodyType()][9][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
					}
					else {
						if (!b.isGrabbed() && b.getZ() == 0) {
							drY -= jump[(int)b.getAge() % 9]/2/jumpLevel[ageState];
						}
						g.drawImage(bodyImage[b.getBodyType()][8][b.getDirection()],drX, drY, dia[ageState], dia[ageState], this);
					}
				}
				else if (b.isStrike()) {
					g.drawImage(bodyImage[b.getBodyType()][7][b.getDirection()],drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isShitting() || b.isBirth()) {
					g.drawImage(bodyImage[b.getBodyType()][1][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);				
				}
				else if (b.isFurifuri()) {
					if (b.getAge() % 8 <= 3) {
						g.drawImage(bodyImage[b.getBodyType()][11][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
					}
					else if (b.getAge() % 8 <= 7) {
						g.drawImage(bodyImage[b.getBodyType()][12][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
					}
				}
				else if (b.isExciting()) {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[b.getBodyType()][2][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isSleeping()) {
					g.drawImage(bodyImage[b.getBodyType()][3][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isPeroPero() || b.isEating()) {
					g.drawImage(bodyImage[b.getBodyType()][13][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isSukkiri()) {
					g.drawImage(bodyImage[b.getBodyType()][14][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isDead()) {
						g.drawImage(bodyImage[b.getBodyType()][4][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isDamaged()) {
					g.drawImage(bodyImage[b.getBodyType()][5][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isScare()) {
					g.drawImage(bodyImage[b.getBodyType()][7][b.getDirection()],drX, drY, dia[ageState], dia[ageState], this);
				}
				else if (b.isAngry()) {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[b.getBodyType()][6][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				else {
					if (!b.isGrabbed() && b.getZ() == 0) {
						drY -= jump[(int)b.getAge() % 9]/2/jumpLevel[ageState];
					}
					g.drawImage(bodyImage[b.getBodyType()][0][b.getDirection()], drX, drY, dia[ageState], dia[ageState], this);
				}
				// draw script
				if (b.getMessage() != null) {
					g.setColor(Color.white);
					g.fillRoundRect(drX+14, drY-14, b.getMessage().length()*yukkuri.fontWidth()+4, 16, 8, 8);
					g.setColor(Color.black);
					g.drawRoundRect(drX+14, drY-14, b.getMessage().length()*yukkuri.fontWidth()+4, 16, 8, 8);
					g.drawString(b.getMessage(), 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 {
	public int compare(Object arg0, Object 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));
	}
}
