//

import render.*;

public class Doll extends Actor
{
   static public boolean debug = false;

   static Material shadowColor = new Material();
   static Material shinyBlack  = new Material();
   static Material matteBlack  = new Material();
   static Material copper      = new Material();
   static Material gold        = new Material();
   static Material silver      = new Material();
   static Material violet      = new Material();
   Material skinColor = silver;
   Material eyeColor  = shinyBlack;
   Material hairColor = matteBlack;
   boolean isGirl = false;

   static {

      // THE FOLLOWING COLORS ARE DECLARED STATIC BECAUSE
      // THEY WILL BE THE SAME FOR ALL DOLLS

      double f=1.3,r=f*.8,g=f*.6,b=f*.24,S=1.5,A=.5;
      gold.setAmbient(A*r,A*g,A*b);
      gold.setDiffuse(r,g,b,2);
      gold.setSpecular(S*r,S*g,S*b,10);

      r = .7; g = .7; b = .8;
      silver.setAmbient(A*r,A*g,A*b);
      silver.setDiffuse(r,g,b,2);
      silver.setSpecular(S*r,S*g,S*b,10);

      r = .8; g = .5; b = .3; S = 2; A=.6;
      copper.setAmbient(A*r,A*g,A*b);
      copper.setDiffuse(r,g,b,2);
      copper.setSpecular(S*r,S*g,S*b,10);

      r = 1; g = 0; b = .7; S = 1.5; A=.5;
      violet.setAmbient(A*r,A*g,A*b);
      violet.setDiffuse(r,g,b,2);
      violet.setSpecular(S*r,S*g,S*b,10);

      shinyBlack.setAmbient(0,0,0);
      shinyBlack.setDiffuse(0,0,0);
      shinyBlack.setSpecular(1.5,1.5,1.5,10);

      matteBlack.setAmbient(0,0,0);
      matteBlack.setDiffuse(.3,.3,.3,2);
      matteBlack.setSpecular(.7,.7,.7,3);

      shadowColor.setAmbient(0,0,0);
      shadowColor.setDiffuse(0,0,0);
      shadowColor.setSpecular(0,0,0,1);
      shadowColor.setTransparency(.8);
   }

   // ALL THE PARTS OF THE DOLL

   public Geometry shadow, head, hair, body, nose, leftEye, rightEye, torso;
   public Geometry leftShoulder, rightShoulder, leftArm, rightArm, leftHand, rightHand;

   double height = 2;         // HOW TALL IS THE DOLL?
   double leftArmAngle  = 0;  // LEFT ARM SWING ANGLE
   double rightArmAngle = 0;  // RIGHT ARM SWING ANGLE

   // DECLARE ALL THE PARTS OF THE DOLL AND SET DEFAULT PROPORTIONS

   public Doll() {
      super();

      shadow = add();
      shadow.add().disk(10);
      shadow.add().disk(10);
      shadow.setMaterial(shadowColor);

      head = parts.add().ball(5);
      hair = head.add();
      hair.add().globe(16, 8, .15,.85, .25,1).setDoubleSided(true);
      nose = head.add().ball(3);
      leftEye = head.add().ball(2);
      rightEye = head.add().ball(2);

      body = parts.add();
      torso = body.add().pill(10,.8,.2);

      leftShoulder = body.add();
      leftShoulder.add().ball(3);
      leftShoulder.add().cylinder(5);
      leftShoulder.child[1].matrix.translate(0,0,1);
      leftShoulder.child[1].matrix.scale(.5,.5,1);

      rightShoulder = body.add();
      rightShoulder.add().ball(3);
      rightShoulder.add().cylinder(5);
      rightShoulder.child[1].matrix.translate(0,0,-1);
      rightShoulder.child[1].matrix.scale(.5,.5,1);

      leftArm = body.add().cylinder(8);
      rightArm = body.add().cylinder(8);

      leftHand = body.add().ball(3);
      rightHand = body.add().ball(3);
   }

   public Doll setGirl() {
      isGirl = true;
      return this;
   }

   public Doll setSkin(Material m) {
      skinColor = m;
      return this;
   }

   public Doll setSkin(double f) {
      skinColor = new Material();
      f = .5 + .5*f;
      double r = .8*f, g = .6*Math.pow(f,1.5), b = .5*f*f;
      skinColor.setAmbient(r/2,g/2,b/2);
      skinColor.setDiffuse(r,g,b);
      skinColor.setSpecular(g/2,g/2,g/2,5);
      return this;
   }

   public Doll setHeight(double h) {
      height = h;
      isNewPose = true;
      return this;
   }

   // GAZING AT OTHER OBJECTS

   double prevLookAngle = 0;
   Geometry prevLookObject = null;
   boolean atLookAngle = false;

   public boolean setGaze(Geometry g) {
      super.setGaze(g);
      if (g != prevLookObject) {
	 atLookAngle = false;
         prevLookObject = g;
	 return false;
      }
      atLookAngle |= Math.abs(prevLookAngle - getLookAngle(g)) < .02;
      return atLookAngle;
   }
   double getLookAngle(Geometry object) {
      double t = 0;
      if (object != null) {
         Matrix A = matrix, B = object.matrix;
         t = Math.atan2(A.get(2,3) - B.get(2,3), B.get(0,3) - A.get(0,3));
         t = (t - theta) / (2 * Math.PI);
      }
      return gw * 2*Math.PI * Math.max(-.25, Math.min(.25, t<-.5 ? t+1 : t>.5 ? t-1 : t));
   }

