//     Dust - a simple particle system for wind visualization
//     Copyright 1999,2005  Nick Thompson
//     see http://nixfiles.com/dust for more information
//
//     This program is free software; you can redistribute it and/or modify
//     it under the terms of the GNU General Public License as published by
//     the Free Software Foundation; either version 2 of the License, or
//     (at your option) any later version.
//
//     This program is distributed in the hope that it will be useful,
//     but WITHOUT ANY WARRANTY; without even the implied warranty of
//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//     GNU General Public License for more details.
//
//     You should have received a copy of the GNU General Public License
//     along with this program; if not, write to the Free Software
//     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//

import java.applet.*;
import java.awt.*;
import java.net.URL;
import java.awt.image.ImageObserver;

public class Dustlet extends Applet implements ImageObserver, Runnable {

    Dust dust;
    double fetch_interval = 0;
    long last_fetch = 0;
    boolean running;
    URL windfile_url;
    Image bgimage;
    Thread animator;
    double timestep;

    public void init() {
        String refetch = getParameter("refetch");
	if (refetch != null) {
	    fetch_interval = Double.valueOf(refetch).doubleValue();
	}

        // read the background image
        String datfile = getParameter("bgimage");
        if (datfile == null) {
            datfile = "http://sfports.wr.usgs.gov/~wind/windsuv.dat";
        }
	try {
	    URL url = new URL(getDocumentBase(), datfile);
	    // get the image and stash it in bgimage
	    bgimage = getImage(url);
	} catch (java.net.MalformedURLException e) {
	    System.err.println("bad url " + datfile + ":\n" + e);
	}

	timestep = 0.1;
	try {
	    String tmp = getParameter("timestep");
	    if (tmp != null) {
		timestep = Double.valueOf(tmp).doubleValue(); // retch
	    }
	} catch (NumberFormatException e) {
	    timestep = 0.1;
	}

        stop_running();
    }

    public void start() {
	int nmotes = 1000;
	try {
	    String motes_str = getParameter("motes");
	    if (motes_str != null) {
		nmotes = Integer.parseInt(motes_str);
	    }
	} catch (NumberFormatException e) {
	    nmotes = 1000;
	}
        dust = new Dust(nmotes);

        String datfile = getParameter("data");
        if (datfile == null) {
            datfile = "http://sfports.wr.usgs.gov/~wind/windsuv.out";
        }
	try {
	    windfile_url = new URL(getDocumentBase(), datfile);
	} catch (java.net.MalformedURLException e) {
	    System.err.println("bad url " + datfile + ":\n" + e);
	}
	update_wind_data();

	// this must be done after xsize and ysize are set...
	// i should keep track of width and height and remove this problem
        Rectangle r = bounds();
        dust.set_pixel_size(r.width, r.height);

        start_running();
    }

    public void stop() {
        stop_running();
    }

    public void destroy() {
        stop_running();
	if (animator != null) {
	    animator.stop();
	    animator = null;
	}
    }

    public synchronized boolean mouseDown(Event ev, int x, int y) {
	if (running) {
	    stop_running();
	} else {
	    start_running();
	}
	return true;
    }

    public boolean imageUpdate(Image img, int infoflags,
			       int x, int y, int width, int height) {
	if ((infoflags & ImageObserver.ALLBITS) != 0) {
	    repaint();
	    return false;
	}
	return true;
    }

    public void paint_background(Graphics g) {
        if (bgimage == null) {
            g.setColor(Color.black);
            Rectangle rect = g.getClipRect();
	    g.fillRect(rect.x, rect.y, rect.width, rect.height);
            return;                
        }

        g.drawImage(bgimage, 0, 0, null);
    }

    // XXX/nix resizing won't work right...
    Image backbuffer;
    Graphics backbuffer_graphics;
    public void update(Graphics g) {	// skip the background fill
	paint(g);
    }
    public void paint(Graphics g) {
	// System.err.println("paint called");
	if (backbuffer == null) {
            Rectangle rect = g.getClipRect();
	    backbuffer = createImage(rect.width, rect.height);
	    backbuffer_graphics = backbuffer.getGraphics();
	}
        paint_background(backbuffer_graphics);
	if (dust != null) {
	    backbuffer_graphics.setColor(Color.green);
	    dust.drawmotes(backbuffer_graphics);
	}
	g.drawImage(backbuffer, 0, 0, null);
    }

    public synchronized void await_running() {
        while (!running) {
	    try {
		wait();
	    } catch (InterruptedException e) {
	    }
        }
    }
    public synchronized void start_running() {
        running = true;
        notify();
	if (animator == null) {
	    animator = new Thread(this);
	    animator.start();
	}
    }
    public synchronized void stop_running() {
        running = false;
        notify();
    }

    public void update_wind_data() {
        java.io.InputStream in = null;

            // XXXopen url stream
        try {
            in = windfile_url.openStream();
            dust.read_windfile(in);
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
	    if (in != null) {
		try {
		    in.close();
		} catch (java.io.IOException e) {
		    System.err.println(e);
		}
	    }
        }
	last_fetch = System.currentTimeMillis();
    }

    public void step() {
        dust.stepmotes(0.1);
        repaint();
    }

    // this is the main loop for the update thread
    public void run() {
        while (true) {
            await_running();

	    long now = System.currentTimeMillis();
            double dt = (now - last_fetch) / 1000.0;
            if (fetch_interval > 0 && dt > fetch_interval) {
                update_wind_data();
            }
	    step();
	    try {
		Thread.sleep(100);
	    } catch (InterruptedException e) {
	    }
        }
    }
}
