import java.awt.Image;
import java.util.Random;
import java.io.IOException;
import java.lang.RuntimeException;

import javax.imageio.ImageIO;

public abstract class Body extends Obj implements java.io.Serializable {
	static final long serialVersionUID = 4L;

	public abstract int getType ();
	public int getHybridType (int partnerType) { return getType(); }
	public boolean isHybrid () { return false; }

	public abstract String getNameJ ();
	public abstract String getNameE ();
	public abstract Body clone ();
	public abstract Image getImage (int type, int direction);

	// public variables	
	public static final int EATAMOUNT[] = {100, 100*2, 100*4};
	public enum AgeState { BABY, CHILD, ADULT };
	public enum Direction { LEFT, RIGHT };
	public enum Parent { PAPA, MAMA };

	// private variables
	protected enum Hunger { NONE, VERY };
	protected enum Damage { NONE, VERY }
	protected enum Happiness { VERY_HAPPY, HAPPY, AVERAGE, SAD, VERY_SAD };
	protected enum Attitude { VERY_NICE, NICE, AVERAGE, SHITHEAD, SUPER_SHITHEAD };

	private static final int HUNGRYLIMIT[] = {100*24, 100*24*3, 100*24*7};
	private static final int SHITLIMIT[] = {100*12, 100*24, 100*24};
	private static final int DAMAGELIMIT[] = {100*24, 100*24*3, 100*24*7};
	private static final int BABYLIMIT = 100*24*7;
	private static final int CHILDLIMIT = 100*24*21;
	private static final int LIFELIMIT = 100*24*365;
	private static final int STEP[] = {1, 2, 4};
	private static final int RELAXPERIOD = 100*1;
	private static final int EXCITEPERIOD = 100*3;
	private static final int PREGPERIOD = 100*24;
	private static final int SLEEPPERIOD = 100*3;
	private static final int ACTIVEPERIOD = 100*6;
	private static final int NEEDLE = 100;
	private static final int HAMMER = 100*24*2;
	private static final int HOLDMESSAGE = 15;	
	private static final int STAYLIMIT = 15;
	private static final int HEADAGELIMIT = 100;

	// Used in image loading.
	protected static final int babySize = ImgFlag.MAXSIZE/4;
	protected static final int childSize = ImgFlag.MAXSIZE/2;
	protected static final int adultSize = ImgFlag.MAXSIZE;
	protected static final int babyIndex = AgeState.BABY.ordinal();
	protected static final int childIndex = AgeState.CHILD.ordinal();
	protected static final int adultIndex = AgeState.ADULT.ordinal();
	protected static final int diameter[] = {babySize, childSize, adultSize};
	private static Image[] images = new Image[3];

	protected enum Language { JAPANESE, ENGLISH };
	protected static Language language = Language.JAPANESE;

	protected AgeState ageState;			//BABY, CHILD, ADULT
	protected String messageBuf;	
	protected int destX = -1, destY = -1;		//destination
	protected int damage = 0;				//counter indicating damage
	protected Damage damageState = Damage.NONE;
	protected int hungry = 0;				//counter indicating how hungry
	protected Hunger hungryState = Hunger.NONE;
	protected Attitude attitude = Attitude.AVERAGE;	// counter indicating shithead/nicehead etc.
	protected Happiness happiness = Happiness.AVERAGE;
	protected int shit = 0;
	protected int noDamagePeriod = 0;
	protected int noHungryPeriod = 0;
	protected boolean hasAccessory = true;		//true if having accessory
	protected boolean hasPants = false;			//true if having pants
	protected boolean hasBaby = false;			//having baby or not
	protected int babyType;
	protected int pregnant = 0;
	protected boolean dead = false;			// dead of alive
	protected boolean excitement = false;		//
	protected int excite = 0;
	protected boolean relax = false;
	protected boolean sleeping = false;
	protected int sleep = 0;
	protected long wakeUpTime;
	protected Random rnd = new Random();
	protected int countX = 0, countY = 0;		// how many steps to same direction
	protected int dirX = 0, dirY = 0;			// direction to move on
	protected Direction direction = Direction.RIGHT;	// direction of body
	protected int messageCount = 0;
	protected boolean staying = false;
	protected int staycount = 0;
	protected int stayTime = STAYLIMIT;
	protected boolean motherhood = false;

	protected Body partner = null;
	protected Body parents[] = {null, null};

	// there has to be a better way of handling this but I am at a loss
	protected boolean toFood = false;
	protected boolean toSukkiri = false;
	protected boolean shitting = false;
	protected boolean birth = false;
	protected boolean angry = false;
	protected boolean furifuri = false;
	protected boolean strike = false;
	protected boolean eating = false;
	protected boolean peropero = false;
	protected boolean sukkiri = false;
	protected boolean scare = false;
	protected boolean dirty = false;
	protected int falldownDamage = 0;
	protected boolean crashed = false;
	protected boolean eatingShit = false;
	protected boolean silent = false;
	protected boolean raper = false;

