//***********************************************************
//*
//* File:           Group4Player1.java
//* Author:         Adam, Han, Lyuba
//*
//***********************************************************
package Organisms2.g4;

import Organisms2.*;
import java.util.*;
import java.awt.Color;
import java.io.*;

public class Group4Player3 implements IFCPlayer {

	Organisms2 engine;
	static final String NAME = "Tequila Sunset";
	static final Color COLOR = new Color(1.0f,1.0f,0.0f);
	int state;
	Random rand;
	DNA myDNA;
	int age;
	int numChildren;
	int foodEaten;
	double probability; //probability of gaining energy on a round
	int energyLastRound;
	int repRate;
	int walkRate;
	
	public void register(Organisms2 amoeba, int key) throws Exception
	{
		try
		{
			engine = amoeba;
			rand = new Random();
			state = rand.nextInt(256);
			
			age = 0;		
			numChildren = 0;
			foodEaten = 0;
			
			int adultAge, oldAge;
			if(key == -1)   //first cell
			{
				if(engine.u() <= 2 * engine.v()) //500
				{
					adultAge = 500;
					oldAge = adultAge + 25;     
				}
				else if(engine.u() <= 5 * engine.v())    // 150
				{
					adultAge = 250; 			
					oldAge = adultAge + 40;
				}
				else if(engine.u() <= 8 * engine.v())   // 75
				{
					adultAge = 125;
					oldAge = adultAge + 60;
				}
				else if(engine.u() <= 10 * engine.v()) //50
				{
					adultAge = 100;
					oldAge = adultAge + 80;			
				}
				else
				{
					adultAge = 50;
					oldAge = adultAge + 50;
				}

				myDNA = new DNA(adultAge, oldAge);
			}
			
			else
			{
				String keyString = new Integer(key).toString();
				int adultLength = Integer.parseInt(keyString.substring(0, 1));
				int oldLength = Integer.parseInt(keyString.substring(1, 2));
				String adultString = keyString.substring(2, 2 + adultLength);
				String oldString = keyString.substring(adultLength + 2, adultLength + 2 + oldLength);
				myDNA = new DNA(Integer.parseInt(adultString), Integer.parseInt(oldString)); 
				probability = .5;
			}
			setRepRate();
			walkRate = 40;  //5
			//System.out.println("repate " + repRate);
		}
		catch(Exception e)
		{
			System.err.println("Tequila Error in register!");
			handleException(e);
		}
	}

	public String name() throws Exception {
		return NAME;
	}

	public Color color() throws Exception {
		return COLOR;
	}

	public boolean interactive() throws Exception {
		return false;
	}

	public int externalState() throws Exception {
		return state;
	}

	public void mutations()
	{
		//divide foodEaten by age to get the probability of obtaining food
		//System.out.println("food" + foodEaten + " age " + age);
		double newProbability = (double)((double)foodEaten/age);
		//System.err.println("Energy per round: " + newProbability);
		if(probability < (double)engine.v()/8.0)
		{
			myDNA.adultAge = 600;					//500, 25
			myDNA.oldAge = myDNA.adultAge + 10;		
			walkRate = 80;		
		}
		if(probability < (double)engine.v()/5.0)   //v/40
		{
			myDNA.adultAge = 500;					//500, 25
			myDNA.oldAge = myDNA.adultAge + 20;		
			walkRate = 60;
		}
		else if(probability < (double)engine.v()/2.0) //v/20
		{
			myDNA.adultAge = 150;     //150, 40
			myDNA.oldAge = myDNA.adultAge + 40;		
			walkRate = 40;
		}
		else if(probability < (double)engine.v())  //v/10
		{
			myDNA.adultAge = 75;    //75, 60
			myDNA.oldAge = myDNA.adultAge + 60;	
			walkRate = 20;	
		}
		else if(probability < 2 * (double)engine.v())  //v/5
		{
			myDNA.adultAge = 50;   //50, 80
			myDNA.oldAge = myDNA.adultAge + 80;	
			walkRate = 10;	
		}
		else
		{
			myDNA.adultAge = 25;   //25, 100
			myDNA.oldAge = myDNA.adultAge + 100;
			walkRate = 5;		
		}
		//System.err.println("walk rate " + walkRate);
		probability = newProbability;
		setRepRate();
	}

	public void setRepRate()
	{
			
			//set the reproductive rate
			//the higher the adult age, the scarser the food
			if(myDNA.adultAge <= 25)
			{
				repRate = 40;        //20
			}
			else if(myDNA.adultAge <= 50)
			{
				repRate = 60;     //40
			}
			else if(myDNA.adultAge <= 75)
			{
				repRate = 80;     //60
			}
			else if(myDNA.adultAge <= 300)
			{
				repRate = 100;    //80
			}
			else if(myDNA.adultAge <= 500)
			{
				repRate = 400;		//400	
			}
			else if(myDNA.adultAge <= 600)
			{
				repRate = 600;   //600
			}	
			else
			{
				repRate = 800;   //600
			}			
			//System.out.println("reprate" + repRate + "probability " + probability);
	}
	
