// (c) MIT 2003.  All rights reserved.

// $Id: WMSimulatorRegistryDDE.java,v 1.25 2004/02/25 17:30:22 cadlerun Exp $
// WMSimulatorRegistryDDE.java --
// Author: Christine Alvarado <calvarad@ai.mit.edu> 
// Maintainer: Christine Alvarado <calvarad@ai.mit.edu> 
// Created: <Thu Mar 18 12:10:44 1999> 
// Time-stamp: <2003-05-07 20:46:29 moltmans>
// Keywords: 
package rec.simulate;

import java.util.*;
import java.io.*;
import rec.util.*;
import rec.sys.*;
import rec.geo.*;
import rec.core.widgets.*;
import java.lang.*;
import com.neva.*;
import rec.simulate.widgets.wm.*;
import rec.simulate.widgets.*;
import rec.util.ClassFinder;
import java.net.URL;

// based on SimulateRegistry.  Has all the funcitonality of the Simulate Reg
// but it can also print out to a WM file.
public class WMSimulatorRegistryDDE extends WMSimulatorRegistryBase
{
  
  public WMSimulatorRegistryDDE( )
  {
    super( );
    // set up the DDE connections 
    try {
      m_output = new Jddeml();
      // this just checks if dde is working.  there may not be a WM
      // started this is repeated (intentionally) in the load method.
      m_output.ddeConnect( "WM", "WMBasic" );
    }
    catch( UnsatisfiedLinkError e ) {
      System.err.println("Unable to load Coroutine/Jddeml");
      System.err.println("Unable to send messages to Working Model");
      m_output = null;
    }
    
  }

  // load all the header, then all the simulatable objects then the footer
  // into the WM file.
  public void load( Surface s )
  {
    if ( m_output == null ) {
      return;
    }
    
    m_WindowTitle = s.getSystemProperty( "WM-window-title",
					 m_WindowTitle );

    m_WMBasicDir = ClassFinder.getResourcePath("wmbasic");

    //    m_WMBasicDir = s.getSystemProperty( "WM-basic-dir", m_WMBasicDir );
    m_WMExe = s.getSystemProperty( "WM-exe", m_WMExe );

    m_WMAnimationStep = s.getSystemProperty( "WM-animation-step", null );

    // connect to working model
    try {    
      if ( !m_output.ddeConnect( "WM", "WMBasic" ) ) {
	Assert.note( "Unable to establish connection with WM" );
	
	try {
	  Runtime.getRuntime().exec( "\"" + m_WMExe + "\"" );
	  boolean is_connected = false;
	  
	  // connect to working model
	  for ( int i=0; i < 5 && !is_connected; i++ ) {
	    
	    if ( m_output.ddeConnect( "WM", "WMBasic" ) ) {
	      is_connected = true;
	    }
	    else {
	      Thread.sleep( 1000 );
	    }
	  }
	  
	  if ( ! is_connected  ) {
	    System.err.println( "Unable to establish connection with WM" );
	    return;
	  }
	}
	catch ( Exception e ) {
	  System.err.println("Exception connecting to DDE: "+e );
	  return;
	}
      }
    }
    catch (Exception e) {
      System.err.println("Exception connecting to DDE: "+e);
    }

    
    Console.writeln( "loading..." );
    

    // starts the transaction of script passing
    m_output.ddeExecute( "$wmstart$", m_timeout );

    // if the debug flag is set, set up the file.
    if ( m_debug ) {
      try {
	m_fileout = new PrintWriter( new FileWriter( "WMDebugInput.wbs" ), true );
      }
      catch ( IOException e ) {
	System.out.println( e );
      }
    }

        
    // print the header stuff to the file
    printHeader();

    send( "WinRestore \""+ m_WindowTitle + "\"" );

    // activate the working model application
    if (m_output.ddeExecute( "WinActivate \"" + m_WindowTitle +"\"",
			     m_timeout))
    {
      Console.writeln( "Acitivated WM window" );
    }
   

    // step through the widgets on the surface.  For each visible widget 
    // which is on the surface and also in this registry, print it to 
    // the file.
    // for now the base simulatable class is WSimBody.  This only
    // handles polygons.  Obviously i will need to abstract one more time
    // in the future.
    Iterator it = s.getSurfacePoolIterator();
    String object_message;
    // load the bodies first
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();
      // check to see if we have this one
      if ( w instanceof WSimBody ) {
	object_message = ((WSimBody)w).load( this );
	send( object_message );
      }
    }

