import java.util.Random;

public class Body extends Obj {
	// public variables	
	public static final int MARISA = 1;
	public static final int REIMU = 2;
	public static final int EATAMOUNT[] = {100*4, 100*6, 100*8};
	public static final int	BABY = 0;
	public static final int CHILD = 1;
	public static final int ADULT = 2;
	public static final int LEFT = 0;
	public static final int RIGHT = 1;
	
	// private variables
	private static final int HUNGRYLIMIT[] = {100*24, 100*24*3, 100*24*7};
	private static final int SHITLIMIT[] = {100*8, 100*12, 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*5;	
	private static final int HUNGRYNOT = 0;
	private static final int HUNGRYMUCH = 1;	
	private static final int DAMAGENOT = 0;
	private static final int DAMAGEMUCH = 1;
	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;
	
	protected static final int JAPANESE = 0;
	protected static final int ENGLISH = 1;

	private int yukkuriType;

	protected int ageState;			//BABY, CHILD, ADULT
	protected String messageBuf;	
	protected int destX = -1, destY = -1;		//destination
	protected int damage = 0;					//counter indicating damage
	protected int damageState = DAMAGENOT;
	protected int hungry = 0;					//counter indicating how hungry
	protected int hungryState = HUNGRYNOT;
	protected int shit = 0;
	protected int noDamagePeriod = 0;
	protected int noHungryPeriod = 0;
	protected boolean hasAccessory = true;	//true if having accessory
	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 int direction = RIGHT;				// direction of body
	protected int messageCount = 0;
	protected boolean staying = false;
	protected int staycount = 0;
	protected int stayTime = STAYLIMIT;
	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 int language = JAPANESE;
	protected int falldownDamage = 0;
	
	// private methods
	private int checkAageState() {
		int state;
		if (age < BABYLIMIT) {
			state = BABY;
		} else if (age < CHILDLIMIT) {
			state = CHILD;
		} else {
			state = ADULT;
		}
		return state;
	}
	
	private int checkHungryState() {
		int state;
		if (hungry < HUNGRYLIMIT[ageState]/4) {
			state = HUNGRYNOT;
		} else {
			state = HUNGRYMUCH;
		}
		return state;
	}
	
	private int checkDamageState() {
		int state;
		if (damage < DAMAGELIMIT[ageState]/2) {
			state = DAMAGENOT;
		} else {
			state = DAMAGEMUCH;
		}
		return state;
	}
	
	private void checkHungry() {
		if (excitement == true || hasBaby == true) {
			hungry += TICK*2;
		} else {
			hungry += TICK;
		}
		if (hungry > HUNGRYLIMIT[ageState]) {
			hungry = HUNGRYLIMIT[ageState];
		}
		if (hungryState == HUNGRYNOT && checkHungryState() == HUNGRYNOT) {
			noHungryPeriod += TICK;
		} else {
			noHungryPeriod = 0;
		}
		hungryState = checkHungryState();
	}

	private void checkDamage() {
		if (hungryState == HUNGRYNOT) {
			damage -= TICK;
		}
		else if (hungry >= HUNGRYLIMIT[ageState]) {
			damage += TICK*2;
		}
		if (damage < 0) {
			damage = 0;
		} else if (damage > DAMAGELIMIT[ageState]) {
			dead = true;
		}
		if (damageState == DAMAGENOT && checkDamageState() == DAMAGENOT) {
			noDamagePeriod += TICK;
		} else {
			noDamagePeriod = 0;
		}
		damageState = checkDamageState();
	}
	
	private boolean checkShit() {
		boolean cantMove = false;
		if (hungryState == HUNGRYNOT) {
			shit += TICK*2;
		} else {
			shit += TICK;
		}
		if (shit > SHITLIMIT[ageState] - TICK*100) {
			cantMove = true;
			shitting = true;
			showShit();
		}
		if (shit > SHITLIMIT[ageState]) {
			shitting = false;
			shit = 0;
			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;
				showBreed2();
			}
		}
		return cantMove;
	}
	