	// private methods
	private AgeState checkAgeState()
	{
		if (age < BABYLIMIT) {
			return AgeState.BABY;
		}
		else if (age < CHILDLIMIT) {
			return AgeState.CHILD;
		}
		return AgeState.ADULT;
	}

	private Hunger checkHungryState()
	{
		if (hungry < HUNGRYLIMIT[ageState.ordinal()]/4) {
			return Hunger.NONE;
		}
		return Hunger.VERY;
	}

	private Damage checkDamageState()
	{
		if (damage > DAMAGELIMIT[ageState.ordinal()]) {
			dead = true;
			return Damage.VERY;
		}
		if (damage >= DAMAGELIMIT[ageState.ordinal()]/2) {
			return Damage.VERY;
		}
		return Damage.NONE;
	}

	private void checkHungry() {
		if (excitement == true || hasBaby == true) {
			hungry += TICK*2;
		} else {
			hungry += TICK;
		}
		if (hungry > HUNGRYLIMIT[ageState.ordinal()]) {
			damage += (hungry - HUNGRYLIMIT[ageState.ordinal()]);
			hungry = HUNGRYLIMIT[ageState.ordinal()];
		}
		if (hungryState == Hunger.NONE && checkHungryState() == Hunger.NONE) {
			noHungryPeriod += TICK;
		} else {
			noHungryPeriod = 0;
		}
		hungryState = checkHungryState();
	}

	private void checkDamage() {
		if (hungryState == Hunger.NONE) {
			damage -= TICK;
		}
		else if (hungry >= HUNGRYLIMIT[ageState.ordinal()]) {
			damage += TICK;
		}
		if (damage < 0) {
			damage = 0;
		}
		Damage newDamageState = checkDamageState();
		if (damageState == Damage.NONE && newDamageState == Damage.NONE) {
			noDamagePeriod += TICK;
		}
		else {
			noDamagePeriod = 0;
		}
		damageState = newDamageState;
	}

	private boolean checkShit() {
		boolean cantMove = false;
		if (hungry < HUNGRYLIMIT[ageState.ordinal()]/4) {
			shit += TICK*2;
		}
		else {
			shit += TICK;
		}
		if (shit > SHITLIMIT[ageState.ordinal()] - TICK*100) {
			if (hasPants) {
				setHappiness(Happiness.SAD);
			}
			shitting = true;
			cantMove = true;
			showShit();
			wakeup();
		}
		if (shit > SHITLIMIT[ageState.ordinal()]) {
			shitting = false;
			shit = 0;
			if (ageState == AgeState.BABY) {
				setDirty(true);
				setHappiness(Happiness.SAD);
			}
			if (hasPants) {
				setHappiness(Happiness.VERY_SAD);
			}
			showShit2();
		}
		return cantMove;
	}

	private boolean checkPregnant() {
		boolean cantMove = false;
		if (hasBaby) {
			pregnant += TICK;
			if (pregnant > PREGPERIOD - TICK*100) {
				cantMove = true;
				birth = true;
				showBreed();
			}
			if (pregnant > PREGPERIOD) {
				// Keep babyType for generating baby.
				pregnant = 0;
				birth = false;
				hasBaby = false;
				if (hasPants) {
					setHappiness(Happiness.VERY_SAD);
				}
				showBreed2();
			}
		}
		return cantMove;
	}

	private boolean checkSleep() {
		if (wakeUpTime + ACTIVEPERIOD < age && !excitement && relax && !scare && !isVerySad()) {
			toFood = false;
			sleeping = true;
			angry = false;
			scare = false;
			setHappiness(Happiness.AVERAGE);
			sleep += TICK;
			if (sleep > SLEEPPERIOD) {
				showWakeup();
				wakeup();
			}
		}
		else {
			sleep = 0;
			sleeping = false;
		}
		return sleeping;
	}

	private void wakeup() {
		sleep = 0;
		sleeping = false;
		wakeUpTime = age;
	}

	private void checkExciteAndRelax() {
		if (noHungryPeriod > RELAXPERIOD && noDamagePeriod > RELAXPERIOD && !sleeping && hasAccessory && !isSad() && !isVerySad()) {
			if (!excitement && !relax) {
				int r;
				if (raper && isRude()) {
					r = rnd.nextInt(6);
				}
				else if (raper || isRude()) {
					r = rnd.nextInt(12);
				}
				else {
					r = rnd.nextInt(24);
				}
				if (((ageState == AgeState.ADULT && !hasBaby) || raper) && r == 0) {
					excitement = true;
					showExcite();
				}
				else {
					relax = true;
					showRelax();
				}
				angry = false;
				scare = false;
			}
			else {
				excite += TICK;
				if (excite > EXCITEPERIOD) {
					excite = 0;
					excitement = false;
					relax = false;
				}
			}
		}
	}

