import vrml.*;
import vrml.node.*;
import vrml.field.*;
import java.lang.Math;
import java.util.Random;

public class control extends Script{

   private SFVec3f bug1pos, bug2pos, bug3pos, bug4pos, bug5pos, bug6pos;
   private SFVec3f bug7pos, bug8pos, bug9pos, bug10pos, bug11pos, bug12pos;
   private SFVec3f bug13pos;

   float xpos[],ypos[],xvel[],yvel[],xtarget[],ytarget[], xyzpos[];
   float orbit_centerx[],orbit_centery[],orbit_radius[],orbit_angle[];
   int type[];

   boolean wander[], follow[], followed[], orbit[];
   int follow_target[], orbit_target[];

   float testx, testy, randx, randy;
   int mindex, count;

   Random random;

   boolean done, collision;
 
   public void initialize() {
      bug1pos = (SFVec3f) getEventOut("bug1pos");
      bug2pos = (SFVec3f) getEventOut("bug2pos");
      bug3pos = (SFVec3f) getEventOut("bug3pos");
      bug4pos = (SFVec3f) getEventOut("bug4pos");
      bug5pos = (SFVec3f) getEventOut("bug5pos");
      bug6pos = (SFVec3f) getEventOut("bug6pos");
      bug7pos = (SFVec3f) getEventOut("bug7pos");
      bug8pos = (SFVec3f) getEventOut("bug8pos");
      bug9pos = (SFVec3f) getEventOut("bug9pos");
      bug10pos = (SFVec3f) getEventOut("bug10pos");
      bug11pos = (SFVec3f) getEventOut("bug11pos");
      bug12pos = (SFVec3f) getEventOut("bug12pos");
      bug13pos = (SFVec3f) getEventOut("bug13pos");

      xpos = new float[13];
      ypos = new float[13];
      xvel = new float[13];
      yvel = new float[13];
      type = new int[13];
      xtarget = new float[13];
      ytarget = new float[13];
      orbit_centerx = new float[13];
      orbit_centery = new float[13];
      orbit_radius = new float[13];
      orbit_angle = new float[13];

      follow = new boolean[13];
      followed = new boolean[13];
      wander = new boolean[13];
      orbit = new boolean[13];

      follow_target = new int[13];
      orbit_target = new int[13];

      xyzpos = new float[3];

      random = new Random();

      for (int j=0; j<13; j++) {
  	follow[j]=false;
        followed[j]=false;
        follow_target[j]=-1;
	wander[j]=true;
        xpos[j]=6.0f * (random.nextFloat() - 0.5f);
        ypos[j]=6.0f * (random.nextFloat() - 0.5f);
        xtarget[j]=6.0f * (random.nextFloat() - 0.5f);
        ytarget[j]=6.0f * (random.nextFloat() - 0.5f);        
      }

      type[0]=0; type[1]=0; type[2]=0; type[3]=0; type[4]=0; type[5]=0;
      type[6]=0; type[7]=1; type[8]=1; type[9]=1; type[10]=2; type[11]=2;
      type[12]=2;

      outputpositions();
   }

   private void outputpositions() {
      xyzpos[0] = xpos[0];
      xyzpos[1] = ypos[0];
      xyzpos[2] = 0.0f;
      bug1pos.setValue(xyzpos);

      xyzpos[0] = xpos[1];
      xyzpos[1] = ypos[1];
      xyzpos[2] = 0.0f;
      bug2pos.setValue(xyzpos);
           
      xyzpos[0] = xpos[2];
      xyzpos[1] = ypos[2];
      xyzpos[2] = 0.0f;
      bug3pos.setValue(xyzpos);

      xyzpos[0] = xpos[3];
      xyzpos[1] = ypos[3];
      xyzpos[2] = 0.0f;
      bug4pos.setValue(xyzpos);

      xyzpos[0] = xpos[4];
      xyzpos[1] = ypos[4];
      xyzpos[2] = 0.0f;
      bug5pos.setValue(xyzpos);

      xyzpos[0] = xpos[5];
      xyzpos[1] = ypos[5];
      xyzpos[2] = 0.0f;
      bug6pos.setValue(xyzpos);

      xyzpos[0] = xpos[6];
      xyzpos[1] = ypos[6];
      xyzpos[2] = 0.0f;
      bug7pos.setValue(xyzpos);

      xyzpos[0] = xpos[7];
      xyzpos[1] = ypos[7];
      xyzpos[2] = 0.0f;
      bug8pos.setValue(xyzpos);

      xyzpos[0] = xpos[8];
      xyzpos[1] = ypos[8];
      xyzpos[2] = 0.0f;
      bug9pos.setValue(xyzpos);

      xyzpos[0] = xpos[9];
      xyzpos[1] = ypos[9];
      xyzpos[2] = 0.0f;
      bug10pos.setValue(xyzpos);

      xyzpos[0] = xpos[10];
      xyzpos[1] = ypos[10];
      xyzpos[2] = 0.0f;
      bug11pos.setValue(xyzpos);

      xyzpos[0] = xpos[11];
      xyzpos[1] = ypos[11];
      xyzpos[2] = 0.0f;
      bug12pos.setValue(xyzpos);

      xyzpos[0] = xpos[12];
      xyzpos[1] = ypos[12];
      xyzpos[2] = 0.0f;
      bug13pos.setValue(xyzpos);
   }

