//
// SpringMass/SpringMass.java       Version 0.02  June 10, 1996
//
// This program is UNDER CONSTRUCTION.
//
// For copyright information contact Bryan Tracey (traceb@rpi.edu)
//
//  Java Applet which models, animates, and analyzes a Spring-Mass System.
//
//  Heavily based on SpringPendulum.java
//  Developed by the members of "Java 65.2961" 
//  at Rensselaer Polytechnic Institute. Troy, NY.
//

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

public class SpringMass extends Applet implements Runnable
{
boolean sigStart    = false;    // UI signal to start simulating.
boolean sigStop     = false;    // UI signal to stop the output.
boolean sigPause    = false;    // UI signal to toggle-pause the output.
boolean simMode     = false;    // flag: Simulation or Initial Condition mode
public int numDatums       =   290;    // number of data points to calculate
DataBuffer buffer   = new DataBuffer(5, numDatums);          
MathEngine  s1;
int         debugLev;           // debugging level, 0 to 5
Thread      TRunning;           // thread control; stops applet when stop()
Label l2, l3, l4;
double scaleConstant  =   0.0;    // Constant used to scale the output
boolean damping = true; // from parameter if true add damping scroll
  
// Gridbag panels
//  These are the three panels which contain/are the output thread/objects.
//  They occupy the upper half of the applet window.
MakeAnimPanel   animPanel;
MakeGraph1Panel graph1Panel;
MakeGraph2Panel graph2Panel; 

//
//  Control buttons
//  Need to declare them up here to get proper scope, but defined in init().
//
Button startBut, pauseBut, stopBut;
Choice sleepTime;
Choice material; 
Scrollbar massOfBobBar, springConstantBar, springLength;
double msb = 1.96;
double ksb = 150.0;
double equil = .12805;
// Range for physical parameters
// curmb, cursc, cursl are the default values and are equal to the physical
// system in the mathlinks demo.
// Max values must all be different
final int maxmb = 300;  
final int minmb = 100;
final int curmb = 196;
final int maxsc = 2000;
final int minsc = 1500;
final int cursc = 1750;
final int maxsl = 450;
final int minsl = 0;
final int cursl = 27;
 
  
public void init()
{
    resize(600, 500);        // width, height of applet window

    //
    // Get parameters here.  They are: 
    // debugLev
    //
    
    String SdebugCh = getParameter("debug");
    debugLev=(SdebugCh==null) ? (0) : (Integer.valueOf(SdebugCh).intValue());
    System.out.println("Debug level is: "+debugLev);

    
if(Integer.parseInt(getParameter("damping")) == 0)
	damping = false;
else 
    damping = true;
   
    //
    // BDT adding Gridbag Layout area 
    // Code spliced in from Tom Finant's work.
    //

    // sets up a GridBagLayout and creates object to store constraints
    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    setLayout(gridbag);
    setFont(new Font("Helvetica", Font.PLAIN, 10));

    // MakeAnimPanel
    c.gridwidth = 2; 
    c.gridheight = 1;
    animPanel = new MakeAnimPanel(buffer, "Animation", this); 
    animPanel.setBackground(Color.lightGray);
    gridbag.setConstraints(animPanel, c); 
    add(animPanel);

 c.gridwidth = 3;
    Panel twoGraphPanel = new Panel();
    twoGraphPanel.setLayout(new GridLayout(2,1));
    graph1Panel = new MakeGraph1Panel(buffer, "Displacement and Velocity vs. Time", 
                                      gridbag, c);
    graph1Panel.setBackground(Color.lightGray); 
    twoGraphPanel.add(graph1Panel);
    graph2Panel = new MakeGraph2Panel(buffer, "Velocity vs. Displacement", gridbag, c);
    graph2Panel.setBackground(Color.lightGray);
    twoGraphPanel.add(graph2Panel);

    c.gridwidth = 3;
    c.weightx = 5.0;
    c.gridheight = GridBagConstraints.REMAINDER;
    c.fill = GridBagConstraints.BOTH;
    gridbag.setConstraints(twoGraphPanel, c);
    add(twoGraphPanel);


    // Construct the three scrollbars    
    makeAllTheScrollbars( gridbag, c);
    // construct the three buttons    
    makeAllTheButtons( gridbag, c );

    //make the choice box to select material
    c.weightx=0.8;
    c.weighty=4.0;
    c.ipady=10;
    c.gridx=0;
    c.gridy=1;
    c.gridheight=1;
    c.gridwidth=1;
    c.anchor= GridBagConstraints.CENTER;
    c.fill = GridBagConstraints.NONE;
    material = new Choice();
    material.addItem("Spring");
    material.addItem("Rubber Band");
    gridbag.setConstraints(material,c);
    add(material);

    sleepTime = new Choice();
    sleepTime.addItem("Fast");
    sleepTime.addItem("Medium");
    sleepTime.addItem("Slow");
    c.weightx=0.1;
    c.gridx=1;
    gridbag.setConstraints(sleepTime,c);
    add(sleepTime);

} // end method init()


//
// Method action()
//  Interprets UI events.
//  Includes dis/enabling for which ones operate in Simulation and ICS modes.
//


public boolean action( Event evt, Object arg)
  {
    if ( this.simMode == false ) {
        if( evt.target == startBut ) {
            this.sigStart = true;
            this.sigPause = false; this.pauseBut.setLabel("Pause");
            this.sigStop  = false;
        }
     }//end if simMode == false

    else if( this.simMode == true ) {
        if( evt.target == stopBut ) {
            this.sigStart = false;
            this.sigPause = false; this.pauseBut.setLabel("Pause");
            this.sigStop  = true;             //to stop simulation
        }
    
        if( evt.target == pauseBut ) {
            this.sigStop  = false;
            if ( this.sigPause == false )
                pauseBut.setLabel("Unpause");
            else
                pauseBut.setLabel("Pause");
            this.sigPause = !this.sigPause;   // toggle switch      
        }
     } // end if simMode == true
return true; 
}

public boolean handleEvent( Event e )
{
    // In Initial Condition Set Mode, can only Start, Quit 
    // and change input scroll bars
    
    if ( this.simMode == false ) {

        if ( e.target instanceof Scrollbar ) {
            int v = ((Scrollbar)e.target).getValue();

            switch (((Scrollbar)e.target).getMaximum()){
              case maxmb:
   				double mtemp;
			    msb = (v *.01);
				mtemp =(Math.floor(v))/100;
				l2.setText("Mass of Bob : " + String.valueOf(mtemp) + "kg");
                break;
              case maxsc:
 				double ktemp;   
	            ksb = (v *.1);
				ktemp = (Math.floor(v))/10;
				l3.setText("Spring Constant : " + String.valueOf(ktemp) + "N/m");
                break;
	      case maxsl:
				double temp3;
				temp3 = Math.floor(v)/10;
				l4.setText("Damping Coefficient : " + String.valueOf(temp3));
				break;
            } 
      double scaleC = 12.0 / ( 0.3175 + msb * 9.8 / ksb ); 
      equil = ((msb * (9.8 * scaleC))/ksb); 
      animPanel.doequil(equil);
      return true;
       }  
    }
    
    //
    // In Simulation Mode, can only Stop and Pause the simulation.
    //
          
return super.handleEvent(e);         
} // end method actions()


//
// public method start()
//  Called after init(), which is called when containing page is entered.
//  Passes the applet into a Thread, TRunning, and starts the Thread.
//
public void start()
{
    if( TRunning==null ) {
        TRunning = new Thread(this);
        TRunning.start();
    }
} // end method start()
  
//
// public method stop()
//  When containing page is exited, stops the applet Thread.
//  But do the other (output) Threads stop?  Well, anyway, because each
//  of them runs only once (finite run() method), and since they're 
//  controlled from the applet's Thread, they should.
//
public void stop()
{
    if( TRunning != null ) {
        TRunning.stop();
        TRunning = null;
    }
} // end method stop()


public void run()
{   boolean do_me=true;
    while ( do_me ) {
        // ********* Initial Conditions Set Mode ********
        this.simMode = false;
        this.animPanel.setSimMode( false );
        this.animPanel.repaint();
        System.out.println("Applet now in ICS mode.");


        // During this phase, the user enters initial conditions.
        // This continues until the start button is pressed.
        
        while( !this.sigStart ) {
            try {
                Thread.sleep(100);
            } catch ( InterruptedException e ) {}
        }
       
        // **************** Simulation Mode *************** 
        // We now move into Simulation mode.
        // First, reset the sigStart back to false.
        // Then, set the various flags and modes to Simulation.
        // Then call simulate(), which will collect the initial conditions.
        // Once simulate exits(), continue loop until sigQuit.
        //
        
        sigStart = false;
        this.simMode = true;                 // move into Simulation mode.
        this.animPanel.setSimMode( true );   // tell animPanel to do so, too.
        System.out.println("Applet now in Simulation Mode.");
	
	    simulate();                          // run the simulation once.

        System.out.println("Applet Simulation complete.");
    }
    System.out.println("Applet quitting.");
  
} // end method run()
  
  
//
// protected method simulate()
//  The heart of the applet: ...
//
public void simulate()
{    
    int    idSolver       =   4;        // ID number for MathEngine
    double interval       =   30;    // accuracy granularity (sec)
    double g              =   9.8;      // acceleration of gravity (m/s^2)
    double springConstant =  50.0;      // Spring constant (...)
    double bobMass        =  25.0;      // Mass of pendulum bob (kg)
    double bobL           =   0.3175;      // Unstretched length
    double bobInitL       =   0.4;      // Pendulum initial R (m)
    double bobInitDL      =   0.0;      // Initial delta L
    double q              =   4;      // initial damping

    // Collect all the initial conditions:
    //  From this.animPanel
    //   From the sliders.
    
    // slider: spring constant
    // slider: mass
    // slider: spring length
    springConstant = (.1 * this.springConstantBar.getValue());
    bobMass        = (.01 * this.massOfBobBar.getValue());
    if (damping){
    q	   = (.1 * this.springLength.getValue());
    }
    else q=0;
  
    bobInitL     = animPanel.getInitStretch();    // animPanel

    // the scale constant is used to try to keep the equilibrium point in 
    // the middle of the animation window.  This will allow wider ranges
    // of the spring constant and mass.

    scaleConstant = 12.0 / ( bobL + bobMass * g / springConstant );
	
    // Pass all the initial conditions to the MathEngine.

    this.s1 = new MathEngine( this.buffer,    // DataBuffer
                              idSolver,       // ID number
                              numDatums,      // # of datums to create
                              interval,       // accuracy granularity
   						      scaleConstant *  g,     // acceleration of gravity
       			              scaleConstant * springConstant, // spring constant
          			          scaleConstant * bobMass,        // mass of pendulum bob
             			      scaleConstant * bobL,           // unstretched length
                              bobInitL,       // initial length
                		      scaleConstant * q,              // init damping
                              bobInitDL,     // initial delta-length
  							  this.scaleConstant); // scaled solution  
		
    // Call the MathEngine to fill the buffer -- do not run as a Thread!

    this.s1.run();

    // Have buffer print out its contents as a diagnostic.
     
    if( this.debugLev > 3 )   {
		System.out.println("\tMass: " + bobMass + "\n\tSpringConstant: " + springConstant
						+"\n\tDamping Coefficent: " + q + "\n\tScaleConstant: " + scaleConstant);
        this.buffer.diagnostics();
  		}
		
    // Run the output objects as mini-threads.
    //  Check for sigPause toggle.
    //  Check for sigStop.
     
    System.out.println("Output commencing."); 
    int timeUnit = -1;
    while( !(this.sigStop) && ( timeUnit < numDatums-1) ) {

        timeUnit++;

        if( this.sigPause ) {
            System.out.println("Output paused by sigPause.");
            while( this.sigPause ) {        // do nop.
                try{
                    Thread.sleep(15);             // the brief sleep() seems necessary.
                } catch ( InterruptedException e ) {}
            }
            System.out.println("Output unpaused.");
        }

        if( debugLev > 2 ) {
            System.out.println();
            System.out.println("   Emptying buffer timeUnit "+timeUnit);
        }

        animPanel.setIndex( timeUnit );   // each graph
		graph1Panel.setIndex( timeUnit ); //paint current 
        graph2Panel.setIndex( timeUnit ); // values on
  
	      // Slow down, let the elements render!
        try {
      Thread.sleep((int)(25*Math.pow(2,sleepTime.getSelectedIndex())));
        } catch ( InterruptedException e ) {}
     
    } // end while-loop 
     
    if ( sigStop )
        System.out.println("Output terminated by sigStop.");
    else
        System.out.println("Output terminated normally.");
       
    // VERY IMPORTANT -- remember to reset all flags when done with them!
    sigStop = false;
       
    // method now exits back to start's action in action().
       
} // end method simulate()

 
protected void makeAllTheButtons( GridBagLayout gridbag, GridBagConstraints c)
{ 
    c.gridx = 1;
    c.gridy = 2;

    c.weightx = 0.1;
    c.weighty = 1.0;
    c.anchor=GridBagConstraints.NORTH;
 
    startBut = new Button("Start");
    gridbag.setConstraints( startBut, c );
    startBut.setForeground( Color.black );
    startBut.setBackground( Color.green );
    add( startBut );
 
    c.gridy = 3;
 
    pauseBut = new Button("Pause");
    gridbag.setConstraints( pauseBut, c );
    pauseBut.setForeground( Color.black );
    pauseBut.setBackground( Color.yellow );
    add( pauseBut );
       
    c.gridy = 4;
 
    stopBut = new Button("Stop");
    gridbag.setConstraints( stopBut, c );
    stopBut.setForeground( Color.black );
    stopBut.setBackground( Color.red );
    add( stopBut );
  
} // end method makeAllTheButtons()


protected void makeAllTheScrollbars( GridBagLayout gridbag, 
                                     GridBagConstraints c)
{     
    c.gridheight = 1;
    c.gridwidth = 1;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weightx = 0.8;
    c.weighty = 0;
    c.gridx = 0;
    c.gridy = 2;
    this.springConstantBar=makeScrollPanel("Spring Constant", 
                                          gridbag, c, minsc, maxsc, cursc);

    c.gridx = 0;
    c.gridy = 3;
    this.massOfBobBar=makeScrollPanel("Mass of Bob", gridbag, c, minmb,
                                       maxmb, curmb);
if (damping){
    c.gridy = 4;    
    this.springLength=makeScrollPanel("Damping Coefficient", gridbag,
					c, minsl, maxsl, cursl);
     }

} // end method makeAllTheScrollbars()

//
// Method makeScrollPanel()
//  Automates the creation of scroll panels.
//  Such panels used here to set initial conditions.
//
protected Scrollbar makeScrollPanel(String name,
                                    GridBagLayout gridbag,
                                    GridBagConstraints c,
                                    int min,
                                    int max,
                                    int cur)
{
    Panel panel = new Panel();
    panel.setLayout(gridbag);
    GridBagConstraints constraints = new GridBagConstraints();
    Scrollbar scrollbar = new Scrollbar(Scrollbar.HORIZONTAL,cur,2,min,max);
          
    switch (max) {
      case maxmb:
        l2 = new Label(name + " : " + String.valueOf(cur * .01) + " kg");
        break;
      case maxsc:
        l3 = new Label(name + " : "  + String.valueOf(cur * .1) + " N/m");
        break;
      case maxsl:
     	l4 = new Label(name + " : "  + String.valueOf(cur/10) + ".0");
	   break; 
   }

    constraints.gridwidth = GridBagConstraints.REMAINDER;   
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.weightx = 1.0;
//    constraints.ipadx = 50;

    gridbag.setConstraints(scrollbar, constraints);
    panel.add(scrollbar);

    constraints.weightx = 1.0;
    constraints.gridwidth = GridBagConstraints.REMAINDER;
    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.CENTER;

    switch (max) {
      case maxmb:
        gridbag.setConstraints(l2, constraints); 
        panel.add(l2);
        break;
      case maxsc:
        gridbag.setConstraints(l3, constraints);
        panel.add(l3);
        break;
      case maxsl:
	gridbag.setConstraints(l4, constraints);
	panel.add(l4);
	break;
    }

    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.CENTER;
    gridbag.setConstraints(panel, c);
    add(panel);
      
    return scrollbar;

} // end method makeScrollPanel()

public double getScaleConstant() {
    return scaleConstant;
}
public double getvrmldis(int i){
 
return (int)(5*this.s1.vrmldis[i]);
}

public double getvrmlvel(int i){
 
return (int)(this.s1.vrmlvel[i]);
}

public void setvalues(double mass_in, double k_in)
{	
	massOfBobBar.setValue((int)mass_in);
	l2.setText("Mass of Bob : " + String.valueOf(mass_in/100) + "kg");
	springConstantBar.setValue((int)k_in);   
	l3.setText("Spring Constant : " + String.valueOf(k_in/10) + "N/m");
//   l4.setText("Damping Coefficient : " + String.valueOf(initd_in/10));
//	springLength.setValue((int)initd_in);
}	        


} // end class SpringMass