	private void checkMessage() {
		if (--messageCount <= 0) {
			messageCount = 0;
			messageBuf = null;
			furifuri = false;
			strike = false;
			eating = false;
			eatingShit = false;
			peropero = false;
			sukkiri = false;
		}
		if (dead) {
			if (!silent) {
				showDead();
			}
			return;
		}
		else if (messageBuf == null) {
			if (toFood) {
				showWantFood();
			}
			else if (toSukkiri) {
				showExcite();
			}
			else if (sleeping) {
				if (rnd.nextInt(10) == 0) {
					showSleep();
				}
			}
			else if (!hasAccessory) {
				showNoAccessory();
			}
			else if (isHungry()) {
				if (rnd.nextInt(30) == 0) {
					if (!isRude()) {
						if (!wantToShit())
							showHungry();
					}
					else {
						showHungry();
					}
				}
			}
			else if (getZ() != 0) {
				showFlying();
			}
		}
	}

	private void stay() {
		staying = true;
		stayTime = STAYLIMIT;
	}

	private void stay(int time) {
		staying = true;
		stayTime = time;
	}
	
	private void setHappiness(Happiness happy) {
		if (happy == Happiness.SAD) {
			if (happiness != Happiness.VERY_SAD) {
				happiness = happy;
			}
		}
		else if (happy == Happiness.HAPPY) {
			if (happiness != Happiness.VERY_HAPPY) {
				happiness = happy;
			}
		}
		else {
			happiness = happy;
		}
	}

	private void moveBody(boolean dontMove) {
		if (grabbed) {
			// if grabbed, it cannot move.
			falldownDamage = 0;
			return;
		}
		if (z != 0) {
			// if falling down, it cannot move to x-y axis
			z -= 2;
			falldownDamage += 2;
			if (z <= 0) {
				z = 0;
				int jumpLevel[] = {2, 2, 1};
				if (falldownDamage >= 8/jumpLevel[ageState.ordinal()]) {
					strike(falldownDamage*100*24*4/Box.maxZ);
					if (dead) {
						showDieing();
						crashed = true;
					}
				}
			}
			return;
		}
		if (dontMove) {
			return;
		}

		// moving
		int sameDest = 30 * STEP[ageState.ordinal()];
		int step = STEP[ageState.ordinal()];
		if (hasBaby || hungryState == Hunger.VERY || damageState == Damage.VERY) {
			step /= 2;
			if (step == 0) {
				step = 1;
			}
		}

		int freq = STEP[AgeState.ADULT.ordinal()]/step;
		if (age % freq != 0) {
			return;
		}

		step = 1;
		// calculate x axis
		if (destX >= 0) {
			if (destX - x > step) {
				dirX = 1;
				x += step;
			}
			else if (x - destX > step) {
				dirX = -1;
				x -= step;
			}
			else {
				dirX = 0;
				destX = -1;
			}
		}
		else {
			if (countX++ == 0) {
				int r = rnd.nextInt(2);
				switch (dirX) {
				case 0:
					dirX = (r == 0 ? 1 : -1);
					break;
				case 1:
					dirX = (r == 0 ? 0 : dirX);
					break;
				case -1:
					dirX = (r == 0 ? 0 : dirX);
					break;
				default:
					break;
				}
			}
			else if (countX++ >= sameDest) {
				countX = 0;
			}
			x += dirX * step;
		}
		if (x < 0) {
			x = 0;
			dirX *= -1;
		}
		else if (x > Box.maxX) {
			x = Box.maxX;
			dirX *= -1;
		}
		// calculate y axis
		if (destY >= 0) {
			if (destY - y > step) {
				dirY = 1;
				y += step;
			}
			else if (y - destY > step) {
				dirY = -1;
				y -= step;
			}
			else {
				dirY = 0;
				destY = -1;
			}
		}
		else {
			if (countY++ == 0) {
				int r = rnd.nextInt(2);
				switch (dirY) {
				case 0:
					dirY = (r == 0 ? 1 : -1);
					break;
				case 1:
					dirY = (r == 0 ? 0 : dirY);
					break;
				case -1:
					dirY = (r == 0 ? 0 : dirY);
					break;
				default:
					break;
				}
			}
			else if (countY++ >= sameDest) {
				countY = 0;
			}
			y += dirY * step;
		}

		if (y < 0) {
			y = 0;
			dirY *= -1;
		}
		else if (y > Box.maxY) {
			y = Box.maxY;
			dirY *= -1;
		}

		if (dirX == -1) {
			direction = Direction.LEFT;
		}
		else if (dirX == 1) {
			direction = Direction.RIGHT;
		}
	}

	protected void setMessage(String message) {
		setMessage(message, HOLDMESSAGE);
	}

	protected void setMessage(String message, int count) {
		if (staying == true && messageBuf != null) {
			return;
		}
		messageCount = count;
		messageBuf = message;
		// reset actions.
		furifuri = false;
		strike = false;
		eating = false;
		eatingShit = false;
		peropero = false;
		sukkiri = false;
	}

	protected abstract String msgWantFoodJ();
	protected abstract String msgWantFoodE();

