import java.awt.*;
import java.applet.*;
import java.net.URL;


public class BlindFlies extends java.applet.Applet implements Runnable {
	Image		im;
	Graphics	offscreen;
	FlySet		flySet;
	int		width;
	int		height;
        int             goodies,baddies,num,size,speed;

	Thread		mainThread;
	boolean		pause;


	public Dimension minimumSize() {
		return new Dimension(width, height);
		}

	public Dimension preferredSize() {
	 	return minimumSize();
		}

	public void pause() {
		pause = !(pause);
		}

	public void init() {
		int i, h, w;

  		goodies	= Integer.parseInt(getParameter("GOODIES"));
  		baddies	= Integer.parseInt(getParameter("BADDIES"));
                width   = Integer.parseInt(getParameter("WIDTH"));
                height  = Integer.parseInt(getParameter("HEIGHT"));
		size	= Integer.parseInt(getParameter("SIZE"));
                speed   = Integer.parseInt(getParameter("SPEED"));


                pause   = false;

		setLayout(new BorderLayout());

		setFont(new Font("Helvetica", Font.PLAIN, 18));

		add("South", new Label("'p' to pause, 'r' to restart",
								Label.CENTER));
		resize(width, height);

		try {
			im = createImage(width, height);
			offscreen = im.getGraphics();
			}
		catch (Exception e) {
			offscreen = null;
			}

                flySet  = new FlySet(this, size, goodies, baddies, width, height, speed);
		flySet.init();
		}

	public void paintCanvas (Graphics g) {
		if (pause) { return; }
		// Do force calculation
		flySet.calcNext();

		// erase old particles
                flySet.rub(g,getBackground());

		// update and draw new ones.
		flySet.moveToNext();
                flySet.draw(g);
		}

	public void update(Graphics g) {
		paint(g);
	}

	public void paint(Graphics g) {
		if (offscreen != null) {
			paintCanvas(offscreen);
			g.drawImage(im, 0, 0, this);
		} else {
			paintCanvas(g);
		}
	}

	public boolean handleEvent(Event e) {
		if ( e.id == Event.KEY_PRESS) {
			if (e.key == 'r') {
				destroy();
				init();
			} else if (e.key == 'p') {
				pause();
			}
		}
		return true;

	}

	public void start() {
		if (mainThread == null) {
			mainThread = new Thread(this);
			mainThread.start();
		}
	}

	public void stop() {
		mainThread.stop();
		mainThread = null;
	}


	public void run() {
		// Ooo!  Multithreaded!
		while (mainThread!=null) {
                        try {mainThread.sleep(1);}
			catch (InterruptedException e) {}
			repaint();
			}
		mainThread = null;
	}

}

class FlySet {
	int	num,goodies;
	int	i,j,k;
	Fly[]	fly;
        int     size,width,height,speed;
        int     honk;
        BlindFlies      parent;

        FlySet(BlindFlies parent, int size, int goodies, int baddies, int width, int height, int speed){
                this.parent=parent;
                this.size=size;
                num=goodies+baddies;
		this.goodies=goodies;
		this.width=width;
                this.height=height;
                this.speed=speed;
                honk=-1;
		}

	public void init(){
		int	i;
		fly=new Fly[num];
                for(i=0;i<num;i++) fly[i]=new Fly(this,size,width,height,speed);
		}

        public void draw(Graphics g){
		int	i;
		g.setColor(Color.blue);
                for (i=0; i < num; i++){
                        if(i<goodies) g.setColor(Color.blue);
                        if(i>=goodies) g.setColor(Color.red);
                        if(i==honk){
                                g.setColor(Color.yellow);
                                if(i<goodies) play(getCodeBase(),"hello.au");
                                else play(getCodeBase(),"boo.au");
                                }
                        fly[i].draw(g,size);
                        }
                honk=-1;
		}

        public void rub(Graphics g, Color c){
		int	i;
                g.setColor(c);
                for (i=0; i < num; i++) fly[i].draw(g,size);
		}


        public void calcNext(){
                int     i,t;
                for(i=0;i<num;i++){
                        if(fly[i].calcNext(width,height,size))
                                honk=i;
                        }
                if(honk>-1)
                        for(i=0;i<num;i++)
                                fly[honk].honk(fly[i],honk>=goodies&&i<goodies,speed);
                }

	public void moveToNext(){
                int     i;
                for(i=0;i<num;i++){
                        fly[i].moveToNext();
                        }
                }



    public URL getCodeBase(){
        return parent.getCodeBase();
    }

    public void play(URL a, String b){
        parent.play(a,b);
    }


	}


class Fly {
    int     x,y,sx,sy,nx,ny;
    int     speed;
    float	ang,nang;
    FlySet  parent;

    Fly(FlySet parent, int size, int width, int height, int speed){
            x=(int)(size+Math.random()*(width-size*2));
            y=(int)(size+Math.random()*(height-size*2));
		    ang=(float)Math.random()*360-180;
            this.speed=speed;
            sx=width/2;
            sy=height/2;
            this.parent=parent;
		}

	public void draw(Graphics g, int size){
                g.fillOval(x-size, y-size, size*2, size*2);
                antena(g,size,-30);
                antena(g,size,30);
		}

	private void antena(Graphics g, int size, float direction){
		g.drawLine(x,y,
                        (int)(x+2*size*Math.cos((ang+direction)/360*2*Math.PI)),
                        (int)(y+2*size*Math.sin((ang+direction)/360*2*Math.PI))
                        );
		}

        public boolean calcNext(int width, int height, int size){
                float   bang,tang;
                int     dx,dy,h;

                nx=x+(int)(Math.cos(nang/360*2*Math.PI)*speed);
                ny=y+(int)(Math.sin(nang/360*2*Math.PI)*speed);
                dx=sx-nx;
                dy=sy-ny;

                nang=ang;
                if(nx+size>width||nx<size) nang=norm(180-nang);
                if(ny+size>height||ny<size) nang=-nang;
                if(nang!=ang) { //if bouncing don't move, just change angle
                        play(getCodeBase(),"houch.au");
                        nx=x;
                        ny=y;
                        }
                else { // otherwise inc position and modify angle
                        if(dx==0) tang=90*((dy>=0)?1:-1) ;
                        else{
                                tang=(float)(Math.atan((float)dy/(float)dx)/Math.PI*180);
                                if(dx<0) tang+=180;
                                }
                        tang=norm(norm(tang)-nang)/10;
                        nang=norm(nang+tang);
                        }

                h=(int)(Math.sqrt(dx*dx+dy*dy)/2);
                if(h>200)h=200;
                if(h<10)h=10;
                if((int)(Math.random()*h)==(int)(Math.random()*h))
                        return true;
                return false;
                }

        private float norm(float ang){
                if (ang>180) return ang-360;
                if (ang<-180) return ang+360;
                return ang;
                }

        public void honk(Fly victim, boolean escape, int speed){
                int     dx,dy;
                float   dist;
                if(this!=victim){
                        if(escape){
                                dx=victim.x-x;
                                dy=victim.y-y;
                                dist=(float)Math.sqrt(dx*dx+dy*dy);
                                victim.sx=(int)((dx/dist)*1000);
                                victim.sy=(int)((dy/dist)*1000);
                                victim.speed=speed*2;
                                }
                        else{
                                victim.sx=x;
                                victim.sy=y;
                                victim.speed=speed;
                                }
                        }
                }




        public void moveToNext(){
                x=nx;
                y=ny;
                ang=nang;
                }


    public URL getCodeBase(){
        return parent.getCodeBase();
    }

    public void play(URL a, String b){
        parent.play(a,b);
    }



	}

