/*
  Copyright 2006 by Sean Luke and George Mason University
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

package sim.app.flockers;
import sim.engine.*;
import sim.util.*;
import sim.field.continuous.*;

public class Flockers extends SimState
    {
    private static final long serialVersionUID = 1;

    public Continuous2D flockers;
    public double width = 150;
    public double height = 150;
    public int numFlockers = 200;
    public double cohesion = 1.0;
    public double avoidance = 1.0;
    public double randomness = 1.0;
    public double consistency = 1.0;
    public double momentum = 1.0;
    public double deadFlockerProbability = 0.1;
    public double neighborhood = 10;
    public double jump = 0.7;  // how far do we move in a timestep?
    
    public double getCohesion() { return cohesion; }
    public void setCohesion(double val) { if (val >= 0.0) cohesion = val; }
    public double getAvoidance() { return avoidance; }
    public void setAvoidance(double val) { if (val >= 0.0) avoidance = val; }
    public double getRandomness() { return randomness; }
    public void setRandomness(double val) { if (val >= 0.0) randomness = val; }
    public double getConsistency() { return consistency; }
    public void setConsistency(double val) { if (val >= 0.0) consistency = val; }
    public double getMomentum() { return momentum; }
    public void setMomentum(double val) { if (val >= 0.0) momentum = val; }
    public int getNumFlockers() { return numFlockers; }
    public void setNumFlockers(int val) { if (val >= 1) numFlockers = val; }
    public double getWidth() { return width; }
    public void setWidth(double val) { if (val > 0) width = val; }
    public double getHeight() { return height; }
    public void setHeight(double val) { if (val > 0) height = val; }
    public double getNeighborhood() { return neighborhood; }
    public void setNeighborhood(double val) { if (val > 0) neighborhood = val; }
    public double getDeadFlockerProbability() { return deadFlockerProbability; }
    public void setDeadFlockerProbability(double val) { if (val >= 0.0 && val <= 1.0) deadFlockerProbability = val; }
        
    public Double2D[] getLocations()
        {
        if (flockers == null) return new Double2D[0];
        Bag b = flockers.getAllObjects();
        if (b==null) return new Double2D[0];
        Double2D[] locs = new Double2D[b.numObjs];
        for(int i =0; i < b.numObjs; i++)
            locs[i] = flockers.getObjectLocation(b.objs[i]);
        return locs;
        }
    
    public Double2D[] getInvertedLocations()
        {
        if (flockers == null) return new Double2D[0];
        Bag b = flockers.getAllObjects();
        if (b==null) return new Double2D[0];
        Double2D[] locs = new Double2D[b.numObjs];
        for(int i =0; i < b.numObjs; i++)
            {
            locs[i] = flockers.getObjectLocation(b.objs[i]);
            locs[i] = new Double2D(locs[i].y, locs[i].x);
            }
        return locs;
        }

    /** Creates a Flockers simulation with the given random number seed. */
    public Flockers(long seed)
        {
        super(seed);
        }
    
    public void start()
        {
        super.start();
        
        // set up the flockers field.  It looks like a discretization
        // of about neighborhood / 1.5 is close to optimal for us.  Hmph,
        // that's 16 hash lookups! I would have guessed that 
        // neighborhood * 2 (which is about 4 lookups on average)
        // would be optimal.  Go figure.
        flockers = new Continuous2D(neighborhood/1.5,width,height);
        
        // make a bunch of flockers and schedule 'em.  A few will be dead
        for(int x=0;x<numFlockers;x++)
            {
            Double2D location = new Double2D(random.nextDouble()*width, random.nextDouble() * height);
            Flocker flocker = new Flocker(location);
            if (random.nextBoolean(deadFlockerProbability)) flocker.dead = true;
            flockers.setObjectLocation(flocker, location);
            flocker.flockers = flockers;
            flocker.theFlock = this;
            schedule.scheduleRepeating(flocker);
            }
        }

    public static void main(String[] args)
        {
        doLoop(Flockers.class, args);
        System.exit(0);
        }    
    }