	protected void showWantFood() {
		switch (language) {
		case JAPANESE:
			setMessage(msgWantFoodJ());
			break;
		case ENGLISH:
			setMessage(msgWantFoodE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgNoFoodJ();
	protected abstract String msgNoFoodE();

	public void showNoFood() {
		if (toFood == false) {
			return;
		}
		toFood = false;

		switch (language) {
		case JAPANESE:
			setMessage(msgNoFoodJ());
			break;
		case ENGLISH:
			setMessage(msgNoFoodE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
		setHappiness(Happiness.VERY_SAD);
	}

	protected abstract String msgExciteJ();
	protected abstract String msgExciteE();

	protected void showExcite() {
		if (excitement) {
			switch (language) {
			case JAPANESE:
				setMessage(msgExciteJ());
				break;
			case ENGLISH:
				setMessage(msgExciteE());
			}
		}
	}

	protected abstract String msgRelaxJ();
	protected abstract String msgFuriFuriJ();
	protected abstract String msgRelaxE();
	protected abstract String msgFuriFuriE();

	protected void showRelax() {
		if (isRude()) {
			switch (language) {
			case JAPANESE:
				setMessage(msgFuriFuriJ(), 30);
				break;
			case ENGLISH:
				setMessage(msgFuriFuriE(), 30);
				break;
			default:
				throw new LanguageException();
			}
			furifuri = true;
			stay(30);
		}
		else {
			switch (language) {
			case JAPANESE:
				setMessage(msgRelaxJ(), 30);
				break;
			case ENGLISH:
				setMessage(msgRelaxE(), 30);
				break;
			default:
				throw new LanguageException();
			}
			stay(30);
		}
	}

	protected abstract String msgWakeupJ();
	protected abstract String msgWakeupE();

	protected void showWakeup() {
		switch (language) {
		case JAPANESE:
			setMessage(msgWakeupJ());
			break;
		case ENGLISH:
			setMessage(msgWakeupE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgSleepJ();
	protected abstract String msgSleepE();

	protected void showSleep() {
		if (sleep != 0) {
			switch (language) {
			case JAPANESE:
				setMessage(msgSleepJ());
				break;
			case ENGLISH:
				setMessage(msgSleepE());
				break;
			}
		}	
	}

	protected abstract String msgScreamJ ();
	protected abstract String msgScreamE ();

	protected void showScream() {
		messageBuf = null; // show message immediately
		staying = false;
		switch (language) {
		case JAPANESE:
			setMessage(msgScreamJ());
			break;
		case ENGLISH:
			setMessage(msgScreamE());
			break;
		default:
			throw new LanguageException();
		}
		strike = true;
		stay();
	}

	protected abstract String msgScareJ ();
	protected abstract String msgScareE ();

	protected void showScare() {
		if (sleeping) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgScareJ());
			break;
		case ENGLISH:
			setMessage(msgScareE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgAlarmJ ();
	protected abstract String msgAlarmE ();

	protected void showAlarm() {
		if (sleeping) {
			return;
		}
		switch (language) {
		case JAPANESE:
			setMessage(msgAlarmJ()); 
			break;
		case ENGLISH:
			setMessage(msgAlarmE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgDyingJ ();
	protected abstract String msgDyingE ();

	protected void showDieing() {
		if (silent) {
			return;
		}
		messageBuf = null;
		switch (language) {
		case JAPANESE:
			setMessage(msgDyingJ());
			break;
		case ENGLISH:
			setMessage(msgDyingE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgDeadJ ();
	protected abstract String msgDeadE ();

	protected void showDead() {
		String messages;
		switch (language) {
		case JAPANESE:
			messages = msgDeadJ();
			break;
		case ENGLISH:
			messages = msgDeadE();
			break;
		default:
			throw new LanguageException();
		}
		setMessage(messages);
		if (messageBuf == messages)  {
			// if the message is set successfully, be silent.
			silent = true;
		}
	}

	protected abstract String msgEatingJ ();
	protected abstract String msgEatingE ();

	protected void showEating() {
		switch (language) {
		case JAPANESE:
			setMessage(msgEatingJ());
			break;
		case ENGLISH:
			//Yoga Note* Check はふはふ
			setMessage(msgEatingE());
			break;
		default:
			throw new LanguageException();
		}
		eating = true;
		stay();
	}

	protected abstract String msgEatingShitJ ();
	protected abstract String msgEatingShitE ();

	protected void showEatingShit() {
		switch (language) {
		case JAPANESE:
			setMessage(msgEatingShitJ());
			break;
		case ENGLISH:
			setMessage(msgEatingShitE());
			break;
		default:
			throw new LanguageException();
		}
		eatingShit = true;
		stay();
	}

	protected abstract String msgFullJ ();
	protected abstract String msgFullE ();

	protected void showFull() {
		switch (language) {
		case JAPANESE:
			setMessage(msgFullJ());
			break;
		case ENGLISH:
			setMessage(msgFullE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgHealingJ ();
	protected abstract String msgHealingE ();

	protected void showHealing() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHealingJ());
			break;
		case ENGLISH:
			setMessage(msgHealingE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgSukkiriJ ();
	protected abstract String msgSukkiriE ();

	protected void showSukkiri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSukkiriJ(), 30);
			break;
		case ENGLISH:
			setMessage(msgSukkiriE(), 30);
			break;
		default:
			throw new LanguageException();
		}
		stay(30);
	}

	protected abstract String msgBirthJ ();
	protected abstract String msgBirthE ();

	protected void showBirth() {
		switch (language) {
		case JAPANESE:
			setMessage(msgBirthJ(), 30);
			break;
		case ENGLISH:
			setMessage(msgBirthE(), 30);
			break;
		}
		stay(30);
	}

	protected abstract String msgShitJ ();
	protected abstract String msgShitE ();

	protected void showShit() {
		switch (language) {
		case JAPANESE:
			setMessage(msgShitJ());
			break;
		case ENGLISH:
			setMessage(msgShitE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgShit2J ();
	protected abstract String msgShit2E ();

	protected void showShit2() {
		switch (language) {
		case JAPANESE:
			setMessage(msgShit2J());
			break;
		case ENGLISH:
			setMessage(msgShit2E());
			break;
		default:
			throw new LanguageException();
		}
		if (!hasPants) {
			furifuri = true;
			stay();
		}
	}

	protected abstract String msgSuriSuriJ ();
	protected abstract String msgSuriSuriE ();

	protected void showSurisuri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSuriSuriJ());
			break;
		case ENGLISH:
			setMessage(msgSuriSuriE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgPeroPeroJ ();
	protected abstract String msgPeroPeroE ();

	protected void showPeroPero() {
		switch (language) {
		case JAPANESE:
			setMessage(msgPeroPeroJ());
			break;
		case ENGLISH:
			setMessage(msgPeroPeroE());
			break;
		default:
			throw new LanguageException();
		}
		peropero = true;
		stay();
	}

	protected abstract String msgBreedJ ();
	protected abstract String msgBreedE ();

	protected void showBreed() {
		switch (language) {
		case JAPANESE:
			setMessage(msgBreedJ());
			break;
		case ENGLISH:
			setMessage(msgBreedE());
			break;
		}
	}

	protected abstract String msgBreed2J ();
	protected abstract String msgBreed2E ();

	protected void showBreed2() {
		messageBuf = null;
		switch (language) {
		case JAPANESE:
			setMessage(msgBreed2J());
			break;
		case ENGLISH:
			setMessage(msgBreed2E());
			break;
		}
		if (!hasPants) {
			furifuri = true;
			stay();
		}
	}

	protected abstract String msgHateShitJ ();
	protected abstract String msgHateShitE ();

	public void showHateShit() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHateShitJ());
			break;
		case ENGLISH:
			setMessage(msgHateShitE());
			break;
		default:
			throw new LanguageException();
		}
	}	

	protected abstract String msgHungryJ ();
	protected abstract String msgHungryE ();

	protected void showHungry() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHungryJ());
			break;
		case ENGLISH:
			setMessage(msgHungryE());
			break;
		default:
			throw new LanguageException();
		}
		stay();
	}

	protected abstract String msgNoAccessoryJ ();
	protected abstract String msgNoAccessoryE ();

	protected void showNoAccessory() {
		switch (language) {
		case JAPANESE:
			setMessage(msgNoAccessoryJ());
			break;
		case ENGLISH: 
			setMessage(msgNoAccessoryE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgHateYukkuriJ ();
	protected abstract String msgHateYukkuriE ();

	public void showHateYukkuri() {
		switch (language) {
		case JAPANESE:
			setMessage(msgHateYukkuriJ());
			break;
		case ENGLISH:
			setMessage(msgHateYukkuriE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgFlyingJ ();
	protected abstract String msgFlyingE ();

	protected void showFlying() {
		switch (language) {
		case JAPANESE:
			setMessage(msgFlyingJ());
			break;
		case ENGLISH:
			setMessage(msgFlyingE());
			break;
		default:
			throw new LanguageException();
		}
	}

	protected abstract String msgSadnessForChildJ ();
	protected abstract String msgSadnessForChildE ();

	protected void showSadnessForChild() {
		switch (language) {
		case JAPANESE:
			setMessage(msgSadnessForChildJ());
			break;
		case ENGLISH:
			setMessage(msgSadnessForChildE());
			break;
		default:
			throw new LanguageException();
		}
		setHappiness(Happiness.VERY_SAD);
	}

	protected abstract String msgSadnessForPartnerJ (Body partner);
	protected abstract String msgSadnessForPartnerE (Body partner);

	protected void showSadnessForPartner(Body partner) {
		switch (language) {
		case JAPANESE:
			setMessage(msgSadnessForPartnerJ(partner));
			break;
		case ENGLISH:
			setMessage(msgSadnessForPartnerE(partner));
			break;
		default:
			throw new LanguageException();
		}
		setHappiness(Happiness.VERY_SAD);
	}

	// public methods
	public Body(int initX, int initY, int initZ, AgeState initAgeState, Body p1, Body p2) {
		objType = Type.YUKKURI;
		x = initX;
		y = initY;
		z = initZ;
		parents[Parent.PAPA.ordinal()] = p1;
		parents[Parent.MAMA.ordinal()] = p2;
		switch (initAgeState) {
		case BABY:
			age = 0;
			showBirth();
			break;
		case CHILD:
			age = BABYLIMIT;
			break;
		case ADULT:
		default:
			age = CHILDLIMIT;
			break;
		}
		age += rnd.nextInt(100);
		ageState = checkAgeState();
		shit = rnd.nextInt(SHITLIMIT[ageState.ordinal()]);
		wakeUpTime = age;
		removed = false;
		if (rnd.nextInt(2) == 0) {
			attitude = Attitude.SHITHEAD;
		}
		else {
			attitude = Attitude.NICE;
		}
	}

	public static Body fromType (int type, int state) {
		String[] ageState = {"BABY", "CHILD", "ADULT"};
		Random rnd = new Random();
		switch (type)
		{
		case Marisa.type:
			return new Marisa(rnd.nextInt(Box.maxX),rnd.nextInt(Box.maxY),0,AgeState.valueOf(ageState[state]),null,null);
		case Reimu.type:
			return new Reimu(rnd.nextInt(Box.maxX),rnd.nextInt(Box.maxY),0,AgeState.valueOf(ageState[state]),null,null);
		case Alice.type:
			return new Alice(rnd.nextInt(Box.maxX),rnd.nextInt(Box.maxY),0,AgeState.valueOf(ageState[state]),null,null);
		case WasaReimu.type:
			return new WasaReimu(rnd.nextInt(Box.maxX),rnd.nextInt(Box.maxY),0,AgeState.valueOf(ageState[state]),null,null);
		default:
			throw new RuntimeException("Unknown yukkuri type.");
		}
	}

	protected Body (Body b) {
		objType = b.objType;
		x = b.x;
		y = b.y;
		z = b.z;
		parents[Parent.PAPA.ordinal()] = b.parents[Parent.PAPA.ordinal()];
		parents[Parent.MAMA.ordinal()] = b.parents[Parent.MAMA.ordinal()];
		age = b.age;
		ageState = b.ageState;
		shit = b.shit;
		wakeUpTime = b.wakeUpTime;
		removed = b.removed;
	}

	@Override
	public void remove() {
		removed = true;
		parents[Parent.PAPA.ordinal()] = null;
		parents[Parent.MAMA.ordinal()] = null;
		partner = null;
	}

	public int getBabyType() {
		return babyType;
	}

	public AgeState getAgeState() {
		return ageState;
	}

	public long getAge() {
		return age;
	}
	
	public static long getAgeLimit() {
		return LIFELIMIT;
	}
	
	public static int getHeadageLimit() {
		return HEADAGELIMIT;
	}
	
	public int getDamage() {
		return damage;
	}
	
	public int getDamageLimit() {
		return DAMAGELIMIT[ageState.ordinal()];
	}
	
	public int getHungry() {
		return hungry;
	}
	
	public int getHungryLimit() {
		return HUNGRYLIMIT[ageState.ordinal()];
	}

	public int getEatAmount() {
		return EATAMOUNT[ageState.ordinal()];
	}

	public int getStep() {
		return STEP[ageState.ordinal()];
	}

	public String getMessage() {
		return messageBuf;
	}

	public Direction getDirection() {
		return direction;
	}
	
	public int getSize() {
		return diameter[ageState.ordinal()];
	}
	
	public Image getShadowImage() {
		return images[ageState.ordinal()];		
	}
	
	public static void loadShadowImages (ClassLoader loader) throws IOException {
		final String path = "images/";

		images[adultIndex] = ImageIO.read(loader.getResourceAsStream(path+"shadow.png"));
		
		images[adultIndex] = images[adultIndex].getScaledInstance(adultSize, adultSize, Image.SCALE_AREA_AVERAGING);
		images[childIndex] = images[adultIndex].getScaledInstance(childSize, childSize, Image.SCALE_AREA_AVERAGING);
		images[babyIndex] = images[adultIndex].getScaledInstance(babySize, babySize, Image.SCALE_AREA_AVERAGING);
	}

	public void setDirty(boolean flag) {
		dirty = flag;
		if (dirty) {
			setHappiness(Happiness.SAD);
		}
		else {
			setHappiness(Happiness.HAPPY);
			showSukkiri();
		}
	}

	public boolean isHungry() {
		return (!dead && hungry >= HUNGRYLIMIT[ageState.ordinal()] / 8);
	}

	public boolean isTooHungry() {
		return (!dead && hungry >= HUNGRYLIMIT[ageState.ordinal()] && checkDamageState() == Damage.VERY);
	}

	public boolean isDead() {
		return dead;
	}

	public boolean isAdult() {
		return (ageState == AgeState.ADULT);
	}

	public boolean isSleeping() {
		return (!dead && sleeping);
	}

	public boolean isShitting() {
		return (!dead && shitting);
	}

	public boolean isExciting() {
		return (!dead && excitement);
	}

	public boolean isAngry() {
		return (!dead && angry);
	}

	public boolean isScare() {
		return (!dead && scare);
	}

	public boolean isSad() {
		return (!dead && happiness == Happiness.SAD);
	}
	
	public boolean isVerySad() {
		return (!dead && happiness == Happiness.VERY_SAD);
	}
	
	public boolean isHappy() {
		return (happiness == Happiness.HAPPY || happiness == Happiness.VERY_HAPPY);
	}

	public boolean isFurifuri() {
		return (!dead && furifuri);
	}

	public boolean isStrike() {
		return (!dead && strike);
	}

	public boolean isDamaged() {
		return (checkDamageState() == Damage.VERY);
	}

	public boolean isBirth() {
		return (!dead && birth);
	}

	public boolean isEating() {
		return (!dead && eating);
	}

	public boolean isEatingShit() {
		return (!dead && eatingShit);
	}

	public boolean isPeroPero() {
		return (!dead && peropero);
	}

	public boolean isSukkiri() {
		return (!dead && sukkiri);
	}

	public boolean isDirty() {
		return (dead || dirty);
	}

	public boolean isCrashed() {
		return crashed;
	}

	public boolean isRude() {
		return (attitude == Attitude.SHITHEAD || attitude == Attitude.SUPER_SHITHEAD);
	}

	public boolean isParent(Body other) {
		return (other.parents[Parent.PAPA.ordinal()] == this || other.parents[Parent.MAMA.ordinal()] == this);
	}

	public boolean isChild(Body other) {
		return other.isParent(this);
	}

	public boolean isPartner(Body other) {
		return partner == other;
	}
	
	public boolean isRaper() {
		return raper;
	}
	
	public boolean isMotherhood() {
		return motherhood;
	}
	
	public boolean isOld() {
		return age > (LIFELIMIT*9/10);
	}
	
	public boolean isTalking() {
		return messageBuf != null;
	}

	public boolean hasAccessory() {
		return hasAccessory;
	}

	public boolean hasPants() {
		return hasPants;
	}
	
	public boolean wantToShit() {
		if (shit > SHITLIMIT[ageState.ordinal()]/3*2) {
			return true;
		}
		return false;
	}

	public void doSukkiri(Body p) {
		if (dead) {
			return;
		}
		// show message
		showSukkiri();
		sukkiri = true;
		// change state
		excitement = false;
		relax = false;
		angry = false;
		scare = false;
		setHappiness(Happiness.HAPPY);
		noHungryPeriod = 0;
		hungry += 100*12;
		hungryState = checkHungryState();
		// if it has pants, cannot get pregnant
		if (hasPants() || p.hasPants()) {
			if (hasPants()) {
				setDirty(true);
			}
			else {
				p.setDirty(true);
			}
			return;
		}
		//change partner state
		p.showSukkiri();
		sukkiri = true;
		if (!p.hasBaby) {
			p.hasBaby = true;
			if (!isHybrid() && !p.isHybrid() && (rnd.nextInt(100) == 0)) {
				p.babyType = p.getHybridType(getType());
			}
			else if (rnd.nextInt(2) == 0) {
				p.babyType = getType();
			}
			else {
				p.babyType = p.getType();
			}
			if ((p.babyType == Reimu.type) && (rnd.nextInt(2) == 0)) {
				p.babyType = WasaReimu.type;
			}
			else if ((p.babyType == WasaReimu.type) && (rnd.nextInt(2) == 0)) {
				p.babyType = Reimu.type;
			}
			if (isAdult() && p.isAdult())
			{
				p.partner = this;
				partner = p;
			}
			p.excitement = false;
			p.relax = false;
			p.angry = false;
			p.scare = false;
			p.setHappiness(Happiness.HAPPY);
			p.noHungryPeriod = 0;
			p.hungry += 100*48;
			p.hungryState = p.checkHungryState();
		}
	}

	public void forceSukkiri() {
		if (dead || !hasAccessory) {
			return;
		}
		excitement = true;
		showExcite();
		stay();
	}

	public void doSurisuri(Body partner) {
		if (dead || partner.dead) {
			return;
		}
		showSurisuri();
		angry = false;
		partner.angry = false;
		scare = false;
		partner.scare = false;
		setHappiness(Happiness.VERY_HAPPY);
		partner.setHappiness(Happiness.VERY_HAPPY);
		//partner.stay();
	}

	public void doPeropero(Body partner) {
		if (dead || partner.dead) {
			return;
		}
		showPeroPero();
		partner.dirty = false;
		angry = false;
		partner.angry = false;
		scare = false;
		partner.scare = false;
		setHappiness(Happiness.VERY_HAPPY);
		partner.setHappiness(Happiness.VERY_HAPPY);
	}

	public void moveTo(int toX, int toY)
	{
		if (dead) {
			return;
		}
		if (toX <= 0) {
			destX = 0;
		}
		else if (toX >= Box.maxX) {
			destX = Box.maxX;
		}
		else {
			destX = toX;
		}
		if (toY <= 0) {
			destY = 0;
		}
		else if (toY >= Box.maxY) {
			destY = Box.maxY;
		}
		else {
			destY = toY;
		}
	}

	public void moveToFood(int toX, int toY) {
		toFood = true;
		toSukkiri = false;
		moveTo(toX, toY);
	}

	public void moveToSukkiri(int toX, int toY) {
		toSukkiri = true;
		toFood = false;
		moveTo(toX, toY);
	}

	public void lookTo(int toX, int toY) {
		if (dead || sleeping) {
			return;
		}
		if (toX > x) {
			direction = Direction.RIGHT;
		}
		else if (toX < x) {
			direction = Direction.LEFT;
		}
		stay();
	}

	public void eatFood(Food.type foodType, int amount) {
		if (dead) {
			return;
		}
		toFood = false;
		if (!isHungry()) {
			showFull();
			return;
		}
		hungry -= amount;
		if (hungry < 0) {
			hungry = 0;
		}
		if (foodType == Food.type.SHIT) {
			showEatingShit();
		}
		else {
			showEating();
		}
		hungryState = checkHungryState();
		angry = false;
		scare = false;
	}

	public void strike(int amount) {
		if (dead) {
			return;
		}
		excitement = false;
		damage += amount;
		showScream();
		damageState = checkDamageState();
		setAngry();
		wakeup();
	}

	public void strikeByNeedle() {
		strike(NEEDLE);
	}

	public void strikeByHammer() {
		if (dead) {
			return;
		}
		strike(HAMMER);
		if (dead) {
			showDieing();
			if (ageState != AgeState.ADULT) {
				crashed = true;
			}
		}
	}

	public void putStress(int numOfBody) {
		if (dead || (age % 10 != 0) || (HEADAGELIMIT >= numOfBody)) {
			return;
		}
		damage += rnd.nextInt(numOfBody - HEADAGELIMIT);
		damageState = checkDamageState();
	}

	public void takeAccessory() {
		hasAccessory = false;
		setHappiness(Happiness.VERY_SAD);
	}

	public void giveAccessory() {
		hasAccessory = true;
		setHappiness(Happiness.VERY_HAPPY);
	}
	
	public void takePants() {
		hasPants = false;
	}
	
	public void givePants() {
		hasPants = true;
		if (!dirty)
			setHappiness(Happiness.HAPPY);
	}

	public void giveJuice(){
		if (dead) {
			return;
		}
		damage -= 100*100;
		if (damage < 0) {
			damage = 0;
		}
		hungry -= 100*100;
		if (hungry < 0) {
			hungry = 0;
		}
		showHealing();
		damageState = checkDamageState();
		hungryState = checkHungryState();
		angry = false;
		scare = false;
		setHappiness(Happiness.AVERAGE);
		toFood = false;
		excitement = false;
	}

	public void runAway(int fromX, int fromY) {
		if (dead || sleeping || excitement || angry) {
			return;
		}
		int toX, toY;
		if (x > fromX) {
			toX = Box.maxX;
		}
		else {
			toX = 0;
		}
		if (y > fromY) {
			toY = Box.maxY;
		}
		else {
			toY = 0;
		}
		moveTo(toX, toY);
		toFood = false;
		scare = true;
		excitement = false;
	}

	public void setAngry() {
		if (damageState == Damage.NONE && hasAccessory) {
			angry = true;
			furifuri = false;
			scare = false;
			excitement = false;
		}
	}

	public static void setLanguage(Language lang) { language = lang; }

	public static Language getLanguage() { return language; }

	// calling every tick
	@Override
	public Obj.Event clockTick()
	{
		// if removed, remove body
		if (removed) {
			return Obj.Event.REMOVED;
		}

		// if dead, do nothing.
		if (dead) {
			moveBody(true); // for falling the body
			checkMessage();
			return Obj.Event.DEAD;
		}

		Event retval = Event.DONOTHING;

		// check age
		age += TICK;
		if (age > LIFELIMIT) {
			dead = true;
			moveBody(true); // for falling the body
			checkMessage();
			return Obj.Event.DEAD;
		}
		ageState = checkAgeState();

		// check status
		checkHungry();
		checkDamage();

		// check events
		boolean dontMove = false;

		// check shit
		int oldShit = shit;
		if ( checkShit() ) {
			dontMove = true;
		}
		if ( oldShit != 0 && shit == 0 ) {
			if (hasPants()) {
				setDirty(true);
			}
			else {
				retval = Obj.Event.DOSHIT;
			}
		}

		// check pregnant
		boolean oldHasBaby = hasBaby;
		if ( checkPregnant() ) {
			dontMove = true;
		}
		if ( oldHasBaby == true && hasBaby == false ) {
			if (hasPants()) {
				setDirty(true);
			}
			else {
				retval = Obj.Event.BIRTHBABY;
			}
		}

		// check sleep
		if ( checkSleep() ) {
			dontMove = true;
		}

		// check relax
		if (isFurifuri()) {
			dontMove = true;
		}

		// check relax and excitement
		checkExciteAndRelax();

		if (staying) {
			staycount += TICK;
			if (staycount > stayTime) {
				staycount = 0;
				staying = false;
			}
			else {
				dontMove = true;
			}
		}

		// move to destination
		// if there is no destination, walking randomly.
		moveBody(dontMove);

		checkMessage();

		return retval;
	}
}
