import java.awt.*;
import java.applet.Applet;
import java.math.*;
import java.util.*;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import java.net.*;


/*
 **        --- SnowFlakes.java ---
 ** 
 ** (c) 2000 Stefan Robl <stefan@qdev.de>
 ** 
 **      Web:  https://www.qdev.de
 **
 **      2004-11-07: fixed a problem which made it impossible
 **                  to run properly in some VMs
 */


public class SnowFlakes extends Applet implements Runnable
{
	Image img;
	Graphics gfx;
	Thread thread;
	Random random=new Random();
	
	int snowflakes, flakedither;
	int stormnew, storm, stormw, stormv;
	boolean stormon=true;
	int sideglue, moveglue;
	int iteration=0;
	
	int xx[], yy[], pixels[];
	int width, height;
	
	Color black=new Color(0,0,0);
	Color white=new Color(255,255,255);
	
	// ****************************************************************
	
	public int get_int_param(String str, int def, int min, int max)
	{
		int val=def;
		
		if ((str=getParameter(str))!=null)
		{        
			try { val=new Integer(str).intValue(); }
			catch ( NumberFormatException e ) { }
			
			if (val<min)
				val=min;
			if (val>max)
				val=max;
		}
		
		return val;
	}
	
	
	public void init() 
	{
		String str;
		
		width=this.size().width;
		height=this.size().height;
		img=createImage(width,height);
		gfx=img.getGraphics();
		
		gfx.setColor(black);
		gfx.fillRect(0, 0, width, height);
		
		snowflakes  = get_int_param("SNOWFLAKES", 5000, 1, 100000);
		flakedither = get_int_param("FLAKEDITHER", -1, 4, 100000);
		sideglue    = get_int_param("SIDEGLUE", -1, -1, 100000);
		moveglue    = get_int_param("MOVEGLUE", -1, -1, 100000);
		
		xx     = new int [snowflakes];
		yy     = new int [snowflakes];
		pixels = new int [width*height];
		
		if ((str=getParameter("PICTURE"))!=null)
		{
			Image image=getImage(getDocumentBase(), str);
			
			MediaTracker mt = new MediaTracker(this);
			mt.addImage(image, 0);
			try { mt.waitForAll(); } // wait for image loading
			catch (InterruptedException e) { }
			
			PixelGrabber pg = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
			try { pg.grabPixels(); } 
			catch (InterruptedException e) { }
			
			gfx.drawImage(image, 0, 0, width, height, this);
		}
		
		if ((str=getParameter("STORM"))!=null)
		{
			if (str.equalsIgnoreCase("OFF"))
				stormon=false;
		}
		
		for(int i=0; i<snowflakes; i++)
			init_snowflake(i);
		
		setstorm(rand(200)-100);
		stormnew = rand(200)-100;
    }
	
	public int rand(int m)
	{
		int r=random.nextInt()%(m+1);
		if (r<0)
			r*=-1;
		return r;
	}
	
	public void plot(int x, int y, int set)
	{
		if (x>=0 && x<width && y>=1 && y<height)
		{
			if (set!=0)
				gfx.setColor(white);
			else 
				gfx.setColor(black);
			
			gfx.fillRect(x,y,1,1);
			
			pixels[(y*width)+x]=set;
		}
	}
	
	public boolean point(int x, int y)
	{
		if (x>=0 && x<width && y>=1 && y<height)
		{
			return (pixels[(y*width)+x] & 0xffffff)!=0;
		}
		return false;
    }
	
	public void init_snowflake(int n)
	{
		boolean f;
		do
		{
			xx[n]=rand(width*2)-width/2;
			yy[n]=-rand(height*2);
			
			f=true;
			
			for(int y=0; y<2; y++)
				for(int x=-1; x<2; x++)
					if (point(xx[n]+x, yy[n]+y))
						f=false;
		}
		while (!f); 
	}
	