	public void randomMutations()
	{
		int adultChange = new Random().nextInt(myDNA.adultAge/8);
		int gapChange = new Random().nextInt((myDNA.oldAge - myDNA.adultAge)/8);
		int upOrDown = new Random().nextInt(1);
		if(upOrDown == 1)
		{
			myDNA.adultAge += adultChange;
			myDNA.oldAge -= gapChange;
		}
		else
		{
			myDNA.adultAge -= adultChange;
			myDNA.oldAge += gapChange;
		}
		setRepRate();
	}
		
	public Move move(boolean[] foodPresent, int[] enemies, int foodLeft, int energyLeft) throws Exception {
	try
	{
		//rebirth for really, really old folks
		if(age > 1000)
		{
			age = 0;
			foodEaten = 0;
		}
		if(energyLeft >= energyLastRound)
		{
			foodEaten += energyLeft - energyLastRound;
		}
		if(age == 50)
		{
			mutations();
		}
		if(age > 50 && age%50 == 0)
		{
			mutations();
		}			

		if(age > myDNA.adultAge && age%100 == 0)
		{
			//randomMutations();
		}		
		Move myMove;
		if(age >= myDNA.oldAge)
		{			
			myMove = youth(foodPresent, enemies, foodLeft, energyLeft);

		}	
		else if(age >= myDNA.adultAge)
		{			
			myMove = adult(foodPresent, enemies, foodLeft, energyLeft);		

		}
		else
		{			
			myMove = youth(foodPresent, enemies, foodLeft, energyLeft);

		}
		age++;
		energyLastRound = energyLeft;
		return myMove;
	}
	catch(Exception e)
	{
		System.err.println("Tequila error in move!");
		handleException(e);
		return new Move(0);
	}
	}

	public boolean energyToReproduce(int energyLeft)
	{
		//see how far organism is from old age to determine how much energy needs to be left after reproduction
		//how much food will the organism need to get back to normal?
		int childEnergy = energyLeft/2 - engine.v();
		if(childEnergy >= 10 * engine.v()) //child can live to reproduce at least once
			return true;
		else 
			return false;
		
	}
	
	public int emptySquareFood(boolean[] foodPresent, int[] enemies)
	{
		int direction = 0;
		for(int i = 0; i < foodPresent.length; i++)
		{
			if(foodPresent[i] && enemies[i] == -1)
			{
				direction = i;
			}
		}
		return direction;	
	}
	
	public int emptySquare(int[] enemies)
	{
		int direction = 0;
		for(int i = 0; i < enemies.length; i++)
		{
			if(enemies[i] == -1)
			{
				direction = i;
			}
		}
		return direction;	
	}
	
	public Move youth(boolean[] foodPresent, int[] enemies, int foodLeft, int energyLeft)
	{
		try
		{
			//aggressively search for food
			int direction = 0;
			if(foodLeft <= 0 && energyLeft < engine.M() - engine.u() )
			{
				direction = emptySquareFood(foodPresent, enemies);
			}		
			if(direction == 0 && energyLeft > 5 * engine.v() && age % walkRate == 0)
				return walk(foodPresent, enemies, foodLeft, energyLeft);
			return new Move(direction);
			//move in a rectangular pattern
		}
		catch(Exception e)
		{
			System.err.println("Tequila Error in youth!");
			handleException(e);
			return null;
		}
	}
	
	public Move reproduce(boolean[] foodPresent, int[] enemies, int foodLeft, int energyLeft)
	{
		try
		{
			//System.out.println("reproducing");
			//int key = (int)(probability * 10000.0);
			String adultString = new Integer(myDNA.adultAge).toString();
			String oldString = new Integer(myDNA.oldAge).toString();
			String keyString = Integer.toString(adultString.length()) + oldString.length() + adultString + oldString; 
			//String keyString = oldString + adultString + oldString.length() + adultString.length();
			//lengths will always be one digit
			int key = Integer.parseInt(keyString);
			//System.err.println("key passed " + keyString + " " + key);			
			int squareFood = emptySquareFood(foodPresent, enemies);
			int emptySquare = emptySquare(enemies);
			if(squareFood > 0)   //if food available in the next square, reproduce into it
			{
				numChildren++;			
				return new Move(_CREPRODUCE, squareFood, key);
			}
			else if(emptySquare > 0) //if no food available, reproduce into empty square
			{
				numChildren++;
				return new Move(_CREPRODUCE, emptySquare, key);
			}
			else if(energyLeft > 5 * engine.v() && age%walkRate == 0)
				return walk(foodPresent, enemies, foodLeft, energyLeft);
			else
				return new Move(0); //stay put		
		}
		catch(Exception e)
		{
			System.err.println("Tequila error in reproduce");
			handleException(e);
			return null;
		}
	}
	
