/*
 * Created on Nov 22, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package jone;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.Random;
/**
 * @author brian
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class Wolf extends Animal {

	private LinkedList pastFitnesses;
	static double radius = .05;
	static int inputsize = 8;
	static int outputsize = 2;
	static int numHidden = 8;
	static int [] numNeuronsInEachLayer = new int[]{ inputsize,numHidden,outputsize};
	static int [] neuronTypesForLayers = new int [] {NeuralNetTBox.tansig,NeuralNetTBox.tansig,NeuralNetTBox.tansig};
	static char [] viewChars = new char[] {'W'};
	int numSpawnsSinceStartOfEpoch;
	Point2D.Double lastPosition;
	/* (non-Javadoc)
	 * @see jone.Animal#mateWith(jone.Animal)
	 */
	public Wolf()
	{
		this.brain = new Brain(numNeuronsInEachLayer,neuronTypesForLayers,
				inputsize, outputsize);
		this.age = 0;
		
	}
	
	public Wolf(Brain b)
	{
		this.brain = b;
		this.age = 0;
		
	}
	public Animal mateWith(Animal animal) {
		// TODO Auto-generated method stub
		Wolf partner = (Wolf) animal;
		Random rand = RandomSingleton.getDefaultInstance();
		Wolf wolf = new Wolf(Brain.splice(rand.nextDouble(),this.brain,partner.brain));
		
		return wolf;
	}

	/* (non-Javadoc)
	 * @see jone.Animal#mutate()
	 */
	public Animal mutate() {
		// TODO Auto-generated method stub
		return new Wolf(this.brain.mutate(.5)); //new Wolf(this.brain.mutate(.5));
	}

	/* (non-Javadoc)
	 * @see jone.Animal#computeFitness(double, double, double)
	 */
	public int computeFitness(double birthCost, double overConsumptionPenalty, double foodBonus) {
		// TODO Auto-generated method stub
		int fitness = (int)( birthCost * this.numSpawnsSinceStartOfEpoch + 
				this.foodConsumed * foodBonus 
		/* + this.overEatPenalty*overConsumptionPenalty*/);
		if( this.pastFitnesses == null) {
			this.pastFitnesses = new LinkedList();
		}
		this.pastFitnesses.add(new Integer(fitness));
		double sum = 0;
		for(int i = 0; i < this.pastFitnesses.size(); i++) {
			sum += ((Integer) this.pastFitnesses.get(i)).intValue();
		}
		this.fitness = (int) Math.round(sum/(1.0*this.pastFitnesses.size()));
		if(Arena.debug)
		{
			Arena.logger.debug("wolf's age is " + this.age + " fitness is " + this.fitness );
		}
		return this.fitness;
	}

	/* (non-Javadoc)
	 * @see jone.Animal#getFitness()
	 */
	public int getFitness() {
		// TODO Auto-generated method stub
		return this.fitness;
	}

	/* (non-Javadoc)
	 * @see jone.Animal#eat(jone.Critter[])
	 */
	public void eat(Critter[] critters) {
		/* in order to create grazing pressure to increase the fitness of 
		 * individuals that eat alone, i need to use the number of sheep around
		 * to affect the consumption rate, 
		 */
		Sheep [] food = (Sheep []) critters;
		for(int i = 0; i < food.length; i++)
		{
			double sheepfood = food[i].underAttack(this);
			
			this.foodInStomach += sheepfood;
			this.foodConsumed += sheepfood;
			/*if(this.foodInStomach > this.optimalFoodCapacity)
			{
				this.overEatPenalty += sheepfood;
			}*/
			if(Arena.debug && sheepfood > 0)
			{
				Arena.logger.debug("wolf is eating " + sheepfood + " sheepfood");
			}
			if(sheepfood > 0) /* eat only one sheep at a time */
				return;
		}
	}

	/* (non-Javadoc)
	 * @see jone.Animal#think(double[])
	 */
	public void think(double[] state) {
		// TODO Auto-generated method stub
		this.direction = this.brain.think(state);
	}

	/* (non-Javadoc)
	 * @see jone.Critter#draw(java.awt.Graphics)
	 */
	public void draw(Graphics g) {
		ArenaPanel ap = ArenaPanel.getInstance();
		
		Graphics2D g2d = (Graphics2D) g;
		if(g == null)
			return;
	    // pull info out of graphics to know the start position of drawing
		int x = (int) (this.position.x * ap.width() - 9*(Wolf.radius/.05)); //+ ap.startX();
		int y = (int)(this.position.y * (ap.height()) -  1*(Wolf.radius/.05));; //+ ap.startY();
		if(Arena.debug)
		{
			//Arena.logger.debug("printing sheep at x = " + x + " y = " + y);
		}
		g2d.setPaint(Color.red);
		String sage = this.age + "";
		g2d.drawChars(sage.toCharArray(),0,1,x,y); // pull the array out
			
		x = (int) ((this.position.x - GrassPatch.xd*Sheep.radius) * ap.width()); //+ ap.startX();
		y = (int) ((this.position.y - GrassPatch.yd*Sheep.radius) * (ap.height()));
		int r = (int) (Sheep.radius * ap.width()); // why doesnt adding to the height do ne thing
		if(Arena.debug)
		{
			//Arena.logger.debug("printing sheep oval at x = " + x + " y = " + y);
		}
		
		 
		g2d.setStroke(new BasicStroke(2));
		g2d.drawOval(x,y,r,r);
		if(Arena.debug)
		{
		}
	}
	

	/* (non-Javadoc)
	 * @see jone.Critter#respawn()
	 */
	public void respawn(Critter [] critters) {

		super.respawn(critters);
		this.directionMagnitude = .025;
		
		this.numSpawnsSinceStartOfEpoch = 0;
		this.lastPosition = this.position;
		
		this.foodConsumed = 0;
		this.foodInStomach = 0;
		this.overEatPenalty = 0;
		this.fitness = 0;
		this.optimalFoodCapacity = 10;
		this.age++;
	}

	public double getRadius()
	{
		return Wolf.radius;
	}
	
	/* (non-Javadoc)
	 * @see jone.Critter#getWeight()
	 */
	public double getWeight() {
		// TODO Auto-generated method stub
		return this.foodInStomach + weight;
	}
	final static double weight = 40;
	/* (non-Javadoc)
	 * @see jone.Animal#move()
	 */
	public void move() {
		// TODO Auto-generated method stub
		double delta = this.directionMagnitude/Math.sqrt(this.direction.x * this.direction.x + 
				this.direction.y * this.direction.y);
		double newx = /* delta * */ this.direction.x +  this.position.x;
		if(newx < 0 || newx > 1)
			newx = this.position.x;
		double newy = /* delta * */ this.direction.y + this.position.y;
		if(newy < 0 || newy > 1)
			newy = this.position.y;
		this.position = new Point2D.Double(newx,newy );
	}

}
