import java.applet.*;import java.awt.*;import java.awt.event.*;

public class Orbit extends Applet implements ActionListener, MouseMotionListener
{ int width, height;

 Button setButton;
 Button plotButton;
 TextField dtEntry;
 TextField numpointsEntry;
 TextField thetaEntry;
 TextField velocityEntry;
 TextField scaleEntry;
 CheckboxGroup modesGroup;
 Checkbox originRadio;
 Checkbox bigpictureRadio;

 Image spaceBuffer, orbitBuffer;
 Graphics board, o;

 public void init()
 {  width = getSize().width;  height = getSize().height;  setBackground( Color.white );

  spaceBuffer = createImage(width, height);
  board = spaceBuffer.getGraphics();
  board.setColor( Color.black );

  orbitBuffer = createImage(width, height);
  o = orbitBuffer.getGraphics();
  //o.setColor( Color.black );

  //addActionListener(this);
  addMouseMotionListener(this);

  setLayout(new FlowLayout());
  setButton = new Button("Update Values");
  plotButton = new Button("Plot Orbit");
  dtEntry = new TextField(".001");
  numpointsEntry = new TextField("100000");
  thetaEntry = new TextField("1/5");
  velocityEntry = new TextField("151/128");
  scaleEntry = new TextField("150");

  modesGroup = new CheckboxGroup();
  bigpictureRadio = new Checkbox("View All", modesGroup, true);
  originRadio = new Checkbox("View Origin", modesGroup, false);

  add(setButton);
  add(plotButton);
  add(new Label("Time Step:"));
  add(dtEntry);
  add(new Label("Iterations:"));
  add(numpointsEntry);
  add(new Label("Theta:"));
  add(thetaEntry);
  add(new Label("Initial Velocity:"));
  add(velocityEntry);
  add(scaleEntry);
  add(bigpictureRadio);
  add(originRadio);

  setButton.addActionListener(this);
  plotButton.addActionListener(this);
 }

 public void update( Graphics g )
 {  g.drawImage( spaceBuffer, 0, 0, this ); }

 public void paint(Graphics g)
 {
  g.drawString("Orbit complete. paint()" , 10, 300 );
  update(g); }

  double dtInput = .001, thetaInput = .25, velocityInput = (46653.0/65536.0), scaleInput=150;
  int numpointsInput = 0;

 public void actionPerformed(ActionEvent cosa)  {
  board.setColor(Color.white);
  board.fillRect(0,0,width,height);

  board.setColor(Color.black);


  if (cosa.getSource() == setButton)   // Actions of setButton
  {
   showStatus("Values updated.");
   board.drawString(dtEntry.getText(),300,265);

   try { dtInput = Double.parseDouble(dtEntry.getText()); }
   catch (NumberFormatException e) {board.drawString("Who throw's a shoe? Honestly.",500,265); }

   board.drawString(String.valueOf(dtInput),400,265);
  
   board.drawString(thetaEntry.getText(),300,280);

   try { numpointsInput = Integer.parseInt(numpointsEntry.getText()); }
   catch (NumberFormatException e) {board.drawString("Who throw's a shoe? Honestly.",500,265); }
   board.drawString(numpointsEntry.getText(),300,295);

   thetaInput = parseFraction( thetaEntry.getText());
   board.drawString("" + thetaInput,300,310);

   velocityInput = parseFraction( velocityEntry.getText());
   board.drawString("" + velocityInput,300,325);

   try { scaleInput = Double.parseDouble(scaleEntry.getText()); }
   catch (NumberFormatException e) {board.drawString("Who throw's a shoe? Honestly.",500,265); }

   if(originRadio.getState())
    board.drawString("View Origin",300,340);
   else
    board.drawString("View Big Picture",300,340);
  }  else if (cosa.getSource() == plotButton)   // Actions of plotButton  {
   board.drawString("Plot!" , 300, 250 );
   board.drawString("theta " + thetaInput , 300, 280 );
   plotOrbit(thetaInput, velocityInput, dtInput, numpointsInput, scaleInput, 0);
   board.drawString("Done!" , 300, 265 );  }
  repaint();
  //cosa.consume(); }

