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

// -*-Mode: JDE-*- 
// $Id: Lisp.java,v 1.12 2003/03/06 00:40:49 moltmans Exp $
// Lisp.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Fri Jan 26 12:36:40 2001> 
// Time-stamp: <2001-04-24 19:04:58 moltmans> 
package rec.annotate.lisp;

import com.franz.jlinker.*;
import java.io.IOException;
import java.net.URL;
import rec.util.DebugWindow;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JOptionPane;
import java.util.Collection;
import java.io.File;
import rec.util.ClassFinder;

/**
 * Lisp.java
 *
 *
 * Created: Fri Jan 26 12:36:44 2001
 *
 * @author <a href="mailto:moltmans@ai.mit.edu">Michael Oltmans</a>
 * @version
 */

public class Lisp {
  // measured in milliseconds
  private static LinkedList RESULT_LIST;

  private int JLINK_POLL_INTERVAL = 20;
  private int JLINK_POLL_COUNT = 300;

  private String m_magicFile = "lisp-to-java-magic-file.trp";
  private String m_lispExe;
  private String LOAD_FILE = "load-lisp.lsp";


  protected File m_lispRoot;

  private Process m_lispProcess;
  
  public Lisp(String lispExe) 
  {
    if (lispExe != null) {
      m_lispExe = lispExe;
    }
    
    URL url = ClassFinder.getResource(this, LOAD_FILE);
    if( url == null ) {
      url = ClassFinder.getResource("lib/lisp/" + LOAD_FILE);
    }
    
    m_lispRoot = new File(url.getFile()).getAbsoluteFile().getParentFile();
    //    m_lispRoot = file.substring(1, file.lastIndexOf("/") + 1);
    m_magicFile = makeFile(m_magicFile);
  }
  
  public Lisp () {
    this(null);
  }
  
  /**
   * Start a lisp process.
   *
   * @param command to run on startup
   * @return <code>true</code> if lisp is started without error.
   */
  boolean startLisp(String command, String file) {
    String[] args = {m_lispExe,
		     "+cx", // hidden console
		     "-qq", // do not load startup files
		     "-e", command, "-L", file,
		     "-e", "(start-jlinker)"
                    };

    try {
      m_lispProcess = Runtime.getRuntime().exec(args, null, m_lispRoot);
    }
    catch (IOException e) {
      e.printStackTrace();
      return false;
    }
    
    return true;
  }

  public File getLispRoot() 
  {
    return m_lispRoot;
  }

  public boolean connectToLisp() {
    JavaLinkDist.connect(m_magicFile, "localhost", 0, 10, 2);
    if (JavaLinkDist.query(true)) {
      DebugWindow.addLine("Lisp", "Lisp already connected");
      return true;
    }

    boolean result = startLisp("(defvar *jlinker-magic-file* \"" +
			       m_magicFile + "\")",
			       makeFile(LOAD_FILE));
    if (result) {
      DebugWindow.addLine("Lisp", "Lisp process started");
    }
    else {
      DebugWindow.addLine("Lisp", "Trouble starting Lisp process");
    }

    JavaLinkDist.connect(m_magicFile, "localhost", 0,
			 JLINK_POLL_INTERVAL, JLINK_POLL_COUNT);
    if (JavaLinkDist.query(true)) {
      DebugWindow.addLine("Lisp", "JLink established to lisp");
      return true;
    }
    else {
      DebugWindow.addLine("Lisp", "Trouble starting JLinker");
      return false;
    }
  }

  public void shutdown() 
  {
    if (JavaLinkDist.query()) {
      JavaLinkDist.disconnect();
    }

    // we started it so we should kill it...
    if (m_lispProcess != null) {
      m_lispProcess.destroy();
    }
    
  }
  

  public String readEvalPrint(String form)
  {
    LispObj obj = runInLisp("read-eval-to-string", form);
    return obj.toString();
  }

  public LispObj readEval(String form)
  {
    DebugWindow.addLine("Lisp-Eval", form);
    LispObj obj = runInLisp("read-eval", form);
    return obj;
  }
  
  public LispObj runInLisp(String command) 
  {
    TranStruct[] result = JavaLinkDist.invokeInLisp(2, command);
    return processResult(result, JavaLinkDist.newDistOb(command), null);
  }
  
