import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.awt.image.*;

public class Confetti extends Applet implements Runnable
{ boolean stop   = false;
  int     Stars  = 25000;
  int[]   StarC  = new int[Stars+1];
  float[] StarX  = new float[Stars+1];
  float[] StarY  = new float[Stars+1];
  float[] StarXA = new float[Stars+1];
  float[] StarYA = new float[Stars+1];
  
  float MouseX =0, MouseY =0;
  float MouseCX=0, MouseCY=0;
  float MouseXA=0, MouseYA=0;
    
  int W, H;
  
  BufferedImage  biScreen;
  DataBuffer db;
  Thread tConfetti;
  
  public void start()
  { if(tConfetti == null)
    { tConfetti=new Thread(this);
      tConfetti.start(); }
  }
  
  public void stop()
  { stop=true;
    tConfetti = null;  }
      
  public void init() 
  { this.enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);

    W = this.getSize().width;
    H = this.getSize().height;
    
    biScreen = new BufferedImage(W, H, 1);
    db = biScreen.getRaster().getDataBuffer();        

    for(int x=0; x<W; x++)
      for(int y=0; y<H; y++)
        db.setElem(x+(y*W), 0xFFFFFF);
              
    for(int t=0; t<Stars; t++)
      newStar(t);
  }
  
  public void run()
  { while(!stop)
    { MouseXA = (MouseCX-MouseX)/5.0f;
      MouseYA = (MouseCY-MouseY)/5.0f;
      MouseX +=MouseXA*2.5f;
      MouseY +=MouseYA*2.5f;
          
      for(int t=0; t<Stars; t++)
      { StarX[t]+=StarXA[t];
        StarY[t]+=StarYA[t];
        StarYA[t]+=(H/100000.0f);
        if((StarY[t]+3>H)||(StarX[t]+3>W)||(StarX[t]-2<0)) 
          newStar(t);

        if(StarY[t]-1>0)
        { db.setElem((int)StarX[t] + ((int)(StarY[t]+1)*W), StarC[t]);
          db.setElem((int)StarX[t] + ((int)(StarY[t]-1)*W), StarC[t]);
          db.setElem((int)StarX[t] + ((int)StarY[t]*W)-1, StarC[t]);
          db.setElem((int)StarX[t] + ((int)StarY[t]*W)+1, StarC[t]);   }
      }
                                   
      repaint(0, 0, W, H);
                              
      try
      { Thread.sleep(10); }
      catch (InterruptedException e)
      {;}      
      
      for(int t=0; t<Stars; t++)
        if(StarY[t]-1>0)
        { db.setElem((int)StarX[t] + ((int)(StarY[t]+1)*W), 0xFFFFFF);
          db.setElem((int)StarX[t] + ((int)(StarY[t]-1)*W), 0xFFFFFF);
          db.setElem((int)StarX[t] + ((int)StarY[t]*W)-1,   0xFFFFFF);
          db.setElem((int)StarX[t] + ((int)StarY[t]*W)+1,   0xFFFFFF);   }
    } 
  }

  private void newStar(int t)
  { if((MouseX<10)||(MouseX>W-10)||(MouseY>H-10))
    { StarX[t]=(int)(Math.random()*(W-10))+5;  
      StarY[t]=(int)(Math.random()*(H-10))+5; }
    else  
    { StarX[t]  =(int)MouseX;
      StarY[t]  =(int)MouseY; }
      
    StarXA[t] = (float)Math.random()*(W/250)-(W/500) + MouseXA;
    StarYA[t] = (float)Math.random()*(H/100)-(H/150) + MouseYA; 
    StarC[t]  = ((int)(Math.random()*250)<<16) | ((int)(Math.random()*250)<<8) | (int)(Math.random()*250);
  }
  
  public void update(Graphics g)
  { paint(g); }
    
  public void paint(Graphics g)
  { g.drawImage(biScreen, 0,0, this); }
                                 
  public void processMouseMotionEvent(MouseEvent e)
  { switch(e.getID())
    { case MouseEvent.MOUSE_DRAGGED: 
      case MouseEvent.MOUSE_MOVED:   MouseCX = (float)e.getX();
                                     MouseCY = (float)e.getY();
    }
  }
}
