/*

	Our Creature class describes each of our boids running around
	the screen, and is a composite of our Particle and Boid classes.
	Each of our Creatures is guided by its DNA, and the fitness of each
	Creature is a combination of what order it reached the target in (1st,
	2nd, 3rd, etc) and how close it got to the target.
*/

public class Creature {

	// Our vectors for controlling movement
	Vector3D loc;
	Vector3D vel;
	Vector3D accel;
	
	// Size of our Creature
	float r;
	
	// Keep track of the fitness score once we calculate it
	float fitness;
	
	// Our DNA, guides our movement
	DNA genes;
	
	// Active is true if we are moving, false if we've hit a barrier or wall
	boolean active;
	
	// What order did we finish in
	int finishOrder;

	public Creature(Vector3D loc_, DNA genes_, int finishOrder_) {
	
		// setup our movement
		accel = new Vector3D(0.0, 0.0, 0.0);
		vel = new Vector3D(0.0, 0.0, 0.0);
		loc = loc_.copy();
		
		// Set a default size
		r = 2.0f;
		
		// Store our genes
		genes = genes_;
		
		// Our activity state and finish order
		finishOrder = finishOrder_;
		active = true;
	}
	
	/*
		Here we calculate our fitness. We need the following information
		for our equation:
			distance -> our distance from the target
			finishOrder -> what order we finished in
		The fitness function uses the following formula to weight the
		fitness properties:
			fitness(distance, order) = (1.0 / order^1.5) * (1.0 / distance^6)
			
		This means that the order will have a much higher impact on our fitness,
		but the closer we get to the target, the exponentially higher our fitness
		calculation will be.
	*/
	void calcFitness() {
	
		// get our distance to the target
		float dist = Vector3D.distance(loc, target);
		
		// if we are within the area of the target, the distance
		// to the target should be set to 1.0
		if (dist < (diam / 2)) {
			dist = 1.0f;
		}
		
		fitness = (1.0f / pow(finishOrder, 1.5f)) * (1.0f / pow(dist, 6));
	}
	
	void setFinishOrder(int f) {
		finishOrder = f;
	}
	
	/*
		Our simulate method needs to take into account
		the possibility of running into obstacles
	*/
	void simulate(ArrayList obst) {
	
		// only keep simulating if we haven't yet run into anything
		if (active) {
		
			update();
			
			// check for obstacles and walls
			if ( borders() || obstacles(obst) ) {
				active = false;
			}
		}
		
		render();
	}
	
	/*
		Check to see if we've hit the edge of the screen
	*/
	boolean borders() {
	
		if ( (loc.getX() < 0) || (loc.getY() < 0) || (loc.getX() > width) || (loc.getY() > height) ) {
			return true;
		} else {
			return false;
		}
	}
	
	/*
		Figure out if we've made it to the target yet
	*/
	boolean isFinished() {
	
		// get our distance to the target
		float dist = Vector3D.distance(loc, target);
		
		if (dist < diam / 2) {
			active = false;
			return true;
		} else {
			return false;
		}
	}
	
	/*
		See if we've hit an obstacle
	*/
	boolean obstacles(ArrayList obst) {
	
		// Check each obstacle, and see if we've instersected it
		for (int i = 0; i < obst.size(); i++) {
			Obstacle o = (Obstacle)obst.get(i);
			if (o.contains(loc)) {
				return true;
			}	
		}
		
		return false;
	}
	
	void update() {
	
		// only run if we haven't yet finished
		if (!isFinished()) {
		
			// figure out where we are in our screen grid
			int gx = (int)loc.getX() / gridscale;
			int gy = (int)loc.getY() / gridscale;
			
			// make sure our numbers are within our grid
			gx = constrain(gx, 0, width / gridscale - 1);
			gy = constrain(gy, 0, height / gridscale - 1);
			
			// get the steering vector from our DNA
			accel = accel.add(genes.getGene(gx + gy * width / gridscale));
			
			// do our movement
			accel = accel.multiply(maxforce);
			vel = vel.add(accel);
			vel.limit(maxspeed);
			loc = loc.add(vel);
			accel.setXYZ(0.0, 0.0, 0.0);
		}
	}
	
	void render() {
	
		// draw our Creature triangle on screen
		float theta = vel.heading2D() + radians(90);
		
		// set our fill based upon our status
		if (active) {
			fill(255);
		} else {
			fill(255, 25);
		}
		noStroke();
		
		// Move into position and draw
		pushMatrix();
		translate(loc.getX(), loc.getY());
		rotate(theta);
		beginShape(TRIANGLES);
		vertex(0, -r * 2);
		vertex(-r, r * 2);
		vertex(r, r * 2);
		endShape();
		popMatrix();
	}
	
	float getFitness() {
		return fitness;
	}
	
	DNA getGenes() {
		return genes;
	}
	
	boolean isActive() {
		return active;
	}
	
}
