package alive;

/**
 * <ALIVE Project>
 * Experiment 1 Agent tipe 1
 * TestAgent1 (heatseeker)
 * is a simple track and move agent
 * in other words, it just look for an object and hunt it out
 * one single 3 input 1 output neuron tells it what to do
 */

import javax.vecmath.*;
import javax.media.j3d.*;

public class xp1_Hunter extends Agent{
  public Actor_Sphere actor = null;
  public int generation=1;
  public double metabolism, energy = 30, scale = .3, size = .001, bite, distance;
  Vector3d force = new Vector3d(), dis = new Vector3d(), tmp = new Vector3d();

// ----------------------------- Constructors ------------------------------
  public xp1_Hunter(Environment env) { // Constructor
    super.configureToEnvironment(env);
    param =  new double[11];
    paramName = new String[11];
    set(0, "reproduction", 160);
    set(1, "starve", 2);
    set(2, "sensibility", 10);
    set(3, "strenght", 50);
    set(9,"resistence",0);
    set(4, "stamina", 20);
    set(5, "temperature", 20);
    set(10, "tolerance", 20);
    set(6, "w1", rnd(-1,1));
    set(7, "w2", rnd(-1,1));
    set(8, "w3", rnd(-1,1));
    env.adjust("hunters",1);
    message("O"); // Agent Created char code
    super.makeAlive();
  }
  public xp1_Hunter(xp1_Hunter parent) { // Constructor
    super.configureToEnvironment(parent.env);
    param =  new double[11];
    paramName = new String[11];
    energy = parent.energy;
    generation = parent.generation + 1;
    for (int i = 0; i < param.length; i++) { // Variational operator
      if (probability(.1)) // 10% chance of
        set(i, parent.paramName[i], parent.param[i] * rnd(.9, 1.1)); // 10% variation
      else
        set(i, parent.paramName[i], parent.param[i]);
    }
    for (int i = 6; i < 10; i++) // Mutational operator
      if (probability(.05)) // 5% chance of mutation
        set(i, parent.paramName[i], rnd(-1, 1));
    Vector3d inc = generateRandomVector();
    inc.scale(.01);
    position.set(parent.position);
    position.add(inc);
    speed.set(inc);
    inc.negate();
    parent.speed.set(inc);
    env.adjust("hunters",1);
    message("o"); // Agent Created char code
    super.makeAlive();
  }

// ----------------------------- User methods ------------------------------
  public void play() {  //   ******   Define Hunter Acctions
    force.set(0,0,0);
    color.set( (float) (.12 + get("w1") / 8),
               (float) (.12 + get("w2") / 8),
               (float) (.12 + get("w3") / 8));
    for (int i = 0; i < env.agents.size() ; i++) {
      Agent ag = env.getAgent(i);
      if (ag!=null) {
        if (ag.id != this.id) { // Hunt
          dis.set(ag.position);
          dis.sub(this.position);
          distance = dis.length();
          dis.normalize();
          if (distance < size) { // in range
            if (distance < size / 1.2) { // too close 20% penetration: repel
              tmp.set(dis);
              tmp.scale(.1*scale);
              ag.speed.add(tmp);
              tmp.negate();
              position.add(tmp);
              speed.add(tmp);
            }
            if (get("strenght") > ag.get("strenght")) {  // Contact
              // Eat target
              bite = .01*signal(ag.get("energy"))*Math.abs(get("strenght")-ag.get("strenght"));
              if (bite<0) bite = bite - get("resistance") * bite;
              ag.adjust("energy", -1 * bite);
              energy = energy + .9 * bite;
            }
          }
          else if (distance < get("sensibility")) {
            //Vision radius interception
            tmp.set(dis.x, dis.y, dis.z);
            dis.scale( (get("w1") * ag.color.x +
                        get("w2") * ag.color.y +
                        get("w3") * ag.color.z) / Math.pow(distance, 2.0));
            force.add(dis);
          }
        }
      }
    }
    if (force.length() < .001) {
      force.set(acceleration);
      if (probability(.1)) { // 10%
        tmp.set(rnd( -1, 1), rnd( -1, 1), rnd( -1, 1));
        force.add(tmp);
      }
    }
    if (force.length() > .05) force.clamp(-0.05,0.05);
    force.scale(get("stamina")*metabolism/100);
    energy = energy - metabolism*force.length()/get("stamina");
    acceleration.set(force);
    if (force.length()==0) force.set(0,-1,0);
    direction.set(force); direction.normalize();
  }

  public synchronized void reproduce() {  // Agent reproduction function
    if (env.nAgents<env.config.POP_LIMIT) {
      energy = energy/2;
      env.addAgent(new xp1_Hunter(this));
    }
  }

// --------------------------- Overrided methods --------------------------
  public void behavior() { //  ***  Main cycle
    //message("Hunter Agent (" + id +") running\n");
    metabolism = 1 - Math.abs(get("temperature")-env.get("temperature"))
                    /get("tolerance");
    energy = energy + .01 * (metabolism - 1); // Round loss
    size = scale*Math.pow(3*energy/12.57, 1/3.0);
    play();   // Look, decide, eat
    if (energy>param[0]) reproduce();
    if (energy<param[1]) die();
  }

  public synchronized void die() {    // Destroy created objects
    env.adjust("hunters",-1);
    message("X");
    super.die();
  }

// ------------------------- Actor Manipulation -------------------------
  public Actor createActor() {
    this.actor = new Actor_Sphere();
    actor.setTexture(env.config.codeBase + "textures/eye2.gif");
    actor.setTransparency(.2f);
    return actor;
  }

  public void updateActor() {
    actor.set(position, direction, size);
  }

// ----------------------- Overrided access methods -----------------------
  public double get(String s) {
    if (s.equals("energy")) return energy;
    else return super.get(s);
  }

  public synchronized boolean adjust(String s, double value) {
    if (s.equals("energy")) { energy = energy + value; return true; }
    else return super.adjust(s, value);
  }
  public synchronized boolean set(String s, double value) {
    if (s.equals("energy")) { energy = value; return true; }
    else return super.set(s, value);
  }
  public String getInfo() {
    String s = new String();
    s = id + " Hunter Agent\ngeneration = " + generation;
    s = s + "\nenergy = " + round(energy,5) + "\n";
    s = s + "metabolism = " + round(metabolism,5);
    s = s + super.getInfo();
    return s;
  }
}