 public void mouseMoved( MouseEvent e )
 {  board.drawImage( spaceBuffer, 0, 0, this );  repaint();  e.consume(); }

 public void mouseDragged( MouseEvent e ) { }

 private void drawCenters(double scale)
 {
  int center;
  center = (int)(scale/2);
  board.setColor(Color.black);
  board.drawRect( (349+center), 174, 2, 2);
  board.drawRect( (349-center), 174, 2, 2);
 }

 private double parseFraction( String input )
 {
  double returnValue = 0.0;
  String errorReport = "";
  try { returnValue = Double.parseDouble(input); }
  catch (NumberFormatException e)
  {
   int fractionIndex = input.indexOf("/");
   double numerator=1.0, denominator=1.0;
   if(fractionIndex >= 0)
   {
    try{ numerator = Double.parseDouble(input.substring(0,(fractionIndex))); }
    catch (NumberFormatException e0) { errorReport += "Bad numerator. "; }

    try{ denominator = Double.parseDouble(input.substring((fractionIndex+1))); }
    catch (NumberFormatException e0) { errorReport += "Bad denominator. "; }
    if(denominator == 0)
     errorReport += "Division by zero.";
    else
     returnValue = numerator/denominator;
   }
   else
    errorReport += "Enter a number.";
  }
  if(errorReport.length() > 0)
  {
   showStatus(errorReport);
   return returnValue;
  }
  return returnValue;
 }

 private void plotOrbit(double theta, double v0, double dt, int numpoints, double scale, double origin)
 {
  //Constants
  double pi=Math.PI;
  double a=.5;
  //double cycle=Math.sqrt((Math.pow((2*pi),2)*Math.pow(a,3));
  double cycle=Math.sqrt((2*pi)*(2*pi)*a*a*a);

  //define dt in terms of number of iterations (nit)
  int nit = numpoints;
  /*int nit = 100000;
  int numcycles = 12;
  int ordernumpoints = 1000;
  double dt=cycle/(nit*1.0);
  int numpoints = nit*numcycles;
  //double writeevery = numpoints/ordernumpoints;
  */
  int writeevery = 0;
  //double scale=50.0;

  double rx=0.0;
  double ry=0.0;

  drawCenters(scale);

  //Initial Conditions
  //[Flower Orbit]
  /*theta = 1.0*pi/5.0;
  v0 = 1.0 + (23.0)/(128.0);*/


  theta *= -pi;
  double vx = v0*Math.cos(theta);
  double vy = v0*Math.sin(theta);

  double t=0.0;
  double zoom=10000.0;
  double xscale=1.0/Math.cos(theta);
  double yscale=1.0/Math.sin(theta);
  o.setColor(Color.black);

  //Main Program Loop
  
  int writenow = 0;
  double lastrx, lastry, r2;
  double[] newvars;
  newvars = new double[5];
  for(int i=0;i<numpoints;i++)
  {
   t=t+dt;
   lastrx = rx;
   lastry = ry;

   newvars = integrator(rx,ry,vx,vy,dt);
   rx = newvars[0];
   ry = newvars[1];
   vx = newvars[2];
   vy = newvars[3];

   r2 = (rx*rx+ry*ry);
   origin = 0;
   if(r2 < (v0*40.0/nit))
   {
    writenow = 0;
    origin = 1;
   }
   else if(r2 > 900.0)
    writenow = 1;
   else if(writeevery != 0)
    writenow = (writenow+1) % writeevery;

   if(writenow == 0)
   {
    board.drawLine((int)(scale*lastrx+350), (int)(scale*lastry+175), (int)(scale*rx+350), (int)(scale*ry+175));
    if(origin == 1 && 1==0)
     board.drawLine((int)(zoom*xscale*lastrx+350), (int)(zoom*yscale*lastry+175), (int)(zoom*xscale*rx+350), (int)(zoom*yscale*ry+175));
   }
   board.drawString("a" + i , 400, 315 );
  }
  board.drawString("Orbit complete. plotOrbit()" , 10, 315 );
  //board.drawImage( orbitBuffer, 0, 0, this );
 }











