/* Simulation section for YHang applet */
/* Simeon Warner - 20May96 */

import java.awt.*;

public class Simulation extends Thread {
  final int aradius=10;
  final int adiam = aradius+aradius;

  int xLeft, yLeft;
  int xRight, yRight;
  int xKnot, yKnot;
  int lenLeftSqd, lenRightSqd;
  boolean slackLeft, slackRight, stable;
  float weight;

  ControlPanel controls;
  DiagramPanel diagram; 
    
  Simulation(ControlPanel c, DiagramPanel d) {
    controls = c;
    diagram = d;
  }

  public void init(float weight) {
    slackLeft = slackRight = false;
    this.weight=weight;
    controls.setWeight(weight);
  }

  public void run() {
    int lSqd, rSqd;		// distances to left and right anchors
    int xk, yk;			// working copies of knot coords
    int xkLast=0, xkLastLast=0; // previous knot x coords
    int downBadness, diagLBadness, diagRBadness, acrossBadness;
    while (true) {
      //System.out.println("Simulation thread running.");
      try {    
        this.sleep(50);
        // First check to see if the knot is stable
        lSqd=(xKnot-xLeft)*(xKnot-xLeft)+(yKnot-yLeft)*(yKnot-yLeft);
        rSqd=(xKnot-xRight)*(xKnot-xRight)+(yKnot-yRight)*(yKnot-yRight);
        slackLeft=(lSqd<lenLeftSqd);
        slackRight=(rSqd<lenRightSqd);
        if ((xKnot>xLeft) && (xKnot<xRight)) {
          if ( ((xKnot-xLeft)*(yLeft-yRight))>=((xRight-xLeft)*(yLeft-yKnot)) &&
               (!slackLeft) && (!slackRight) ) stable=true; 
        } else {
          if ((xKnot==xLeft) && (xKnot==xRight)) {
            stable=( !slackLeft && (yKnot>=yLeft) ) || ( !slackRight && (yKnot>=yRight) );
          } else if ((xKnot==xLeft) && (yKnot>=yLeft)) stable=!slackLeft;
          else if ((xKnot==xRight) && (yKnot>=yRight)) stable=!slackRight;
        }
        // If knot is unstable then move, else go to sleep.
        if ( stable ) {
          // Knot stable, sleep
          //System.out.println("Stable, going to sleep");
          diagram.repaint();
          this.suspend();
        } else {
          // Must move knot either down, diagonally or sideways
          // Make local copies of xKnot and yKnot
          xk=xKnot; yk=yKnot;
          // Try to make knot fall down
          if ((downBadness=tryMove(xk,yk+1))==Integer.MAX_VALUE) {
            yk++;
          } else {
            //Check for knot `outside' anchors
            if (xk<xLeft) {
              if (tryMove(xk+1,yk)!=Integer.MAX_VALUE) {
                xk++;
              } else if (tryMove(xk+1,yk+1)!=Integer.MAX_VALUE) {
                xk++; yk++;
              } else {
                yk++;
              }
            } else if (xk>xRight) {
              if (tryMove(xk-1,yk)!=Integer.MAX_VALUE) {
                xk--;
              } else if (tryMove(xk+1,yk+1)!=Integer.MAX_VALUE) {
                xk--; yk++;
              } else {
                yk++;
              }
            } else {
              // knot in between anchors, use which rope is tight to
              // determine which way to go
              if (!slackLeft) {
                diagLBadness=tryMove(xk-1,yk+1,true,false);
                acrossBadness=tryMove(xk-1,yk,true,false);
                if ((downBadness<diagLBadness) && (downBadness<acrossBadness)) {
                  yk++;
                } else if (diagLBadness<acrossBadness) {
                  xk--; yk++;
                } else {
                  xk--;
                }
              } else if (!slackRight) {
                diagRBadness=tryMove(xk+1,yk+1,false,true);
                acrossBadness=tryMove(xk+1,yk,false,true);
                if ((downBadness<diagRBadness) && (downBadness<acrossBadness)) {
                  yk++;
                } else if (diagRBadness<acrossBadness) {
                  xk++; yk++;
                } else {
                  xk++;
                }
              } else {
                yk++;
              }
            }
          } 
          setKnot(xk,yk);
          xkLastLast=xkLast;
          xkLast=xk;
          diagram.repaint();
        }
      } catch (InterruptedException ignored) {}
    }
  }