	public Move adult(boolean[] foodPresent, int[] enemies, int foodLeft, int energyLeft)
	{
	try
	{
		//reproduce every repRate rounds
		//if the board is crowded, do not reproduce
		int crowd = 0;
		for(int i = 0; i < enemies.length; i++)
		{
			if(enemies[i] >= 0)
			{
				crowd++;
			}
		}
		if(crowd > 0)
		{
			return youth(foodPresent, enemies, foodLeft, energyLeft);		
		}
		if(age % repRate != 0)
			return youth(foodPresent, enemies, foodLeft, energyLeft);
		//check if there's enough energy to reproduce
		if(energyToReproduce(energyLeft))
		{		
			//System.out.println("enough energy to reproduce");
			return reproduce(foodPresent, enemies, foodLeft, energyLeft);
		}
		//if not, call youth() to find food
		else
		{
			//System.out.println("not enough energy to reproduce");
			return youth(foodPresent, enemies, foodLeft, energyLeft);		
		}
	}
	catch(Exception e)
	{
		System.err.println("Error in adult");
		handleException(e);
		return null;
	}
	}
	
	public Move seniorCitizen(boolean[] foodPresent, int[] enemies, int foodLeft, int energyLeft)
	{
		try
		{	
		int direction = 0;	
		// stay put until you die
		return new Move(direction);
		}
		catch(Exception e)
		{
			System.err.println("Error in senior citizen");
			handleException(e);
			return null;
		}
	}
	
	
	/*Adam's code*/
	public Move walk(boolean [] foodPresent, int [] enemyPresent, 
		      int foodLeft, int energyLeft) throws Exception
	{
		int dir;
		boolean repro = false;
		int count;
		int x;
	
			count = 0;
		for (x = 1; x < 5; x++) {
		    if (enemyPresent [x] >= 0) {
			count += 1;
		    }
		}

		/* if there's nowhere to go */
		if (count == 4) {
		    return new Move (_CSTAYPUT);
		}

		/* If there's only one open path */
		if (count == 3) {
		    if (enemyPresent [_CNORTH] == -1) {
			return new Move (_CNORTH);
		    }
		    if (enemyPresent [_CSOUTH] == -1) {
			return new Move (_CSOUTH);
		    }
		    if (enemyPresent [_CEAST] == -1) {
			return new Move (_CEAST);
		    }
		    return new Move (_CWEST);
		}
		
		/* If there are two paths we can take */
		if (count == 2) {
		    dir = rand.nextInt (2);
		    if ((enemyPresent [_CNORTH] == -1) &&
			(enemyPresent [_CSOUTH] == -1)) {
			if (dir == 0) {
			    return new Move (_CNORTH);
			}
			return new Move (_CSOUTH);
		    }
		    if ((enemyPresent [_CNORTH] == -1) &&
			(enemyPresent [_CEAST] == -1)) {
			if (dir == 0) {
			    return new Move (_CNORTH);
			}
			return new Move (_CEAST);
		    }
		    if ((enemyPresent [_CNORTH] == -1) &&
			(enemyPresent [_CWEST] == -1)) {
			if (dir == 0) {
			    return new Move (_CNORTH);
			}
			return new Move (_CWEST);
		    }
		    if ((enemyPresent [_CSOUTH] == -1) &&
			(enemyPresent [_CEAST] == -1)) {
			if (dir == 0) {
			    return new Move (_CSOUTH);
			}
			return new Move (_CEAST);
		    }
		    if ((enemyPresent [_CSOUTH] == -1) &&
			(enemyPresent [_CWEST] == -1)) {
			if (dir == 0) {
			    return new Move (_CSOUTH);
			}
			return new Move (_CWEST);
		    }
		    if (dir == 0) {
			return new Move (_CEAST);
		    }
		    return new Move (_CWEST);
		}

		/* Check if we have enough food to reproduce */
		if ((energyLeft >= engine.M () * 3.0 / 4.0) &&
		    (rand.nextInt (4) == 1)){
		    repro = true;
		}



		/* We're all alone */
		if (repro && (count == 0)) {
		    dir = rand.nextInt (4);
		    if (dir == 0) {
			return new Move (_CREPRODUCE, _CNORTH, -1);
		    }
		    if (dir == 1) {
			return new Move (_CREPRODUCE, _CSOUTH, -1);
		    }
		    if (dir == 2) {
			return new Move (_CREPRODUCE, _CEAST, -1);
		    }
		    return new Move (_CREPRODUCE, _CWEST, -1);
		}
		/* Check if there's food around */
		if (foodPresent [_CNORTH]) {
		    return new Move (_CNORTH);
		}
		if (foodPresent [_CSOUTH]) {
		    return new Move (_CSOUTH);
		}
		if (foodPresent [_CEAST]) {
		    return new Move (_CEAST);
		}
		if (foodPresent [_CWEST]) {
		    return new Move (_CWEST);
		}
		return new Move (_CSTAYPUT);
		
	    }
	/*End Adam's Code*/
	
	public void handleException(Exception e)
	{
		System.err.println("Tequila Error!!");
		e.printStackTrace();
	}
	
}
