/*
 * SimThread.java
 *
 * Created on December 22, 2000, 6:58 AM
 */

package pharynx;
import java.util.*;
import java.awt.*;
import java.awt.geom.*;

/**
 *
 * @author  leon
 * @version 
 */
public class SimThread extends java.lang.Thread implements Kickable {
    private Cubbyhole c;
    private Pharynx p;
    private Collection particles;
    private SimOptions options;
    private double dt;                   // time step size
    private double start;
    private PictureData pData;
    private Snapper snapper = new Snapper();

    /** Creates new SimThread, setting time step
     *
     * @param dt    the time step
     */
    public SimThread(Cubbyhole c, SimOptions options) {
        this.options = options;
        this.c = c;
        this.dt = options.dt;
        this.start = options.start;
    }

    public void run() {
        this.currentThread().setName("Simulation");
        this.currentThread().setPriority(Thread.MIN_PRIORITY);
        p = new Pharynx(options.makeMotions(), true);
        pData = new PictureData();
        particles = new ArrayList();
        getConstantDims();
        addParticles();
        p.sim().add(new SKick(start, this, ANIMATE));
        addSnapshots();
        p.simulate();
        c.halt();
    }

    private void addParticles() {
        for(Iterator i = options.particleSeries.iterator(); i.hasNext();) {
            ParticleSeries ps = (ParticleSeries) i.next();
            for(int j = 0; j < ps.n; j++) {
                double t = ps.startTime + j * ps.interval;
                SKick sk = new SKick(t, this, INSERT_PARTICLE);
                sk.ps = ps;
                p.sim().add(sk);
            }
        }
    }

    private void addSnapshots() {
        if (0 == options.snapFile.getPath().compareTo(""))
            return;
        p.sim().add(new SKick(options.snapStart, this, SNAPSHOT));
    }
    
    public void halt() {
        p.halt();
    }

    /** Internal Kick with extra info */
    class SKick extends Kick {
        private int type;
        private ParticleSeries ps;
        
        public SKick(double t, Kickable k, int type) {
            super(t, k);
            this.type = type;
        }
    }
    
    private void retrievePictureData(double time) {
        pData.t = time;
        getCurrDiameter(time);
        pData.things = new ArrayList();
        for(Iterator i = particles.iterator(); i.hasNext();) {
            Particle pp = (Particle) i.next();
            if (!pp.inPharynx()) {
                i.remove();
                continue;
            }
            PictureData.Thing pt = pData.new Thing();
            pt.x = pp.where(time);
            pt.color = pp.color();
            pt.shape(pp.shape());
            pData.things.add(pt);
        }
    }

    /**
     * receive a Kick from the Kick handler
     *
     * Get numbers to be animated, send them off, and ask for another kick
     *
     * @param k     the Kick
     */
    public void kick(Kick k) {
        SKick sk = (SKick) k;
        switch(sk.type) {
        case ANIMATE:
            if (p.sim().isEmpty()) return;
            send(k.time());
            k.time(k.time() + dt);
            p.sim().add(k);
            break;

        case SNAPSHOT:
            if (p.sim().isEmpty()) return;
            snap(k.time());
            double next = k.time() + options.snapInterval;
            if (
                (next > k.time()) &&
                (next >= options.snapStart) &&
                (next <= options.snapStop)
            ) {
                k.time(next);
                p.sim().add(k);
            }
            break;

        case INSERT_PARTICLE:
            Particle pp = null;
            switch(sk.ps.type) {
            case ParticleSeries.BACTERIUM:
                pp = new Bacterium(p.sim(), p, sk.ps.getDiameter());
                break;

            case ParticleSeries.ACCELERATED:
                pp = new AcceleratedParticle(p.sim(), p, sk.ps.getDiameter(),
                                             sk.ps.acceleration);
                break;

            case ParticleSeries.FLUID:
                pp = new FluidParticle(p.sim(), p, sk.ps.getDiameter());
                break;

            default:
                throw new CantHappenException("Unknown particle type");
            }
            if (null != pp) {
                pp.color(sk.ps.color);
                pp.shape(sk.ps.shape);
                particles.add(pp);
                pp.place(sk.time(), p.length() - sk.ps.startX);
                pp.start();
            }
            break;

        default:
            throw new CantHappenException("Unknown SKick type");
        }
    }

    /** send PictureData to the animator */
    private void send(double time) {
        retrievePictureData(time);
        c.put(new PictureData(pData));
    }

    /** send PictureData to the animator */
    private void snap(double time) {
        retrievePictureData(time);
        snapper.write(time, pData, options);
    }

    private void getConstantDims() {
        java.util.List sections = p.sections();
        pData.length = p.length();
        pData.width = p.width();
        pData.maxDiameter = new double[sections.size()];
        pData.thickness = new double[sections.size()];
        for(int i = 0; i < sections.size(); i++) {
            Section s = (Section) sections.get(i);
            pData.maxDiameter[i] = s.diameter();
            pData.thickness[i] = s.thickness();
        }
    }
    
    private void getCurrDiameter(double t) {
        java.util.List sections = p.sections();
        pData.currDiameter = new double[sections.size()];
        for(int i = 0; i < sections.size(); i++) {
            Section s = (Section) sections.get(i);
            pData.currDiameter[i] = s.currDiameter(t);
        }
    }

    public void dt(double dt) { this.dt = dt; }
    public double dt() { return(dt); }
    public Pharynx pharynx() { return p; }

    private static final int ANIMATE= 0;
    private static final int INSERT_PARTICLE = 1;
    private static final int SNAPSHOT = 2;
    
}