public class ABCL {
   private static Interpreter interpreter;
   private static boolean invertCase = false;
   private static Function makeWebServiceArgs;
   private static int lispRelease = 0;
   private static boolean once = true;
   public static void init() {  // only called once
      interpreter = Interpreter.createInstance();
      invertCase();
      load("com/xxx/lisp/clos-utils");
      load("com/xxx/lisp/package-lru");
      load("com/xxx/lisp/utils");
      load("com/xxx/lisp/mappings");
      makeWebServiceArgs = findLispFunction("UTILS", "make-web-service-args"); // this line is repeated in multiple places
   }
   private static void invertCase() {
      interpreter.eval("(setf (readtable-case *readtable*) :invert)");  // make lisp case sensitive
      invertCase = true;
   }
   public static String fixCase(String symbol) {
      if (invertCase) {
         int ucl = 0, lcl = 0;
         char[] vec = symbol.toCharArray();
         for (int i=0 ; i < vec.length && (ucl == 0  ||  lcl == 0) ; i++)
            if (Character.isUpperCase(vec[i]))
               ucl++;
            else if (Character.isLowerCase(vec[i]))
               lcl++;
         if (ucl != 0  &&  lcl != 0  ||  ucl == 0  &&  lcl == 0)
            return symbol;
         else
            if (ucl != 0)
               return symbol.toLowerCase();
            else
               return symbol.toUpperCase();
      } else
         return symbol.toUpperCase();
   }
   public static void reset() {
//    if (interpreter == null)
//       return;
//    try {
//       interpreter.eval("(delete-package \"ARAHANT-UTILS\")");
//    } catch (Throwable t) {
//    }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"MAPPINGS\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"UTILS\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"PACKAGE-LRU\")");
      } catch (Throwable t) {
      }
      if (interpreter == null)
         return;
      try {
         interpreter.eval("(delete-package \"CLOS-UTILS\")");
      } catch (Throwable t) {
      }
      load("com/xxx/lisp/clos-utils");
      load("com/xxx/lisp/package-lru");
      load("com/xxx/lisp/utils");
      load("com/xxx/lisp/mappings");
      makeWebServiceArgs = findLispFunction("UTILS", "make-web-service-args"); // this line is repeated in multiple places
   }
   public static LispObject load(String fileName) {
      return eval("(load \"" + FileSystemUtils.getSourcePath() + fileName + "\")");
   }
   public static LispObject compileFile(String fileName) {
      return eval("(compile-file \"" + FileSystemUtils.getSourcePath() + fileName + "\")");
   }
   public static void loadPackage(String lispPackage, String fileName) throws Exception {
      try {
         eval("(package-lru:load-package \"" + lispPackage + "\" \"" + FileSystemUtils.getSourcePath() + fileName + "\")");
      } catch (Throwable t) {
         // Convert Throwable to Exception
         throw new Exception("Error loading lisp file " + fileName, t);
      }
   }
   public static void packageDone(String lispPackage) {
      if (FileSystemUtils.isUnderIDE())
         eval("(package-lru:package-done-unload \"" + lispPackage + "\")");
      else
         eval("(package-lru:package-done \"" + lispPackage + "\")");
   }
    public static LispObject eval(String str) {
        return interpreter.eval(str);
    }
    public static Function findLispFunction(String packageName, String funName) {
        if (packageName == null  ||  packageName.isEmpty())
            packageName = "CL-USER";
//        else
//            packageName = fixCase(packageName);
        org.armedbear.lisp.Package lispPackage = Packages.findPackage(packageName);
      if (lispPackage == null)
         throw new RuntimeException("Package " + packageName + " not found");
        Symbol symbol = lispPackage.findAccessibleSymbol(fixCase(funName));
      if (symbol == null)
         throw new RuntimeException("Symbol " + packageName + ":" + fixCase(funName) + " not found");
        Function fun = (Function) symbol.getSymbolFunction();
        return fun;
    }
    public static LispObject executeLispFunction(Function fun, Object ... args) {
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }
    public static LispObject executeLisp(String packageName, String funName, Object ... args) {
        Function fun = findLispFunction(packageName, funName);
      if (fun == null)
         return null;
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }
    public static LispObject executeLispArray(String packageName, String funName, Object [] args) {
        Function fun = findLispFunction(packageName, funName);
      if (fun == null)
         return null;
        LispObject [] jargs;
        jargs = new LispObject[args.length];
        for (int i=0 ; i < args.length ; i++)
            jargs[i] = JavaObject.getInstance(args[i], true);
        return fun.execute(jargs);
    }
   public static Function getMakeWebServiceArgs() {
      return makeWebServiceArgs;
   }
