import java.applet.Applet;
import java.awt.*;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
 
public class snow extends Applet
implements Runnable {
    
    public void start() {
        tSnow = new Thread(this);
        isRunning = true;
        tSnow.start();
    }
    
    public void stop() {
        isRunning = false;
    }
    
    public void run() {
        while(isRunning) {
            movetotFlakes();
            nextFrame();
            repaint();
            Thread.yield();
        }
    }
    
    private void movetotFlakes() {
        System.arraycopy(srcPixels, 0, destPixels, 0, countPixels);
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < totFlakes / (i + 1) / (i + 1); j++) {
                double d = (Math.random() + Math.random()) - 1.0D;
                flakeVX[j][i] += (int)(2D * (double)(i + 1) * d);
                if(flakeVX[j][i] > speedLimit * (i + 1))
                    flakeVX[j][i] = speedLimit * (i + 1);
                else
                    if(flakeVX[j][i] < -speedLimit * (i + 1))
                        flakeVX[j][i] = -speedLimit * (i + 1);
                if(flakeX != null)
                    flakeX[j][i] += flakeVX[j][i] / 10;
                flakeX[j][i] = (flakeX[j][i] + widthImage) % widthImage;
                flakeY[j][i] = flakeY[j][i] + i + 1;
                if(flakeY[j][i] >= heightImage) {
                    flakeY[j][i] = 0;
                    flakeVX[j][i] *= -1;
                }
                for(int k = 0; k < i + 1; k++) {
                    for(int l = 0; l < i + 1; l++)
                        destPixels[((flakeY[j][i] + k) * widthImage + flakeX[j][i] + l + countPixels) % countPixels] = snowCols[Math.abs(flakeVX[j][i]) / (speedLimit * (i + 1))];
                    
                }
                
            }
            
        }
        
    }
    
    private void nextFrame() {
        try {
            Image image = createImage(new MemoryImageSource(widthImage, heightImage, destPixels, 0, widthImage));
            synchronized(gScreen) {
                gScreen.drawImage(image, 0, 0, this);
            }
            System.gc();
            return;
        }
        catch(Exception exception) {
            System.out.println("Unable to create next frame: " + exception);
        }
    }
    
    public void getPixs(Image image) {
        MediaTracker mediatracker = new MediaTracker(this);
        mediatracker.addImage(image, 1);
        try {
            mediatracker.waitForID(1);
            widthImage = image.getWidth(this);
            heightImage = image.getHeight(this);
            iScreen = createImage(widthImage, heightImage);
            gScreen = iScreen.getGraphics();
            synchronized(gScreen) {
                gScreen.drawImage(image, 0, 0, this);
            }
            countPixels = widthImage * heightImage;
            srcPixels = new int[countPixels];
            destPixels = new int[countPixels];
            PixelGrabber pixelgrabber = new PixelGrabber(image, 0, 0, widthImage, heightImage, srcPixels, 0, widthImage);
            try {
                pixelgrabber.grabPixels();
                System.arraycopy(srcPixels, 0, destPixels, 0, countPixels);
                return;
            }
            catch(Exception exception1) {
                System.out.println("Unable to grab pixels: " + exception1);
            }
            return;
        }
        catch(Exception exception) {
            System.out.println("Unable to getPixs: " + exception);
        }
    }
    
    public void setForce(String s) {
        int i = Integer.parseInt(s);
        if(i > 10)
            force = 10;
        else
            if(i < 1)
                force = 1;
            else
                force = i;
        totFlakes = countPixels / (25 * (11 - force));
        speedLimit = 10 * force;
        flakeX = new int[totFlakes][3];
        flakeVX = new int[totFlakes][3];
        flakeY = new int[totFlakes][3];
        for(int j = 0; j < 3; j++) {
            for(int k = 0; k < totFlakes; k++) {
                flakeVX[k][j] = (int)((Math.random() - 0.5D) * (double)speedLimit);
                flakeX[k][j] = (int)(Math.random() * (double)widthImage);
                flakeY[k][j] = (int)(Math.random() * (double)heightImage);
            }
            
        }
        
    }
    
    public void setImage(String s) {
        Image image = getImage(getDocumentBase(), s);
        if(image != null) {
            getPixs(image);
            return;
        } else {
            error("Unable to load image " + s);
            return;
        }
    }
    
    public void init() {
        System.out.println(getAppletInfo());
        for(int i = 0; i < snowCols.length; i++)
            snowCols[i] = 0xff000000 | (215 + 40 / (i + 1)) * 0x10101;
        
        String s = getParameter("image");
        if(s != null)
            setImage(s);
        s = getParameter("force");
        if(s != null) {
            setForce(s);
        } else {
            setForce("3");
        }
    }
    
    private void error(String s) {
        strError = s;
        getAppletContext().showStatus(s);
    }
    
    public final void update(Graphics g) {
        paint(g);
    }
    
    public final void paint(Graphics g) {
        if(iScreen != null)
            g.drawImage(iScreen, 0, 0, this);
    }
    
    public snow() {
        snowCols = new int[10];
        strError = "";
    }
    
    private Image imgBuffer;
    private Graphics gBuffer;
    private transient Thread tSnow;
    private transient boolean isRunning;
    private int totFlakes;
    private int flakeX[][];
    private int flakeVX[][];
    private int flakeY[][];
    private int speedLimit;
    private int widthImage;
    private int heightImage;
    private int srcPixels[];
    private int destPixels[];
    private int countPixels;
    private volatile Image iScreen;
    private volatile Graphics gScreen;
    private int force;
    private int snowCols[];
    private String strError;
}