    Iterator it2 = s.getSurfacePoolIterator();
    while( it2.hasNext() ) {
      Widget w = (Widget)it2.next();
      if ( w instanceof WSimConstraint ) {
	object_message = ((WSimConstraint)w).load( this );
	send( object_message );
      }      
    }

    // run the simulation for a few timesteps
    send( "doc.run" );
    
    // print the footer stuff to the file
    printFooter();
    

    // finish the transaction of script passing
    m_output.ddeExecute( "$wmend$", m_timeout );
    m_output.ddeDisconnect();

    if( m_fileout != null ) {
      m_fileout.close();
    }
  }

  //////////////////////////////////////////////////////////////////////

  // there are some variable name issues to consider here, like the name 
  // of the doccument, and the names of any objects created.  They should
  // all be controlled by this function, and passed in to the loading
  // function.  Also, when we start loading libraries to handle more 
  // complex shapes (that is, WMBasic libraries which already exist), into
  // the file that we are creating we will need to somehow keep track of 
  // them to be used at the beginning of printHeader()
  private void loadTypes()
  {
    // point type
    send( "Type point" );
    send( "X as double" );
    send( "Y as double" );
    send( "end Type" );
  }

  private void loadVariables()
  {
    send( "Dim center as point" );
    send( "Dim p() as point" );
    send( "Dim t1 as point" );
    send( "Dim t2 as point" );
  }
  
  private void loadLibs()
  {
    send( "WM.LoadWMBLibrary \"" + m_WMBasicDir + "\\draw_bodies.wbs\"" );
    send( "WM.LoadWMBLibrary \"" + m_WMBasicDir + "\\draw_constraints.wbs\"" );
  }
  
  private void unloadLibs()
  {
    send( "WM.UnloadWMBLibrary \"" + m_WMBasicDir + "\\draw_bodies.wbs\"" );
    send( "WM.UnloadWMBLibrary \"" + m_WMBasicDir + "\\draw_constraints.wbs\"" );
  }

  private void loadDeclare()
  {
    send( "Declare function Draw_Rect(h, w, c as point)" );
    send( "Declare function Draw_Poly(n, p() as point, c as point)" );
    send( "Declare function Draw_Spring(l as point, r as point)" );
    send( "Declare function Draw_Pulley(n, a() as point)" );
    send( "Declare function Draw_Damper(l as point, r as point)" );
    send( "Declare function Draw_SpringDamper(l as point, r as point)");
    send( "Declare function Draw_Circle(r, c as point)" );
    send( "Declare function Draw_Force(c as point, fx, fy)" );
    send( "Declare function Draw_Rod(l as point, r as point)");

  }

  private void printHeader()
  {

    loadTypes();
    loadDeclare();

    Console.writeln( "writing to file" );
    send( "Sub Main()" );
    
    loadLibs();

    send("dim doc as WMDocument");
    send("set doc = WM.ActiveDocument");

    // reset the document 
    send("doc.Reset");

    // clear everything
    send("doc.SelectAll");
    send("doc.Delete");

    if ( m_WMAnimationStep != null ) {
      send("doc.AutoAnimationStep = False");
      send("doc.AnimationStep = " + m_WMAnimationStep );
    }
    else {
      send("doc.AutoAnimationStep = True");
    }
    

    // declare variables
    loadVariables();

    // set gravity appropriately 
    if ( !m_gravity ) {
      send("doc.gravity = \"none\"");
    } 
    else {
      send("doc.gravity = \"linear\"");
    }
  }

  private void printFooter()
  {
    send("doc.SelectAll False");
    unloadLibs();
    send("end Sub");

  }


  ///////////////////////////////////////////////////////////////////////////
  //
  // if m_debug is set, sends the string to working model and to an output
  // file.  otherwise, it just sends the output to working model
  private boolean send( String command ) 
  {
    if( m_output == null ) {
      return false;
    }
    
    if ( m_debug && m_fileout != null ) {
      m_fileout.println( command );
    }
    return ( m_output.ddeExecute( command, m_timeout ) );
  }

  public void shutdown() 
  {
  }
  

  ///////////////////////////////////////////////////////////////////////////
  private Jddeml m_output;
  private final int m_timeout = 500; 
  private final boolean m_debug = true;
  private PrintWriter m_fileout;

  // if any of these don't work set the value in an ini or mod file
  // with:  SystemProperty "WM-window-title" "What My Window is Called"
  // DO NOT JUST CHANGE THEM HERE
  private String m_WMAnimationStep = null;
  private String m_WindowTitle = "Working Model 2D";
  private String m_WMBasicDir = "z:\\hal\\source\\rec\\wmbasic";
  private String m_WMExe = "C:\\Program Files\\Working Model\\PROGRAM\\WM.exe";
}


