// ParameterController.m
// By Charles G. Fleming, Halchin and Fleming.
// You may freely copy, distribute and reuse this code. 
// Halchin and Fleming, and the author disclaim any warranty of any kind, 
// expressed or implied, as to its fitness for any particular use.

#import "ParameterController.h"
#import <stdlib.h>
#import <strings.h>
#import "MathTool.h"
#import "MathGraph.h"
#import <appkit/TextField.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <appkit/Font.h>
#import "DomainAndRange.h"
#import <appkit/Button.h>
#import <appkit/Window.h>
#import <appkit/Button.h>
#import <appkit/defaults.h>     // for NXArgv

#import <sys/param.h>           // for MAXPATHLEN

#import <libc.h>                     // for chdir, getwd

@implementation ParameterController

// Start Mathematica and load the package "Laplace".  Modify the scroll view 
// that will display the nongraphical results.  Bring the windows on 
// screen in the order desired.
- appDidInit:sender
{ 
    char *commandString;
    char packagePath[MAXPATHLEN], fullPath[MAXPATHLEN], *suffix;  

    // This will set up and start Mathematica.
    [mathTool initMath:self];

    // Clear the variable t in Mathematica.  Send 1 to be evaluated so that Mathematica doesn't
    // say that NULL was returned.    
    [mathTool executeCommand:"Clear[t];  1" resultType:INPUT];
      
   // Set up the path to the Mathematica package Laplace.m.   
   strcpy(packagePath, NXArgv[0]);
   if (suffix = rindex(packagePath, '/'))
      *suffix = '\0';
   if(packagePath)                            // appPath is the pathname of a directory.  chdir causes the 
      chdir(packagePath);			// directory to become the current working directory. 
      
   getwd(packagePath);                   // This copies the absolute pathname of the current working 
    						// directory into appPath.
   sprintf(fullPath, "%s/Laplace.m",packagePath);
         
    // Construct the string that will be used to load the Laplace Transform 
    // package.    
    commandString = malloc(MAXPATHLEN);
    sprintf(commandString, "<<%s", fullPath);
   
    // Send the command to the MathTool. Get the result from the MathTool,
    // (it will be null) but do not display it.
    [mathTool executeCommand:commandString resultType:NONE];
    free(commandString); 
    
    // Set up the scroll view so that it can handle Mathematica's output.
    [[resultScrollView docView] setFont:[Font newFont:"Courier" size:12]]; 
    
    // Widen the margins in the scroll view to make Mathematica's output
    // about 1/8 inch from the margins.  
    [[resultScrollView docView] setMarginLeft:5 right:5 top:5 bottom:5];  

    // Bring the windows on screen.
    [[mathGraph window] orderFront:sender];
    [[resultScrollView window] orderFront:sender]; 
    [self makeKeyAndOrderFront:sender];
    
    // This flag is used to detect whether or not the result view and graph
    // view may need to be cleared.
    notCleared = NO; 
    return self;
}

// Solve the differential equation using a the function "Laplace". 
- compute:sender
{
    char *commandString, *resultString;
    float mass, friction, springConstant, forcingFrequency;
    float  forcingMagnitude, initialPosition, initialVelocity;

    // Get the current values from all of the fields.
    mass = [massField floatValue];
    friction = [frictionField floatValue];
    springConstant = [springConstantField floatValue];
    forcingFrequency = [forcingFrequencyField floatValue];
    forcingMagnitude = [forcingMagnitudeField floatValue];
    initialPosition = [initialPositionField floatValue];
    initialVelocity = [initialVelocityField floatValue];
    
    // Construct the string that will be used to execute the "Laplace"
    // command to solve the differential equation.
    commandString = malloc(500);
    sprintf(commandString, "Laplace[%f,%f,%f,%f,%f,%f,%f]", mass, friction,
            springConstant, forcingMagnitude, forcingFrequency,
	    initialPosition, initialVelocity);
    
    // Send the command to the MathTool and get the result in a form
    // suitable for displaying.
    [mathTool executeCommand:commandString resultType:OUTPUT];
    resultString = [mathTool outputTextString];
    
    // Display the result in the scroll view.
    [[resultScrollView window] orderFront:self];
    [[resultScrollView docView] setText:resultString];

    // Also get the result from Mathematica in a form that can be used
    // for generating graphics.
    [mathTool executeCommand:commandString resultType:INPUT];
    solution = [mathTool inputTextString]; 
    
    // Enable the graphics buttons so that the solution can be plotted.
    [plotButton setEnabled:YES]; 
    [graphSettingsButton setEnabled:YES];

    // This is used to detect whether or not the result view and graph
    // view may need to be cleared.
    notCleared = YES; 
           	            	
    free(commandString);       
    return self;
}