	private boolean checkSleep() {
		if (wakeUpTime + ACTIVEPERIOD < age && !excitement && relax && !scare) {
			toFood = false;
			sleeping = true;
			angry = false;
			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) {
			if (excitement == false && relax == false) {
				int r = rnd.nextInt(24);
				if (ageState == ADULT && hasBaby == false && r == 0) {
					excitement = true;
					relax = false;
					showExcite();
				}
				else {
					excitement = false;
					relax = true;
					angry = false;
					scare = false;
					showRelax();
				}
			} else {
				excite += TICK;
				if (excite > EXCITEPERIOD) {
					excite = 0;
					excitement = false;
					relax = false;
				}
			}
		}
		if (hungryState != HUNGRYNOT || damageState != DAMAGENOT || !hasAccessory) {
			relax = false;
			wakeup();
		}
	}
		
	private void checkMessage() {
		if (--messageCount <= 0) {
			messageCount = 0;
			messageBuf = null;
			furifuri = false;
			strike = false;
			eating = false;
			peropero = false;
			sukkiri = false;
		}
		if (dead) {
			showDead();
		} 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(15) == 0) {
					showHungry();
				}
			}
			else if (getZ() != 0) {
				showFlying();
			}
		}
	}
	
	protected void stay() {
		staying = true;
		stayTime = STAYLIMIT;
	}
	
	protected void stay(int time) {
		staying = true;
		stayTime = time;
	}
	
	private void moveBody(boolean dontMove) {
		if (grabbed) {
			// if grabbed, cannot move.
			falldownDamage = 0;
			return;
		}
		if (z != 0) {
			// if falling down, cannot move to xy axis
			z -= 2;
			falldownDamage += 2;
			if (z <= 0) {
				z = 0;
				strike(falldownDamage*100*24*4/Box.maxZ);
			}
			return;
		}
		if (dontMove) {
			return;
		}
		
		// moving
		int sameDest = 30 * STEP[ageState];
		int step;
		if (hasBaby || hungryState == HUNGRYMUCH || damageState == DAMAGEMUCH) {
			step = STEP[ageState]/2;
		} else {
			step = STEP[ageState];
		}
		if (step == 0) {
			step = 1;
		}
		int freq = STEP[ADULT]/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: {
					if (r == 0) {
						dirX = 1;
					} else {
						dirX = -1;
					}
					break;
				}
				case 1: {
					if (r == 0) {
						dirX = 0;
					}
					break;
				}
				case -1: {
					if (r == 0) {
						dirX = 0;
					}
					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: {
					if (r == 0) {
						dirY = 1;
					} else {
						dirY = -1;
					}
					break;
				}
				case 1: {
					if (r == 0) {
						dirY = 0;
					}
					break;
				}
				case -1: {
					if (r == 0) {
						dirY = 0;
					}
					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 = LEFT;
		} else if (dirX == 1) {
			direction = RIGHT;
		}
	}
	
	protected void setMessage(String message) {
		if (staying == true && messageBuf != null) {
			return;
		}
		messageCount = HOLDMESSAGE;
		messageBuf = message;
	}
	protected void setMessage(String message, int count) {
		if (staying == true && messageBuf != null) {
			return;
		}
		messageCount = count;
		messageBuf = message;
	}

	protected void showWantFood() {
		System.out.println("Uknown type");
	}
	
	public void showNoFood() {
		if (toFood == false) {
			return;
		}
		toFood = false;
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showExcite() {
		if (excitement) {
			System.out.println("Uknown type");
		}
	}

	protected void showRelax() {
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showWakeup() {
		System.out.println("Uknown type");
		stay();
	}

	protected void showSleep() {
		if (sleep != 0) {
			System.out.println("Uknown type");
		}		
	}
	
	protected void showScream() {
		staying = false;
		strike = true;
		System.out.println("Uknown type");
		stay();
	}

	protected void showScare() {
		System.out.println("Unknow type");
	}
	
	protected void showDieing() {
		staying = false;
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showDead() {
		System.out.println("Uknown type");		
	}
	
	protected void showEating() {
		eating = true;
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showFull() {
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showHealing() {
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showSukkiri() {
		System.out.println("Uknown type");
		sukkiri = true;
		stay(30);
	}
	
	protected void showBirth() {
		System.out.println("Uknown type");
		stay(30);
	}

	protected void showShit() {
		System.out.println("Uknown type");
	}
	
	protected void showShit2() {
		System.out.println("Uknown type");
		furifuri = true;
		stay();
	}
	
	protected void showSurisuri() {
		System.out.println("Uknown type");
		stay();
	}

	protected void showPeroPero() {
		peropero = true;
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showBreed() {
		System.out.println("Uknown type");
	}
	
	protected void showBreed2() {
		messageBuf = null;
		System.out.println("Uknown type");
		furifuri = true;
		stay();
	}
	
	public void showHateShit() {
		System.out.println("Uknown type");
	}
	
	protected void showHungry() {
		System.out.println("Uknown type");
		stay();
	}
	
	protected void showNoAccessory() {
		System.out.println("Uknown type");
	}
	
	public void showHateYukkuri() {
		System.out.println("Uknown type");
	}
	
	protected void showFlying() {
		System.out.println("Uknown type");
	}

	// public methods
	public Body(int initX, int initY, int initZ, int initType, int initAgeState, int language) {
		objType = YUKKURI;
		x = initX;
		y = initY;
		z = initZ;
		setLanguage(language);
		yukkuriType = initType;
		switch (initAgeState) {
		case BABY:
			age = 0;
			showBirth();
			break;
		case CHILD:
			age = BABYLIMIT;
			break;
		case ADULT:
		default:
			age = CHILDLIMIT;
			break;
		}
		age += rnd.nextInt(100);
		ageState = checkAageState();
		shit = rnd.nextInt(SHITLIMIT[ageState]);
		wakeUpTime = age;
		removed = false;
	}
	
	public int getBodyType() {
		return yukkuriType;
	}
	
	public int getBabyType() {
		return babyType;
	}
	
	public int getAgeState() {
		return ageState;
	}
	
	public long getAge() {
		return age;
	}
	
	public int getEatAmount() {
		return EATAMOUNT[ageState];
	}
	
	public int getStep() {
		return STEP[ageState];
	}
	
	public String getMessage() {
		return messageBuf;
	}
	
	public int getDirection() {
		return direction;
	}
	
	public boolean isDead() {
		return dead;
	}
	
	public boolean isAdult() {
		if (ageState == ADULT) {
			return true;
		}
		else {
			return false;
		}
	}
	
	public boolean isSleeping() {
		if (dead) {
			return false;
		}
		return sleeping;
	}
	
	public boolean isHungry() {
		if (dead) {
			return false;
		}
		if (hungry >= HUNGRYLIMIT[ageState]/8) {
			return true;
		}
		return false;
	}
	
	public boolean isShitting() {
		if (dead) {
			return false;
		}
		return shitting;
	}
	
	public boolean isExciting() {
		if (dead) {
			return false;
		}
		return excitement;
	}
	
	public boolean isAngry() {
		if (dead) {
			return false;
		}
		return angry;
	}
	
	public boolean isScare() {
		if (dead) {
			return false;
		}
		return scare;
	}
	
	public boolean isFurifuri() {
		if (dead) {
			return false;
		}
		return (furifuri);
	}
	
	public boolean isStrike() {
		if (dead) {
			return false;
		}
		return strike;
	}
	
	public boolean isDamaged() {
		if (checkDamageState() == DAMAGEMUCH) {
			return true;
		}
		return false;
	}
	
	public boolean isBirth() {
		if (dead) {
			return false;
		}
		if (birth) {
			return true;
		}
		return false;
	}
	
	public boolean isAccessory() {
		return hasAccessory;
	}
	
	public boolean isEating() {
		if (dead) {
			return false;
		}
		return eating;
	}
	
	public boolean isPeroPero() {
		if (dead) {
			return false;
		}
		return peropero;
	}
	
	public boolean isSukkiri() {
		if (dead) {
			return false;
		}
		return sukkiri;
	}
		
	public void doSukkiri(Body partner) {
		if (dead) {
			return;
		}
		toSukkiri = false;
		// show message
		showSukkiri();
		partner.showSukkiri();
		// change state
		excitement = false;
		relax = false;
		angry = false;
		noHungryPeriod = 0;
		hungry += 100*12;
		hungryState = checkHungryState();
		//change partner state
		partner.hasBaby = true;
		if (yukkuriType == partner.yukkuriType) {
			partner.babyType = yukkuriType;
		} else if (rnd.nextInt(2) == 0) {
			partner.babyType = yukkuriType;
		} else {
			partner.babyType = partner.yukkuriType;
		}
		partner.excitement = false;
		partner.relax = false;
		partner.angry = false;
		partner.noHungryPeriod = 0;
		partner.hungry += 100*24;
		partner.hungryState = partner.checkHungryState();
	}
	
	public void forceSukkiri() {
		if (dead || !hasAccessory) {
			return;
		}
		if (hasBaby == false) {
			excitement = true;
			showExcite();
		}
		stay();
	}
	
	public void doSurisuri(Body partner) {
		if (dead) {
			return;
		}
		if (ageState == ADULT && partner.ageState != ADULT) {
			showPeroPero();
		}
		else {
			showSurisuri();
		}
		angry = false;
		partner.angry = false;
		scare = false;
		partner.scare = false;
		//partner.stay();
	}
	
	public void moveTo(int toX, int toY) {
		if (dead) {
			return;
		}
		destX = toX;
		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 = RIGHT;
		}
		else if (toX < x) {
			direction = LEFT;
		}
		stay();
	}
	
	public void eatFood(int amount) {
		if (dead) {
			return;
		}
		toFood = false;
		if (!isHungry()) {
			showFull();
			return;
		}
		hungry -= amount;
		if (hungry < 0) {
			hungry = 0;
		}
		showEating();
		hungryState = checkHungryState();
		angry = false;
	}
	
	public void strike(int amount) {
		if (dead) {
			return;
		}
		damage += amount;
		showScream();
		damageState = checkDamageState();
		setAngry();
		wakeup();
	}
	
	public void strikeByNeedle() {
		strike(NEEDLE);
	}

	public void strikeByHammer() {
		if (dead) {
			return;
		}
		strike(HAMMER);
		if (damage > DAMAGELIMIT[ageState]) {
			showDieing();
		}
	}
	
	public void takeAccessory() {
		hasAccessory = false;
	}
	
	public void giveAccessory() {
		hasAccessory = true;
	}
	
	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;
		toFood = 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;
	}
	
	public void setAngry() {
		if (damageState == DAMAGENOT && hasAccessory) {
			angry = true;
		}
		else {
			angry = false;
		}
	}
	
	public void setLanguage(int lang) {
		language = lang;
	}

	// calling every tick
	public int clockTick() {
		int retval = DONOTHING;
		
		// if removed, remove body
		if (removed) {
			return REMOVED;
		}
		
		// if dead, do nothing.
		if (dead) {
			checkMessage();
			return DEAD;
		}
		
		// check age
		age += TICK;
		if (age > LIFELIMIT) {
			dead = true;
		}
		ageState = checkAageState();
		
		// check hungry
		checkHungry();
		
		// check damage
		checkDamage();
		
		// check events
		boolean dontMove = false;
		
		// check shit
		int oldShit = shit;
		if (checkShit()) {
			dontMove = true;
		}
		if (oldShit != 0 && shit == 0) {
			retval = DOSHIT;
		}
		
		// check pregnant
		boolean oldHasBaby = hasBaby;
		if (checkPregnant()) {
			dontMove = true;
		}
		if (oldHasBaby == true && hasBaby == false) {
			retval = BIRTHBABY;
		}
		
		// check sleep
		if (checkSleep()) {
			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;
	}
}