 private double[] acc(double rx, double ry)
 {
  double ax=.5, ay=0.0;
  double rma3, rpa3, axout, ayout;

  rma3 = Math.pow(Math.sqrt(Math.pow((rx-ax),2) + Math.pow((ry-ay),2)),3);
  rpa3 = Math.pow(Math.sqrt(Math.pow((rx+ax),2) + Math.pow((ry+ay),2)),3);
  axout = (-.5)*((rx-ax)/(rma3) + (rx+ax)/(rpa3));
  ayout = (-.5)*((ry-ay)/(rma3) + (ry+ay)/(rpa3));

  double[] returnvars = {axout, ayout};
  return returnvars;
 }

 private double[] macc(double rx, double ry, double dt)
 {
  double amx=.5, amy=0.0, apx=.5, apy=0.0;
  double rma, rpa, rma3, rpa3;
  double cm, cp, gradx, grady;
  double ax, ay, axout, ayout;

  double[] accvector;
  accvector = new double[2];

  accvector = acc(rx,ry);
  ax = accvector[0];
  ay = accvector[1];

  rma  = Math.sqrt(Math.pow((rx-amx),2) + Math.pow((ry-amy),2));
  rpa  = Math.sqrt(Math.pow((rx+amx),2) + Math.pow((ry+amy),2));
  rma3 = Math.pow(rma,3);
  rpa3 = Math.pow(rpa,3);

  amx = (rx-amx)/rma3;
  amy = (ry-amy)/rma3;
  apx = (rx-apx)/rpa3;
  apy = (ry-apy)/rpa3;

  cm = (2.0)/(rma3) + 3.0*rma*(amx*apx + amy*apy) - (1.0)/(rpa3);
  cp = (2.0)/(rpa3) + 3.0*rpa*(amx*apx + amy*apy) - (1.0)/(rma3);
  gradx = (-.5)*(cm*amx+cp*apx);
  grady = (-.5)*(cm*amy+cp*apy);
  axout = ax + ((1.0)/(48.0))*(dt*dt)*gradx;
  ayout = ay + ((1.0)/(48.0))*(dt*dt)*grady;

  double[] returnvars = {axout, ayout};
  return returnvars;
 }

 private double[] integrator(double rx, double ry, double vx, double vy, double dt)
 {
  double sixths = 1.0/6.0, pmass = .5;
  double ax0, ay0, ax1, ay1, ax2, ay2;
  double[] accvector;
  accvector = new double[2];

  accvector = acc(rx,ry);
  ax0 = accvector[0];
  ay0 = accvector[1];

  vx  = vx + sixths*ax0*dt;
  vy  = vy + sixths*ay0*dt;
  rx  = rx + 3*sixths*(vx/pmass)*dt;
  ry  = ry + 3*sixths*(vy/pmass)*dt;

  accvector = macc(rx,ry,dt);
  ax1 = accvector[0];
  ay1 = accvector[1];

  vx  = vx + 4*sixths*ax1*dt;
  vy  = vy + 4*sixths*ay1*dt;
  rx  = rx + 3*sixths*(vx/pmass)*dt;
  ry  = ry + 3*sixths*(vy/pmass)*dt;

  accvector = acc(rx,ry);
  ax2 = accvector[0];
  ay2 = accvector[1];

  vx  = vx + sixths*ax2*dt;
  vy  = vy + sixths*ay2*dt;

  double[] returnvars = {rx, ry, vx, vy, dt};
  return returnvars;
 }

}