  public LispObj runInLisp(String command, String arg) {
    TranStruct[] args = new TranStruct[1];
    args[0] = JavaLinkDist.newDistOb(arg);
    TranStruct cmd = JavaLinkDist.newDistOb(command);
    return runInLisp(cmd, args);
  }

  public LispObj runInLisp(String command, String arg1, String arg2) {
    TranStruct[] args = new TranStruct[2];
    args[0] = JavaLinkDist.newDistOb(arg1);
    args[1] = JavaLinkDist.newDistOb(arg2);
    TranStruct cmd = JavaLinkDist.newDistOb(command);
    return runInLisp(cmd, args);
  }

  public LispObj runInLisp(String command,
			   String arg1, String arg2, String arg3)
  {
    TranStruct[] args = new TranStruct[3];
    args[0] = JavaLinkDist.newDistOb(arg1);
    args[1] = JavaLinkDist.newDistOb(arg2);
    args[2] = JavaLinkDist.newDistOb(arg3);
    TranStruct cmd = JavaLinkDist.newDistOb(command);
    return runInLisp(cmd, args);
  }

  public LispObj runInLisp(String command, TranStruct[] args) 
  {
    TranStruct cmd = JavaLinkDist.newDistOb(command);
    return runInLisp(cmd, args);
  }
  
  public LispObj runInLisp(TranStruct cmd, TranStruct[] args) {
    TranStruct[] result = JavaLinkDist.invokeInLisp(2, cmd, args);
    return processResult(result, cmd, args);
  }

  private LispObj processResult(TranStruct[] result,
				TranStruct command,
				TranStruct[] args) 
  {
    if (isLispError(result)) {
      DebugWindow.addLine("Lisp",
			  "Error evaling: " + LispObj.toString(result[0]));
      String print = "  (" + LispObj.toString(command);

      if ( args != null ) {
	for (int i=0; i<args.length; i++) {
	  print += " " + LispObj.toString(args[i]);
	} // end of for ()
      } // end of if ()
      DebugWindow.addLine("Lisp", print + ")");
      return null;
    }
    return LispObj.makeResult(result);
  }

  public void setq(String var, TranStruct value) 
  {
    runInLisp("java-setq", new TranStruct[] {JavaLinkDist.newDistOb(var),
					     value});
  }

  /**
   * Build a file name that is friendly to lisp.  It prepends the lisp
   * root directory and converts nasty backslashes to forward slashes.
   **/
  public String makeFile(String filename)
  {
    File f = new File(m_lispRoot, filename);
    return f.getPath().replace('\\', '/');
  }
  

  public boolean loadFile(String file)
  {
    return readEval("(load \"" + makeFile(file) + "\")") != null;
  }


  public static String list(Collection list)
  {
    return list(list, false);
  }
  
  public static String list(Collection list, boolean isQuoted)
  {
    String[] array = new String[list.size()];
    return list((String[])list.toArray(array), isQuoted);
  }
  
  public static String list(String[] items)
  {
    return list(items, false);
  }
  
  public static String list(String[] items, boolean isQuoted) 
  {
    StringBuffer result = new StringBuffer(isQuoted ? "'(" : "(");
    for (int i=0; i<items.length; i++) {
      result.append(items[i]).append(" ");
    }
    return result.append(")").toString();
  }

  public static String quotes(String string)
  {
    return "\"" + string + "\"";
  }
    
  static boolean isLispError(TranStruct[] result) {
    return (result.length == 1 && JavaLinkDist.errorP(result[0]));
  }

  public static LinkedList getResultList() 
  {
    return RESULT_LIST;
  }

  public static void setResultList(LinkedList list) 
  {
    RESULT_LIST = list;
  }
  
  public static void main(String[] args){
    Lisp l = new Lisp();
    l.connectToLisp();

    boolean done = false;
    while (!done) {
      String inputValue = JOptionPane.showInputDialog("Enter a value to eval");
      if (inputValue == null ||
	  inputValue.toLowerCase().equals("q") ||
	  inputValue.toLowerCase().equals("quit") ||
	  inputValue.toLowerCase().equals("exit"))
      {
	done = true;
      }
      else {
	String result = l.readEvalPrint(inputValue);
	DebugWindow.addLine("Result", "eval: " + inputValue + ":\n");
	DebugWindow.addLine("Result", result);
      }
    }
    
    try {
      l.shutdown();
    }
    catch (Exception e){
    }
    
    System.exit(0);
  }

}// Lisp



