/*
 * SnowFlake.java
 *
 * (C) 2005, Alex S.
 */

import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;

/**
 * moving thing.
 */
class MovingThing {
    double x,y;
    double direction;
}

/**
 * BufferedCanvas
 */
class BufferedPixCanvas extends Canvas {

    public int W=0, H=0, pix[];
    public Image im = null;
    private MemoryImageSource mis = null;

    private Vector things = null;

    /**
     * re-init thing to go somewhere
     * set it up at border, and give it a random direction
     */
    private MovingThing reInitThing(MovingThing thing){
        thing.x = Math.random()*(W-1);
        thing.y = Math.random()*(H-1);
        double side = Math.random();
        if(side < 0.25){
            thing.x = 0;
            thing.direction = Math.random()*Math.PI - Math.PI/2;
        }else if(side < 0.50){
            thing.x = W-1;
            thing.direction = Math.random()*Math.PI + Math.PI/2;
        }else if(side < 0.75){
            thing.y = 0;
            thing.direction = Math.random()*Math.PI;
        }else{
            thing.y = H-1;
            thing.direction = Math.random()*Math.PI + Math.PI;
        }
        return thing;
    }

    /**
     * move pieces and set pixels
     */
    public void setPix(){

        if(things.size() < 10000){
            things.addElement(reInitThing(new MovingThing()));
        }

        Enumeration enum = things.elements();
        while(enum.hasMoreElements()){
            MovingThing thing = (MovingThing)enum.nextElement();

            if(thing.x < 0 || thing.x >= W || thing.y < 0 || thing.y >= H){
                reInitThing(thing);
            }
            double sin = Math.sin(thing.direction);
            double cos = Math.cos(thing.direction);
            double nx = thing.x + cos * 1;
            double ny = thing.y + sin * 1;

            if(nx < 0 || nx >= W || ny < 0 || ny >= H){
                pix[ xy2i( (int)(thing.x), (int)(thing.y) ) ] = pack(0xFF,0xFF,0xFF);
                reInitThing(thing);
            }else if( unpack(pix[ xy2i( (int)(nx), (int)(ny) ) ],0) == 0){
                pix[ xy2i( (int)(thing.x), (int)(thing.y) ) ] = pack(0,0,0);
                reInitThing(thing);
            }else{
                pix[ xy2i( (int)(thing.x), (int)(thing.y) ) ] = pack(0xFF,0xFF,0xFF);
                thing.x = nx; thing.y = ny;
                pix[ xy2i( (int)(thing.x), (int)(thing.y) ) ] = pack(1,1,1);
            }
        }
    }

    /**
     * called when an update happens.
     */
    public void update(Graphics g) {
        if (W != bounds().width || H != bounds().height) {
            W = bounds().width;
            H = bounds().height;
            pix = new int[W*H];
            mis = new MemoryImageSource(W, H, pix, 0, W);
            mis.setAnimated(true);
            im = createImage(mis);
            things = new Vector();
            for(int i=0;i<pix.length;i++){
                pix[i] = pack(0xFF,0xFF,0xFF);
            }
            pix[ xy2i( W/2,H/2 ) ] = pack(0,0,0);
        }
        setPix();
        mis.newPixels(0, 0, W, H, true);
        g.drawImage(im, 0, 0, null);
    }

    /**
     * CONVERT XY COORDS TO INDEX INTO THE IMAGE PIXEL BUFFER ARRAY
     */
    public int xy2i(int x, int y) { return y * W + x; }

    /**
     * PACK RGB VALUES INTO AN IMAGE PIXEL
     */
    public static int pack(int rgb[]) { return pack(rgb[0],rgb[1],rgb[2]); }

    public static int pack(int r, int g, int b) {
        if(r > 255) r = 255;
        if(g > 255) g = 255;
        if(b > 255) b = 255;
       return ((r&255)<<16) | ((g&255)<< 8) | ((b&255)) | 0xff000000;
    }

    /**
     * UNPACK RGB VALUES FROM AN IMAGE PIXEL
     */
    public static int unpack(int packed, int i) { return packed >> 16-8*i & 255; }

    public static void unpack(int rgb[], int packed) {
       rgb[0] = (packed >> 16) & 255;
       rgb[1] = (packed >>  8) & 255;
       rgb[2] = (packed      ) & 255;
   }

}

/**
 * very basic swarm applet
 */
public class SnowFlake extends java.applet.Applet implements Runnable {
    Thread t = null;
    BufferedPixCanvas bc = null;

    public void init(){
        setLayout(new BorderLayout());
        bc = new BufferedPixCanvas();
        add("Center",bc);
        show();
    }

    public void start() {
        if (t == null) {
            t = new Thread(this);
            t.start();
        }
    }
    public void stop() {
        if (t != null) {
            t.stop();
            t = null;
        }
    }

    public void run() {
        try {
            while (true) {
                bc.repaint();
                repaint();
                t.sleep(30);
            }
        } catch (InterruptedException e) {
        }
    }
}