@SuppressWarnings("unchecked")
   public static Object LispObjectToJavaObject(LispObject obj) {
      if (obj.atom())
         if (obj.characterp())
            return obj.princToString().charAt(0);
         else if (obj.stringp())
            return obj.princToString();
         else if (obj.integerp())
            return obj.intValue();
         else if (obj.realp())
            return obj.doubleValue();
         else if (obj.listp())
            return null;
         else if (obj.constantp())
            return true;
         else
            return obj.princToString();
      else if (obj.listp()) {
         LinkedList ll = new LinkedList();
         while (!obj.endp()) {
            ll.addLast(LispObjectToJavaObject(obj.car()));
            obj = obj.cdr();
         }
         return ll;
      } else if (obj.vectorp()) {
         int len = obj.length();
         Object [] vec = new Object[len];
         for (int i=0 ; i < len ; i++)
            vec[i] = LispObjectToJavaObject(obj.AREF(i));
         return vec;
      } else
         return null;
   }
   public static LispObject JavaObjectToLispObject(Object jobj) {
      if (jobj instanceof Boolean)
         return ((Boolean)jobj) ? Lisp.T : Lisp.NIL;
      else if (jobj instanceof Character)
         return LispCharacter.getInstance((Character)jobj);
      else if (jobj instanceof Short)
         return LispInteger.getInstance((Short)jobj);
      else if (jobj instanceof Integer)
         return LispInteger.getInstance((Integer)jobj);
      else if (jobj instanceof Long)
         return LispInteger.getInstance((Long)jobj);
      else if (jobj instanceof Float)
         return SingleFloat.getInstance((Float)jobj);
      else if (jobj instanceof Double)
         return DoubleFloat.getInstance((Double)jobj);
      else if (jobj instanceof String)
         return new SimpleString((String)jobj);
      else if (jobj instanceof StringBuilder)
         return new SimpleString((StringBuilder)jobj);
      else if (jobj instanceof LinkedList) {
         LispObject lobj = Lisp.NIL;
         ListIterator it = ((LinkedList) jobj).listIterator();
         while (it.hasNext())
            lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
         return lobj;
      } else if (jobj instanceof Set) {
         LispObject lobj = Lisp.NIL;
         Iterator it = ((Set) jobj).iterator();
         while (it.hasNext())
            lobj = new Cons(JavaObjectToLispObject(it.next()), lobj);
         return lobj;
      } else if (jobj instanceof Array) {
         Array a = (Array) jobj;
         int len = Array.getLength(a);
         SimpleVector vec = new SimpleVector(len);
         for (int i=0 ; i < len ; i++)
            vec.setSlotValue(i, JavaObjectToLispObject(Array.get(a, i)));
         return null;
      }
      return null;
   }
   public static void printStackTrace(Throwable e) {
      try {
         Function fun = findLispFunction("UTILS", "print-stack-trace");
         if (fun != null) {
            LispObject stackTrace = LispThread.currentThread().backtrace(0);
            if (stackTrace != null && stackTrace != Lisp.NIL) {
               System.err.println("Lisp execution error");
               fun.execute(stackTrace);
            }
         }
      } catch (Throwable t) {
      }
      e.printStackTrace();
   }
   public static int getLispRelease() {
      return lispRelease;
   }
   public static void setLispRelease(int lispRelease) {
      ABCL.lispRelease = lispRelease;
   }
}