	public void reset_snowflake(int n)
	{
		plot(xx[n], yy[n], 1);
		xx[n]=rand(width*2)-width/2;
		yy[n]=-rand(height);
	}
	
	
	public void control_snowflake(int n, int xp)
	{
		boolean out=false;
		
		if (xx[n]<=0 || xx[n]>=width-1)
			out=true;
		
		if (yy[n]>=height-1)
		{
			reset_snowflake(n);
		}
		else if (point(xx[n]+xp, yy[n]))
		{
			boolean dr=true;
			
			if (!point(xx[n], yy[n]))
			{
				dr=false;
			}
			else if (!point(xx[n]-xp, yy[n]))
			{
				xx[n]-=xp;
				dr=false;
			}
			
			if (dr || (sideglue>=0 && rand(sideglue)==0))
			{
				reset_snowflake(n);
			}
		}
		else if (point(xx[n]+xp, yy[n]+1))
		{
			xx[n]+=xp;
			
			boolean dr=true;
			int r, f=0;
			
			do
			{
				r=rand(2);
				
				switch (r)
				{
					case 0:
						if (!point(xx[n]-1, yy[n]) && point(xx[n]+1, yy[n]) && !point(xx[n]-1, yy[n]+1))
						{
							xx[n]--;
							dr=false;
						}
						f|=1;
						break;
					case 1:
						if (point(xx[n]-1, yy[n]) && !point(xx[n]+1, yy[n]) && !point(xx[n]+1, yy[n]+1))
						{
							xx[n]++;
							dr=false;
						}
						f|=2;
						break;
					case 2:
						if (!point(xx[n]-1, yy[n]) && !point(xx[n]+1, yy[n]) && !point(xx[n]-1, yy[n]+1) && !point(xx[n]+1, yy[n]+1))
						{
							xx[n]+=(rand(1)*2)-1;
							dr=false;
						}
						f|=4;
						break;
				}	    
			}
			while (f!=7 && dr);
			
			if (!out && (xx[n]<=0 || xx[n]>=width-1))
				dr=true;
			
			if (dr || (moveglue>=0 && rand(moveglue)==0))
			{
				reset_snowflake(n);
			}
		}
		else
		{
			xx[n]+=xp;
		}
	}
	
	public void setstorm(int s)
	{
		stormv=0;
		stormw=0;
		
		if (stormon)
		{
			if (s<-100)
				s=-100;
			if (s>100)
				s=100;
			
			if (s<0)
			{
				stormv=-1;
				stormw=100+s+5;
			}
			else if (s>0)
			{
				stormv=1;
				stormw=100-s+5;
			}
		}
	}
	
	public void calc_storm()
	{
		if (storm<stormnew)
			storm++;
		else if (storm>stormnew)
			storm--;
        
		setstorm(storm);
		
		if (iteration++>500)
		{
			stormnew=75+rand(25);
			if (rand(1)==0)
				stormnew*=-1;
			iteration=0;
		}
	}
	
	// *******************************************************
	
	public void start()
	{    
		if (thread == null)
		{
			thread = new Thread(this);
			thread.start();
		}
	}
	
	public void stop()
	{
		if (thread != null)
		{
			thread.stop();
			thread = null;
		}
	}
	
	public void run()
	{
		while (true)
		{
			repaint();
			try { Thread.sleep(10); }
			catch(InterruptedException e) {}
		}
	}
	
	public void paint( Graphics g )
    {	 
		int i;
		
		for(i=0; i<snowflakes; i++)
			plot(xx[i], yy[i]-1, 0);
		
		for(i=0; i<snowflakes; i++)
		{
			if (flakedither>=0 && (rand(flakedither)==0 || flakedither==0))
				control_snowflake(i, rand(2)-1);
			
			if (rand(stormw)==0 || stormw==0)
				control_snowflake(i, stormv);
			
			control_snowflake(i, 0);
		}
		
		for(i=0; i<snowflakes; i++)
		{
			plot(xx[i], yy[i], 2);
			yy[i]++;
		}
		
		g.drawImage(img, 0, 0, this);
		
		if (stormon)
			calc_storm(); 
	}
	
	public void update(Graphics g)
	{
		paint(g);
	}
}
