import java.awt.*;
import java.awt.event.*;
import java.applet.*;

/** Insects simulator
 *   @author Jun Rekimoto
 *   @version $Id:$
 *   Copyright(C) 1998 Jun Rekimoto, All Rights Reserved
 */
public class Insects extends Applet implements Runnable, MouseListener, MouseMotionListener {
	double px[], py[], vx[], vy[], nvx[], nvy[];
	int size = 100;
	Thread thread;
	int area = 400;
	int ax[], ay[];
	double gx, gy;
	double cx, cy;
	int mx, my;
	double th = 0.0;
	Color insectcolor;

	
	public static void main(String argv[]) {
		Frame f = new Frame();
		Insects app = new Insects();
		f.add(app);
		f.pack();
		f.show();
		app.init();
		app.start();
	}

	public Insects() {
		px = new double[size];
		py = new double[size];
		vx = new double[size];
		vy = new double[size];
		nvx = new double[size];
		nvy = new double[size];
		for (int i = 0; i < size; i++) {
			px[i] = Math.random() * area;
			py[i] = Math.random() * area;
			vx[i] = Math.random() * 10.0 - 5.0;
			vy[i] = Math.random() * 10.0 - 5.0;
		}
		cx = cy = gy = gx = area / 2.0;
		mx = my = -1;
		addMouseListener(this);
		addMouseMotionListener(this);
		setBackground(Color.black);
		insectcolor = new Color(100, 150, 240);
		ax = new int[2];
		ay = new int[2];
		ax[0] = 170;  ax[1] = 230;
		ay[0] = ay[1] = 200;
	}

	public void paint(Graphics g) {
		g.setColor(Color.orange);
		for (int k = 0; k < 2; k++)
			g.fillOval(ax[k] - 10, ay[k] - 10, 20, 20);

		g.setColor(Color.red);
		g.fillRect((int)(gx) - 2, (int)(gy) - 2, 4, 4);
		
		g.setColor(insectcolor);
		for (int i = 0; i < size; i++) {
			int xx = (int)(px[i]);
			int yy = (int)(py[i]);
			g.fillRect(xx - 1, yy - 1, 3, 3);
			g.drawLine(xx, yy, (int)(xx + vx[i]), (int)(yy + vy[i]));
		}
	}

	public double dist(int i, int j) {
		return (px[i]-px[j]) * (px[i]-px[j]) + (py[i]-py[j]) * (py[i]-py[j]);
	}

	public void incrementValues() {
		Dimension d = getSize();
		
		/**
		th += 0.01;
		ax[1] = (int)(Math.sin(th) * 50.0) + 200;
		ay[1] = (int)(Math.cos(th) * 50.0) + 200;
		**/
		
		if (mx >= 0 && my >= 0) {
			gx = mx;
			gy = my;
		} else {
			gx = 0.0;
			gy = 0.0;
			for (int i = 0; i < size; i++) {
				gx += px[i];
				gy += py[i];
			}
			gx /= size;
			gy /= size;
		}



		for (int i = 0; i < size; i++) {
				nvx[i] =  (gx - px[i]) / 200.0;
				nvy[i] =  (gy - py[i]) / 200.0;

			for (int j = 0; j < size; j++) {
				if (i == j) continue;
				if (dist(i, j) < 200.0) {
					//System.out.println("x " + i + "," + j);//
					if (px[i] > px[j])
						nvx[i] += 5.0 / (10.0 + px[i] - px[j]);
					else
						nvx[i] -= 5.0 / (10.0 + px[j] - px[i]);
						
					if (py[i] > py[j])
						nvy[i] += 5.0 / (10.0 + py[i] - py[j]);
					else
						nvy[i] -= 5.0 / (10.0 + py[j] - py[i]);
				}
				
				for (int k = 0; k < 2; k ++) {
				if (((px[i]-ax[k])*(px[i]-ax[k])+
					(py[i]-ay[k])*(py[i]-ay[k])) < 400.0) {
					if (px[i] > ax[k])
						nvx[i] += 0.1 / (1.0 + px[i] - ax[k]);
					else
						nvx[i] -= 0.1 / (1.0 + ax[k] - px[i]);
						
					if (py[i] > ay[k])
						nvy[i] += 0.1 / (10.0 + py[i] - ax[k]);
					else
						nvy[i] -= 0.1 / (10.0 + ay[k] - py[i]);
				}
				}
					

				if (dist(i, j) < 1000.0) {
					nvx[i] += (0.99 * vx[i] + 0.01 * vx[j]) - vx[i];
					nvy[i] += (0.99 * vy[i] + 0.01 * vy[j]) - vy[i];
				}
				
			}
		}

		for (int i = 0; i < size; i++) {
			vx[i] += nvx[i];
			vy[i] += nvy[i];
			
			if ((vx[i] < 0.5 || vy[i] < -0.5) && (vy[i] < 0.5 || vy[i] < -0.5)) {
				vx[i] *= 1.1;
				vy[i] *= 1.1;
			}
			
			if (vx[i] > 4 || vx[i] < -4 || vy[i] > 4 || vy[i] < -4) {
				vx[i] *= 0.8;
				vy[i] *= 0.8;
			}
		}

		for (int i = 0; i < size; i++) {
			px[i] += vx[i];

			while (px[i] < 0) px[i] += d.width;
			while (px[i] > d.width) px[i] -= d.width;

			py[i] += vy[i];

			while (py[i] < 0) py[i] += d.height;
			while (py[i] > d.height) py[i] -= d.height;

		}
	}			
	
	public Dimension getPreferredSize() {
		return new Dimension(area, area);
	}
	
	public Dimension getMinimumSize() {
		return getPreferredSize();
	}
	

	public void mousePressed(MouseEvent e) {
		mx = e.getX();
		my = e.getY();
	}
	public void mouseReleased(MouseEvent e) {
		mx = -1;
		my = -1;
	}
	public void mouseEntered(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {
		mx = -1;  my = -1;
	}
	public void mouseMoved(MouseEvent e) {
		mx = e.getX();
		my = e.getY();
	}
	public void mouseDragged(MouseEvent e) {}
		

	
	public void run() {
		while(true) {
            try{Thread.sleep(50);} catch (InterruptedException e) {}
			incrementValues();
			repaint();
		}
	}
	
    public void start() {
        if (thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }

    public void stop() {
        if (thread != null) {
            thread.stop();
            thread = null;
        }
    }

}
	
