//  Individual bug
//
//  Author  Matthew Caryl
//  Created 25.2.97
//
//  Although under copywrite to the author (Matthew Caryl) this code can be copied and modified for non-commercial
//  purposes as long as any derivatives contain this condition.

package Bugs;

import java.awt.Color;
import java.lang.Math;

final class Bug {
    //
    // Private interface
    //

    static int      MAXIMUM_ENERGY = 1500;
    static int      ENERGY_PER_TICK = 1;
    static int      REPRODUCTION_AGE = 100;
    static int      REPRODUCTION_ENERGY = 1000;
    static int      INITIAL_ENERGY = 500;
    private static int      MAXIMUM_GENE_VALUE = 10;
    private static Color    NONSELECTED_COLOR = Color.magenta;
    private static Color    SELECTED_COLOR = Color.yellow;
    private static Color [] COLORS = {Color.magenta.brighter(), Color.cyan};//PAD

    private static int[]    facingXDelta = {0, 1, 1, 0, -1, -1}; // x-delta for facing
    private static int[]    facingYDelta = {1, 1, -1, -1, -1, 1}; // y-delta for facing

    private World           world;
    private int             x, y;
    private int             facing; // in range 0..5
    private int             energy;
    private int             age;
    private int 	    genlen = 7; // PAD
    private int[]           genes = new int[genlen]; //PAD
    private int[]           behaviour = new int[genlen]; //PAD
	// almost mistake: made eating behav the mark of the species
	// but only the bahav that I'm interested in
	// eventually generalize viewer to color by various features
    private Color	    color;


				// 0 = EatLess 1 = Eatmore

    //
    // Public interface
    //

	// PAD: bug from a parent
    public Bug(Bug b, int E) {
        world = b.world;
        x = b.x;
        y = b.y;
        facing = b.facing;
        energy = E;
        for (int i = genlen-1; i >= 0; i--)//PAD copy ALL genes!
            genes[i] = b.genes[i];
        mutateGenes();
        calculateBehaviour();
        }
    /*
	// PAD: bug w/o parent
    public Bug(World W, int X, int Y) {
        world = W;
        x = X;
        y = Y;
        facing = world.random().range(6);  // random facing
        energy = INITIAL_ENERGY;
        for (int i = genlen-1; i >= 0; i--)
            genes[i] = world.random().range(MAXIMUM_GENE_VALUE);
        calculateBehaviour();
        }
*/
	// PAD: bug w/o parent of a particular species
    public Bug(World W, int X, int Y) {
        world = W;
        x = X;
        y = Y;
        facing = world.random().range(6);  // random facing
        energy = INITIAL_ENERGY;
        for (int i = genlen-2; i >= 0; i--)
            genes[i] = world.random().range(MAXIMUM_GENE_VALUE);
	int speciesOffset =(x < world.width/2)?0:5 ; // left half of world = species 0
	genes[6] = world.random().range(MAXIMUM_GENE_VALUE/2) + speciesOffset;
        calculateBehaviour();
        }

    public void paint() {
   //     world.paint(x, y, COLORS[behaviour[6]]); // PAD now use color calc'd in calc_behav.
        world.paint(x, y, color);
}

    public void tick() {
        age++;
        world.erase(x, y);
        possibleTurn();
        moveForward();
        world.paint(x, y, color);
        eatAndDigest();
        possibleReproduction();
        }

    public int sqrDistance(int X, int Y) {
        return (x - X) * (x - X) + (y - Y) * (y - Y);
        }

    private void possibleTurn() {
        int r = world.random().range(behaviour[5]);
        if (r < behaviour[0])
            return;
        else if (r < behaviour[1])
            facing += 1;
        else if (r < behaviour[2])
            facing += 2;
        else if (r < behaviour[3])
            facing +=  3;
        else if (r < behaviour[4])
            facing +=  4;
        else
            facing +=  5;
        if (facing > 5)
            facing -= 6;
        }

    private void moveForward() {
        x += facingXDelta[facing];
        y += facingYDelta[facing];
        if (x < 0)
            x += world.width();
        else if (x >= world.width())
            x -= world.width();
        if (y < 0)
            y += world.height();
        else if (y >= world.height())
            y -= world.height();
        }

    private void eatAndDigest() {
			// PAD added eating behav arg
 //For test:
 //	energy += world.feed(x, y, 0) - ENERGY_PER_TICK;
  	energy += world.feed(x, y, behaviour[6]) - ENERGY_PER_TICK;
        if (energy > MAXIMUM_ENERGY)
            energy = MAXIMUM_ENERGY;
        else if (energy <= 0)
            kill();
        }

// PAD Repro mutates both "parent" and child -- so really two offspring...
    private void possibleReproduction() {
        if (age >= REPRODUCTION_AGE && energy >= REPRODUCTION_ENERGY) {
            world.add(new Bug(this, energy/2));
            world.add(new Bug(this, energy - energy/2));
            kill();
            }
        }

    private void mutateGenes() {
        int r = world.random().range(genlen-1);//PAD exempt greed
        if (world.random().flip())
            {
            genes[r] += 1;                      // mutate upwards
            if (genes[r] > MAXIMUM_GENE_VALUE)  // if above maximum lower all gene values
                for (int l = 0; l < (genlen - 1); l++) {//PAD exempt greed
                    genes[l] -= 1;
                    genes[l] = Math.max(genes[l], 0);
                    }
            }
        else if (genes[r] > 0)
            genes[r] -= 1;
        }

    // 1 << genes[i] is the weighing for a turn of i.
    // behaviour[i] = (sum of all lower behaviours) + (1 << genes[i])
    private void calculateBehaviour() {
        behaviour[0] =                (1 << genes[0]);
        behaviour[1] = behaviour[0] + (1 << genes[1]);
        behaviour[2] = behaviour[1] + (1 << genes[2]);
        behaviour[3] = behaviour[2] + (1 << genes[3]);
        behaviour[4] = behaviour[3] + (1 << genes[4]);
        behaviour[5] = behaviour[4] + (1 << genes[5]);
	behaviour[6] = (genes[6]<5)?0:1; //PAD -- two eating behaviours
	color = COLORS[behaviour[6]];	//PAD
        }

    private void kill() {
        world.sub(this);
        world.erase(x, y);
        world = null; // indicates bug is dead
        }

    public int energy() {
        return energy;
        }


    public int age() {
        return age;
        }

    public int gene(int P) {
        return genes[P];
        }

    public int behaviour(int P) {
        return behaviour[P];
        }

    public void select() {
        color = SELECTED_COLOR;
        }

    public void unselect() {
        color = COLORS[behaviour[6]];
        }

    public boolean dead() {
        return world == null;
        }
    }