   private void calctarget(int index) {
        xtarget[index] = 6.0f * (random.nextFloat() - 0.5f);
        ytarget[index] = 6.0f * (random.nextFloat() - 0.5f);
   }

   private void calcvelocities() {
        float dx, dy, mag, velx, vely;
        int j;

        for (j=0;j<13;j++) {
            dx = xtarget[j] - xpos[j];
            dy = ytarget[j] - ypos[j];
            mag = dx * dx + dy * dy;
            mag = (float)Math.sqrt(mag);
            xvel[j] = 0.05f * ((1.0f/mag) * dx);
            yvel[j] = 0.05f * ((1.0f/mag) * dy);
        }
   }

   private float dist(float a, float b, float c, float d) {
        float x;
        x = ((c - a) * (c - a)) + ((d - b) * (d - b));
        x = (float)Math.sqrt(x);
        return x;
   }

   private float mindist(int index, float x, float y) {
        float min = 100f;
        float test;
        for (int i = 0; i < 13; i++) {
           if (index != i) {
              test = dist(x,y,xpos[i],ypos[i]);
              if (test < min) { min = test; mindex = i; }
           }
        }
        return min;
   }

   private void orbit_update(int index) {
        float testx, testy;

        orbit_angle[index] = orbit_angle[index] + 0.15f;
        testx = orbit_centerx[index] + (float)Math.cos(orbit_angle[index]) * orbit_radius[index];
        testy = orbit_centery[index] + (float)Math.sin(orbit_angle[index]) * orbit_radius[index];
        if (mindist(index,testx,testy) < orbit_radius[index] && mindex != orbit_target[index] && (type[mindex] == 0 || type[mindex] == 2)) {
            followed[orbit_target[index]]=false;
            wander[orbit_target[index]]=true;
            if (!followed[mindex]) {
                wander[mindex]=false;
                followed[mindex]=true;
                orbit_centerx[index]=xpos[mindex];
                orbit_centery[index]=ypos[mindex];
                orbit_radius[index] = 0.4f;
                orbit_angle[index]=(float)Math.atan2(ypos[index]-ypos[mindex],xpos[index]-xpos[mindex]);
                orbit_target[index]=mindex;
            } else {
                wander[index] = true;
                orbit[index] = false;
            }
        }
        else {                           
            xpos[index]=testx;
            ypos[index]=testy;
        }
   }

   private void follow_update(int index) {
        float testx, testy;
        int j;

        if (dist(xpos[index],ypos[index],xpos[follow_target[index]],ypos[follow_target[index]]) > 0.5f) {
           followed[follow_target[index]]=false;
           wander[index]=true;
           follow[index]=false;
        }

        testx = xpos[index] + xvel[follow_target[index]];
        testy = ypos[index] + yvel[follow_target[index]];
        j=0;
        while (mindist(index,testx,testy) < 0.4f && j < 5) {
           j++;
           calctarget(follow_target[index]);
           calcvelocities();
           testx = xpos[index] + xvel[follow_target[index]];
           testy = ypos[index] + yvel[follow_target[index]];
        }
        xpos[index] = testx;
        ypos[index] = testy;
   }

   private void wander_update(int index) {
        float testx, testy;
        int j;

        testx = xpos[index] + xvel[index];
        testy = ypos[index] + yvel[index];

        if (dist(xpos[index], ypos[index], xtarget[index], ytarget[index]) < 0.2f)
           calctarget(index);
        else if (mindist(index,testx,testy) < 0.5f && type[index] == 1 && !followed[mindex] && type[mindex] == 0) {
                wander[index]=false;
                follow[index]=true;
                followed[mindex]=true;
                follow_target[index]=mindex;
        }
        else if (mindist(index,testx,testy) < 0.48f && type[index] == 2 && !followed[mindex] && type[mindex] == 0) {
                wander[mindex]=false;
                wander[index]=false;
                followed[mindex]=true;
                orbit[index]=true;
                orbit_centerx[index]=xpos[mindex];
                orbit_centery[index]=ypos[mindex];
                orbit_radius[index] = 0.4f;
                orbit_angle[index]=(float)Math.atan2(ypos[index]-ypos[mindex],xpos[index]-xpos[mindex]);
                orbit_target[index]=mindex;
        }
        else if (mindist(index,testx,testy) < 0.42f && type[mindex] == 1 && !followed[index] && type[index] == 0) {
                followed[follow_target[mindex]] = false;
                follow_target[mindex] = index;
                followed[index] = true;
        }
        else {
           calcvelocities();
           j = 0;
           while (mindist(index, testx, testy) < 0.4f && j < 5) {
                j++;
                calctarget(index);
                calcvelocities();
                testx = xpos[index] + xvel[index];
                testy = ypos[index] + yvel[index];
           }
        }

        xpos[index] = xpos[index] + xvel[index];
        ypos[index] = ypos[index] + yvel[index];
   }

   private void set_fraction (float frac) {
        for (int i = 0; i < 13; i++) {
            if (wander[i])
                wander_update(i);
            else if (follow[i])
                follow_update(i);
            else if (orbit[i])
                orbit_update(i);
            outputpositions();
        }                
   }

   public void processEvent (Event e) {
      String EventName = e.getName();
      
      if (EventName.equals("set_fraction"))
         set_fraction(((ConstSFFloat)e.getValue()).getValue()); 
   }
}