import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;


public class ZEffect extends java.applet.Applet implements Runnable
{
  Image im, im2;
  Thread runThread;

  int time = 0;

  Bubbler b;
  RingPairs r;
  RingPairs r2;

  int[][] buf;

  Color[] col;

  public void init() {
    im = createImage(320, 240);
    b = new Bubbler(0xFC6704DD, 10);
    r = new RingPairs(0x9871FFC1, 3, 15, b);
    r2 = new RingPairs(0x8176A444, 1, 25, r);
    buf = new int[128][128];
    for(int j=0; j<128; j++) for(int i=0; i<128; i++) buf[j][i] = (((i*117+j)%(172+j/18+i/39))+145)&255;
  }

  public void start() {
    runThread = new Thread(this);
    runThread.start();
  }

  public void stop() {
    runThread.stop();
    runThread = null;
  }

  public void run() {

    while(true) {
      repaint();
      try {runThread.sleep(20);} catch (InterruptedException e){}
      time += 20;
    }
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g)
  {
    Graphics img = im.getGraphics();
    img.setColor(Color.white);
    img.fillRect(0, 0, 320, 240);

//    b.Redraw(time, img);
//    r.Redraw(time, img);
//    r2.Redraw(time, img);

    img.setColor(Color.black);

    for(int j=0; j<128; j++) {
      for(int i=0; i<128; i++) {
        buf[j][i] = (((
          (int)buf[(j-1)&127][i] +
          (int)buf[(j+1)&127][i] +
          (int)buf[j][(i-1)&127] +
          (int)buf[j][(i+1)&127]
        )>>2) + 1)&255;
        if(buf[j][i] < 240 || buf[i][j] > 248) img.fillRect(i, j, 1, 1);
      }
    }

    g.drawImage(im, 0, 0, this);
  }

}



class ZObject
{
  int rand_seed = 0;
  protected int rand(int max) { rand_seed = (rand_seed*371+73746119)&0x7FFFFFFF; return (rand_seed/3)%max; }
  protected int irand(int min, int max) { return rand(max-min) + min; }
  protected double drand(double min, double max) { return rand((int)((max-min)*1000))*0.001 + min; }
  protected void randSeed(int seed) { rand_seed = seed; }
}



class ZObjectGroup2D extends ZObject
{
  public double getX(int time, int index) { return 0; }
  public double getY(int time, int index) { return 0; }
  public int getNum() { return 0; }
}



class Bubbler extends ZObjectGroup2D
{
  double[] px, fx, py, fy;
  int num;

  public Bubbler(int seed, int num)
  {
    randSeed(seed);

    this.num = num;

    px = new double[num];
    fx = new double[num];
    py = new double[num];
    fy = new double[num];

    for(int i=0; i<num; i++) {
      px[i] = drand(0.05, 0.25);
      fx[i] = drand(0.05, 0.25);
      py[i] = drand(0.05, 0.25);
      fy[i] = drand(0.05, 0.25);
    }
  }

  void Redraw(int time, Graphics g)
  {
    g.setColor(Color.red);

    for(int i=0; i<num; i++) {
      int x = (int)getX(time, i)-4;
      int y = (int)getY(time, i)-4;
      g.fillRect(x, y, 8, 8);
    }

  }

  public double getX(int time, int index) {
    return 160.0 + 120.0 * Math.sin( px[index] + fx[index] * time*3.14159/500.0 );
  }

  public double getY(int time, int index) {
    return 120.0 + 100.0 * Math.cos( py[index] + fy[index] * time*3.14159/500.0 );
  }

  public int getNum() { return num; }
}



class RingPairs extends ZObjectGroup2D
{
  ZObjectGroup2D zog;

  double[] p1, f1;
  int[] i1;
  int num;
  int r;

  public RingPairs(int seed, int num, int r, ZObjectGroup2D zog)
  {
    randSeed(seed);

    this.num = num;
    this.r = r;
    this.zog = zog;

    p1 = new double[zog.getNum()];
    f1 = new double[zog.getNum()];

    i1 = new int[zog.getNum()];

    for(int i=0; i<zog.getNum(); i++) {
      p1[i] = drand(0, 50);
      f1[i] = drand(10000, 40000);
    }
  }

  public void Redraw(int time, Graphics g)
  {
    g.setColor(Color.blue);

    for(int i=0; i<zog.getNum(); i++) i1[i] = i;

    for(int i=0; i<zog.getNum(); i++) {
      int r = ((int)(time/f1[i]+p1[i])) % zog.getNum();
      int j = i1[r]; i1[r] = i1[i]; i1[i] = j;
    }

    for(int i=0; i<num; i++) {

      int x1 = (int)getX(time, (i<<1)  );
      int y1 = (int)getY(time, (i<<1)  );
      int x2 = (int)getX(time, (i<<1)+1);
      int y2 = (int)getY(time, (i<<1)+1);

      int d = (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);

      if(d > 2*r*r) {
        double dd = 1.0 / Math.sqrt(d);
        int dx = (int)(dd*r*(x2-x1));
        int dy = (int)(dd*r*(y2-y1));
        g.drawLine(x1+dx, y1+dy, x2-dx, y2-dy);
      }

      g.drawArc(x1-r, y1-r, r*2, r*2, 0, 360);
      g.drawArc(x2-r, y2-r, r*2, r*2, 0, 360);
    }
  }

  public double getX(int time, int index) {
    return zog.getX(time, i1[index]);
  }

  public double getY(int time, int index) {
    return zog.getY(time, i1[index]);
  }

  public int getNum() { return num; }
}



