import java.awt.event.*;
import javax.swing.*;
import org.opensourcephysics.controls.*;
import org.opensourcephysics.display.*;
import org.opensourcephysics.display.axes.*;
import org.opensourcephysics.frames.*;
import org.opensourcephysics.numerics.*;

/**
 * Plot our compass
 * Programmed to the soundtrack of Mr Horrible by They Might Be Giants. 
 * By Dave, stolen from opensourcephysics thingy
 */
public class Poincareapp extends AbstractAnimation implements InteractiveMouseHandler{
   final static double PI = Math.PI;    // defined for brevity
   PlotFrame phaseSpace = new PlotFrame("theta", "ang vel", "Phase space plot");
   PlotFrame poincare = new PlotFrame("theta", "ang vel", "Poincare plot");
   PlotFrame simu = new PlotFrame("X", "Y", "Compass");
   Compass compass = new Compass();
   Verlet odeMethod = new Verlet(compass);
   double[] xpoints=new double[5];
   double[] ypoints=new double[5];
   double x=0;
   double y=0;
   double dtime=0;
   double theta=0;
   double nstep=100;
   double istep=0;
   int colour=0;
   /**
    * Constructs a Poincareapp. Surprising really.
    */
   public Poincareapp() {
      poincare.setMarkerSize(0, 2); // smaller size gives better resolution
      phaseSpace.setMarkerSize(0,1);
      phaseSpace.setMessage("t="+0);
      phaseSpace.setName("P"); // This doesn't seem to get passed but I'm leaving it in incase I can work this out!
      phaseSpace.setInteractiveMouseHandler(this);
      simu.setMarkerSize(0,1);
      simu.setName("S");
      simu.setPreferredMinMax(-1,1,-1,1);
      simu.setInteractiveMouseHandler(this);
  }

   /**
   * Resets all parameters to their defaults.
   */
   public void resetAnimation() {
      control.setValue("Initial angle", 0.2);
      control.setValue("Angular velocity", 0.6);
      control.setValue("Damping", 0.0);                    // damping constant - damping proportional to v squared
      control.setValue("Frequency of field", 0.1);         // self explanatory
      control.setValue("Size of B field", 1);              // likewise
      control.setValue("Magnetic Moment", 1);              // and again
      control.setValue("Moment of inertia", 1);            // 
      control.setValue("dt", 0.1);		           // 
      poincare.clearData();
      poincare.repaint();
      simu.clearData();
      simu.repaint();
      phaseSpace.clearData();
      phaseSpace.repaint();
   }

   public void doStep() {
   istep=istep+1;
   double state[] = compass.getState();
       simu.clearData();
        odeMethod.step();
         if (state[0] > PI) {
              state[0] = state[0] - 2.0*PI;
              }
         else if (state[0] < -PI) {
              state[0] = state[0] + 2*PI;
              }
             phaseSpace.append(colour,state[0], state[1]);
	     phaseSpace.setMessage("t="+decimalFormat.format(state[2]));
     	     phaseSpace.repaint();
     	     x=Math.cos(state[0]);
	     y=Math.sin(state[0]);
	     for (double l=0;l<20 ;l++ )
	          {
	               simu.append(1,l/20*x,l/20*y);
	          }
	     simu.repaint();
      if (istep>nstep){
     poincare.append(colour,state[0], state[1]);
      poincare.setMessage("t="+decimalFormat.format(state[2]));
      poincare.repaint();
      istep=0;
      }
}
   /**
    * Initializes the animation and clears the plots.
    */
 public void initializeAnimation() {
     double theta = control.getDouble("Initial angle");           // initial angle
     double dtheta= control.getDouble("Angular velocity");        // initial angular velocity
     dtime= control.getDouble("dt");                              // timestep;
     compass.omega= control.getDouble("Frequency of field");      // initial angular velocity
     compass.gamma = control.getDouble("Damping");                // damping constant
     compass.B = control.getDouble("Size of B field");            // magnitude of B field
     compass.I = control.getDouble("Moment of inertia");          // Moment of inertia of compass
     compass.mu = control.getDouble("Magnetic Moment");           // Magnetic moment of compass
     compass.initializeState(new double[]{theta,dtheta,0});
     clear();
     odeMethod.setStepSize(dtime); // 
     nstep = Math.floor(2*PI/(dtime*compass.omega));       
     istep = 0; 
     colour=0;    
	 control.println("lambda= "+Math.sqrt(2*compass.mu*compass.B/(compass.I*compass.omega*compass.omega)));
     }

   /**
    * Clears the plots.
    */
   public void clear() {
      phaseSpace.clearData();
      poincare.clearData();
      phaseSpace.repaint();
      poincare.repaint();
      simu.clearData();
      simu.repaint();
   }
      public void handleMouseAction(InteractivePanel panel, MouseEvent evt){
         switch(panel.getMouseAction()){
             case InteractivePanel.MOUSE_PRESSED:
                  double Maxx=panel.getPreferredXMax(); // This is a very very ugly hack - just checks if we're in simu 
                  double Mx = panel.getMouseX();        // I do it because interactivepanel doesn't get passed names.
                  double My = panel.getMouseY();
				  int button=panel.getMouseButton();
                  if (Maxx==1)
                      {
                      double newtheta=Math.atan(Math.abs(My)/Math.abs(Mx));  // Just doing the angle thing 
                      if(Mx>0){
                          if(My>0){
                                theta=newtheta; 
			  }
                          if(My<0){
                                theta=-newtheta;
                          }
                       }
                       if(Mx<0){
                          if(My>0){
                                theta=PI-newtheta;
                          }
                          if(My<0){
                                theta=-PI+newtheta;
                          }
                       }    
                       compass.setTheta(theta);
                       compass.setdTheta(0);    
                       if(simu.isShowing()){
                          simu.clearData();
                          x=Math.cos(theta);
                          y=Math.sin(theta);
                          for (double l=0;l<100 ;l++ ){
                             simu.append(1,l/100*x,l/100*y);
                          }
                          simu.repaint();
                          }
                       }else{
                          compass.setTheta(Mx);         // OK now we know we're in phaseSpace theta is the X axis, 
                          compass.setdTheta(My);        // and dtheta is the Y axis. 
  
						  colour++;
                       if (button==1)
                       {
						   compass.setT(0);              // if you use button 1 set t=0, else don't
                       }
					   }
                    break;
                    }
                 }
   /**
    * Starts the Java application.
    * @param args  command line parameters
    */
    public static void main(String[] args) {
         Poincareapp app = new Poincareapp();
         AnimationControl control = new AnimationControl(app);
         app.setControl(control);
         control.addButton("clear","Clear");
    }
}