   // FACING OTHER OBJECTS

   double getFacingAngle(Geometry object) {
      double t = 0;
      if (object != null) {
         Matrix A = matrix, B = object.matrix;
         t = Math.atan2(A.get(2,3) - B.get(2,3), B.get(0,3) - A.get(0,3));
         t = (t - theta) / (2 * Math.PI);
      }
      return 2*Math.PI * Math.max(-.25, Math.min(.25, t<-.5 ? t+1 : t>.5 ? t-1 : t)) + theta;
   }

   // SETTING ARM POSITIONS

   public boolean setLeftArmAngle(double angle) {
      boolean isDone = isNear(angle, leftArmAngle);
      leftArmAngle = angle;
      return isDone;
   }
   public double getLeftArmAngle() {
      return leftArmAngle;
   }
   public boolean setRightArmAngle(double angle) {
      boolean isDone = isNear(angle, rightArmAngle);
      rightArmAngle = angle;
      return isDone;
   }
   public double getRightArmAngle() {
      return rightArmAngle;
   }

   double dTime = 0, prevTime = 0;

   public void animate(double time) {
      dTime = time - prevTime;
      prevTime = time;

      isNewLocation = true;
      isNewPose = true;
      super.animate(time);
   }

   public double animateLooking() {
      double lookAngle = getLookAngle(gazeObject);
      if (! atLookAngle)
         lookAngle = prevLookAngle + 2 * dTime * (lookAngle - prevLookAngle);
      prevLookAngle = lookAngle;
      return lookAngle;
   }

   public void computePose() {
      super.computePose();
      double h = (height-1)/2;

      head.matrix.identity();
      head.matrix.translate(0,height-.5,0);
      head.matrix.rotateY(animateLooking());
      head.matrix.scale(.5,.5,.5);

      hair.matrix.identity();
      if (isGirl) {
         hair.matrix.rotateX(-Math.PI/2);
         hair.matrix.rotateZ(-.2);
         hair.matrix.scale(1.15,1.15,1.15);
      }
      else
         hair.matrix.scale(0,0,0);

      nose.matrix.identity();
      nose.matrix.translate(1,0,0);
      nose.matrix.scale(.5,.4,.4);

      leftEye.matrix.identity();
      leftEye.matrix.translate(.7,.5,-.37);
      leftEye.matrix.scale(.3,.3,.3);

      rightEye.matrix.identity();
      rightEye.matrix.translate(.7,.5,.37);
      rightEye.matrix.scale(.3,.3,.3);

      body.matrix.identity();
      body.matrix.translate(0,h,0);

      torso.matrix.identity();
      torso.matrix.scale(.5,h,.5);
      torso.matrix.rotateX(Math.PI/2);

      leftShoulder.matrix.identity();
      leftShoulder.matrix.translate(0,.5*h+.2*h*h,-.72);
      leftArm.matrix.copy(leftShoulder.matrix);
      leftShoulder.matrix.scale(.2,.2,.2);
      leftArm.matrix.rotateZ(leftArmAngle);
      leftArm.matrix.translate(0,-h/2,0);
      leftHand.matrix.copy(leftArm.matrix);
      leftHand.matrix.translate(0,-h/2,0);
      leftHand.matrix.scale(.185,.185,.185);
      leftArm.matrix.scale(.2,h/2,.2);
      leftArm.matrix.rotateX(Math.PI/2);

      rightShoulder.matrix.identity();
      rightShoulder.matrix.translate(0,.5*h+.2*h*h,.72);
      rightArm.matrix.copy(rightShoulder.matrix);
      rightShoulder.matrix.scale(.2,.2,.2);
      rightArm.matrix.rotateZ(rightArmAngle);
      rightArm.matrix.translate(0,-h/2,0);
      rightHand.matrix.copy(rightArm.matrix);
      rightHand.matrix.translate(0,-h/2,0);
      rightHand.matrix.scale(.185,.185,.185);
      rightArm.matrix.scale(.2,h/2,.2);
      rightArm.matrix.rotateX(Math.PI/2);

      head.setMaterial(skinColor);
      hair.setMaterial(hairColor);
      leftEye.setMaterial(eyeColor);
      rightEye.setMaterial(eyeColor);
      leftHand.setMaterial(skinColor);
      rightHand.setMaterial(skinColor);

      shadow.matrix.identity();
      shadow.matrix.rotateY(Math.PI/4);
      shadow.matrix.translate(0,.04,-.045 * height);
      shadow.matrix.scale(.7,.02,.5 +.045 * height);
      shadow.matrix.rotateX(-Math.PI/2);
      shadow.child[1].matrix.identity();
      shadow.child[1].matrix.translate(0,0,2);
      shadow.child[1].matrix.scale(.94,.88,1);
   }
}