// Display the solution obtained using "Laplace".
- plotSolution:sender
{
   char *commandString, *graphicsString;
   double domainLowerBound, domainUpperBound, rangeLowerBound;
   double rangeUpperBound, aspectRatio;

   // Build the command to generate the graphics and get the graphics
   // string from Mathematica. 
   // First get the domain and range from the domainAndRange window.
   if([[domainAndRange defaultButton:self] state])
   {
      domainLowerBound = [domainAndRange domainLower];
      domainUpperBound = [domainAndRange domainUpper]; 
      commandString = malloc(50 + strlen(solution));
      sprintf(commandString, "Plot[%s, {t, %f, %f}]", solution, 
              domainLowerBound, domainUpperBound);
   }
   else 
   {
      domainLowerBound = [domainAndRange domainLower];
      domainUpperBound = [domainAndRange domainUpper]; 
      rangeLowerBound = [domainAndRange rangeLower];
      rangeUpperBound = [domainAndRange rangeUpper]; 
      aspectRatio = (rangeUpperBound - rangeLowerBound)/ (domainUpperBound - 
                     domainLowerBound);
      commandString = malloc(160 + strlen(solution));
      sprintf(commandString, "Plot[%s, {t, %f, %f}, PlotRange->{%f, %f},
               AspectRatio->%f]", solution, 
              domainLowerBound, domainUpperBound, rangeLowerBound, 
	      rangeUpperBound, aspectRatio);
   }	      

   // Send the Plot command to Mathematica.       
   [mathTool executeCommand:commandString resultType:GRAPHICS];   
   graphicsString = [mathTool graphicsString];
   
   // Graph the result.
   [[mathGraph window] orderFront:self];
   [mathGraph display:graphicsString];

   free(commandString);
   return self;
}

// Whenever a slider moves, place its value into the corresponding field.
- setValues:sender
{
   int tag;
   
   // Find which slider moved.   
   tag = [sender tag];

   // Put the slider's value in the field.   
   switch (tag) 
   {
      case 0:
         [massField setFloatValue:[sender floatValue]];
	 break;
      case 1:
         [frictionField setFloatValue:[sender floatValue]];
	 break;
      case 2:
         [springConstantField setFloatValue:[sender floatValue]];
	 break;
      case 3:
         [forcingMagnitudeField setFloatValue:[sender floatValue]];
	 break;
      case 4:
         [forcingFrequencyField setFloatValue:[sender floatValue]];
	 break;
      case 5:
         [initialPositionField setFloatValue:[sender floatValue]];
	 break;
      case 6:
         [initialVelocityField setFloatValue:[sender floatValue]];
	 break; 
   }

   // Clear only when necessary.
   if (notCleared)
   {   	    
      // Disable the graphics buttons until the solve button has 
      // been pushed again. Clear the displays.
      [plotButton setEnabled:NO];
      [graphSettingsButton setEnabled:NO];
      [mathGraph clearMathGraph];
      [[resultScrollView docView] setText:""];
      notCleared = NO;
   }
        
   return self;
}         
      
@end