  private int tryMove(int xk, int yk) {
    return tryMove(xk,yk,false,false);
  }

  private int tryMove(int xk, int yk, boolean onLeft, boolean onRight) {
    int dl,dr,badness;
    dl=(xk-xLeft)*(xk-xLeft)+(yk-yLeft)*(yk-yLeft)-lenLeftSqd;
    dr=(xk-xRight)*(xk-xRight)+(yk-yRight)*(yk-yRight)-lenRightSqd;
    if ((dl>0) && (dr>0)) {
      badness=dl*dr;
    } else if (dl>0) {
      badness=dl*100;
      if (onRight) badness=badness*100;
    } else if (dr>0) {
      badness=dr*100;
      if (onLeft) badness=badness*100;
    } else {
      badness=Integer.MAX_VALUE;
    }
    return badness;
  }

  public synchronized void setAngle(float angle) {
    float s, c, qa, qb, qc, qq, x1, x2, dy;
    // make sure left is left etc.
    sortAnchors();
    // make rope hang within bound xLeft and xRight
    if (xKnot<xLeft) xKnot=xLeft;
    if (xKnot>xRight) xKnot=xRight;
    // do some sums to work out where the knot should be
    if (xRight==xLeft) {
      yKnot=(yLeft+yRight)/2;
    } else if (angle>(float)179.999) {
      // Normal algorithm not good for 180 degrees!
      yKnot=yLeft+((yRight-yLeft)*(xKnot-xLeft))/(xRight-xLeft);
    } else {
      dy=yRight-yLeft; x1=xKnot-xLeft; x2=xRight-xKnot;
      s=(float)Math.sin(angle*Math.PI/180.0);
      c=(float)Math.cos(angle*Math.PI/180.0);
      qa=s; qb=s*dy - c*(x1+x2); qc=-(s*x1*x2+c*dy*x2);
      qq= qb*qb - (float)4.0*qa*qc;
      if (qq<0.0) {
        System.out.println("Program error - qq<0.");
        qq=(float)0.0;
      }
      yKnot=yRight+(int)((-qb+(float)Math.sqrt(qq))/(qa+qa));
    }
    controls.setAngle(angle);
    diagram.repaint();
  }

  public synchronized void setWeight(float w) {    
    this.weight=w;
    diagram.repaint();
  }

  public synchronized float getWeight() {
    return this.weight;
  }

  public synchronized void setLeft(int x, int y) {
    xLeft=x; yLeft=y;
  }

  public synchronized void setRight(int x, int y) {
    xRight=x; yRight=y;
  }

  public synchronized void setKnot(int x, int y) {
    xKnot=x; yKnot=y;
    controls.setAngle(getAngle());
  }

  public synchronized float getAngle() {
    float al,ar,angle;
    al=(float)Math.atan2(yKnot-yLeft,xLeft-xKnot);
    ar=(float)Math.atan2(yKnot-yRight,xRight-xKnot);
    angle=(al-ar)*(float)(180.0/Math.PI);
    if (angle<0.0) angle=angle+(float)360.0;
    return(angle);
  }

  public void checkSlack() {
    sortAnchors();
    // set lengths (squared) whether slack or not
    lenLeftSqd=(xKnot-xLeft)*(xKnot-xLeft)+(yKnot-yLeft)*(yKnot-yLeft);
    lenRightSqd=(xKnot-xRight)*(xKnot-xRight)+(yKnot-yRight)*(yKnot-yRight);
    // now think about whether ropes are slack (unstable)
    if ((xKnot<xLeft) || (xKnot>xRight) ||
        (((xKnot-xLeft)*(yLeft-yRight))<((xRight-xLeft)*(yLeft-yKnot))) ) {
      slackLeft=true; slackRight=true; stable=false;
      //System.out.println("Slack");
    }      
  }

  private synchronized void sortAnchors() {
    int j;
    boolean q;
    if (xLeft>xRight) {
      j=xLeft; xLeft=xRight; xRight=j;
      j=yLeft; yLeft=yRight; yRight=j;
      j=lenLeftSqd; lenLeftSqd=lenRightSqd; lenRightSqd=j;
      q=slackLeft; slackLeft=slackRight; slackRight=q;
    }
  }
 
}
















