//

import render.*;

public class Actor extends Geometry
{

   static public void clearActors() { nActors = 0; }

//----- STUFF THAT'S UNIQUE TO EACH INDIVIDUAL Actor

   // CONSTRUCTOR

   public Actor() {
      index = nActors++;
   }

   public Actor setSize(double X, double Y, double Z) {
      sizeX = X; sizeY = Y; sizeZ = Z; setP(); return this;
   }
   public Actor setSize(double Size) {
      sizeX = sizeY = sizeZ = Size; setP(); return this;
   }
   public Actor setPosition(double X, double Z) {
      x = X; z = Z; setP(); return this;
   }
   public Actor setGaze(Geometry g) {
      Matrix m = g.getMatrix();
      return setGaze(m.get(0,3),m.get(1,3),m.get(2,3));
   }
   public Actor setGaze(double X, double Y, double Z) {
      gx = X;
      gy = Y;
      gz = Z;
      return this;
   }
   public Actor setGazeWeight(double W) {
      gw_target = W;
      return this;
   }
   public Actor setDirection(double Theta) {
      theta = Theta; return this;
   }
   public Actor avoidActors(boolean state) {
      avoidingActors = state; return this;
   }
   public Actor avoidWalls(boolean state) {
      avoidingWalls = state; return this;
   }
   public Actor setThrottle(double t) {
      throttle = t; return this;
   }

   void setP() {
      p[index][AX] = p[index][BX] = x;
      p[index][AZ] = p[index][BZ] = z;
      p[index][SIZE] = sizeZ;
   }

   // ANIMATE ONE FRAME

   public void animate(double time) {

      // COMPUTE ELAPSED TIME SINCE LAST FRAME

      if (prevTime == 0) prevTime = time - 1;
      elapsed = time - prevTime;
      prevTime = time;

      // SMOOTH OUT TIME-VARYING CONTROL PARAMETERS

      gw = smooth(gw, gw_target);

      // TRAVEL FORWARD

      double travel = sizeZ * getTravel(elapsed);
      double tz = travel * Math.cos(theta);
      double tx = travel * Math.sin(theta);

      x += tx * throttle;
      z += tz * throttle;

      setP();
      p[index][BX] = x + tx * throttle;
      p[index][BZ] = z + tz * throttle;

      // TURN AWAY FROM OBSTACLES

      double R = repulse(x,z,sizeZ,index);
      if (R > 0) {
         double Rx = (repulse(x+.01,z,sizeZ,index) - R) / .01;
         double Rz = (repulse(x,z+.01,sizeZ,index) - R) / .01;
         if (Rx*tx > -Rz*tz)                 // IF FACING WALL
	    theta += Rx*tz < Rz*tx ? R : -R; // TURN MORE AWAY
      }

      // SET TRANSLATION, ROTATION AND SCALE

      matrix.identity();
      matrix.translate(x,0,z);
      matrix.rotateY(Math.PI + theta);
      matrix.scale(sizeX,sizeY,sizeZ);
   }

   double smooth(double t, double target) {
      return lerp(2 * elapsed, t, target);
   }

   // COMPUTE HOW FAR FORWARD ACTOR TRAVELS IN A SMALL TIME INTERVAL

   public double getTravel(double elapsed) { return 0; }

   // INSTANCE DATA

   int index;
   double elapsed=0, prevTime=0, x=0, z=0, theta=0, sizeX=1, sizeY=1, sizeZ=1;

   double gx=0, gy=0, gz=0, gw=0;    // GAZE X,Y,Z,WEIGHT
   double gw_target=0;

   boolean avoidingActors = true, avoidingWalls = true;
   double throttle = 1;

//----- DEFINING STATIC WALLS IN THE SCENE FOR ALL ACTORS TO AVOID

   // ADD A WALL, OR CLEAR ALL WALLS

   static public void addWall(double wall[]) { w[nWalls++] = wall; }
   static public void clearWalls() { nWalls = 0; }

   double repulse(double x, double z, double size, int index) {
      double R = 0;

      // REPULSION FROM ALL WALLS

      if (avoidingWalls)
         for (int i = 0 ; i < nWalls ; i++) {
            int n = w[i].length/2 - 1;
            for (int j = 0 ; j < n ; j++)
               R += repulse(w[i][2*j  ]-x, w[i][2*j+1]-z,
	                    w[i][2*j+2]-x, w[i][2*j+3]-z, j==0, j==n-1, size);
         }

      // REPULSION FROM OTHER POLLYS

      if (avoidingActors)
         for (int i = 0 ; i < nActors ; i++)
            if (i != index)
	       R += repulse(p[i][AX]-x,p[i][AZ]-z,
	                    p[i][BX]-x,p[i][BZ]-z,true,true,.6*(size+p[i][SIZE]));

      return R;
   }

   // COMPUTE REPULSION FORCE FROM ONE WALL SECTION

   static double repulse(double ax,double az,double bx,double bz,
                         boolean aE,boolean bE, double size) {

      // COMPUTE UNIT VECTOR BETWEEN THE WALL'S TWO END POINTS

      double dx = bx - ax, dz = bz - az;
      if (dx == 0 && dz == 0) dz = .1;
      double len = Math.sqrt(dx*dx + dz*dz);
      dx /= len;
      dz /= len;

      // REPULSION DROPS OFF WITH DISTANCE AWAY FROM PLANE OF WALL

      double t = ax * dz - az * dx;
      t = Math.max(0, 2.2*size - Math.abs(t)) / (2.2*size);

      // REPULSION ALSO DROPS OFF AT THE TWO ENDS OF THE WALL

      return t*t * Math.max(0,Math.min(1, (aE?1:.5) - .5*(ax*dx + az*dz))) *
                   Math.max(0,Math.min(1, (bE?1:.5) + .5*(bx*dx + bz*dz))) ;
   }

   static double lerp(double t, double a, double b) { return a + t * (b - a); }

   // STATIC DATA

   static int nWalls = 0;
   static double w[][] = new double[100][];

   final static int AX=0,AZ=1,BX=2,BZ=3,SIZE=4;

   static int nActors = 0;
   static double p[][] = new double[100][5];
}