package codegen;

import absyn.Exp;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.PointerPointer;

import static org.bytedeco.javacpp.LLVM.*;
import static error.ErrorManager.em;

// http://llvm.org/releases/3.8.0/docs/index.html
// http://llvm.org/releases/3.8.0/docs/tutorial/index.html
// http://llvm.org/releases/3.8.0/docs/ProgrammersManual.html
// http://llvm.org/docs/doxygen/html/group__LLVMC.html
// https://github.com/paulsmith/getting-started-llvm-c-api
// http://bytedeco.org/javacpp-presets/llvm/apidocs/org/bytedeco/javacpp/LLVM.html
// https://github.com/wickedchicken/llvm-c-example

public class Generator {

   public static void codegen(String name, Exp exp) {
      BytePointer error = new BytePointer((Pointer) null); // Used to retrieve messages from functions
      LLVMLinkInMCJIT();
      LLVMInitializeNativeAsmPrinter();
      LLVMInitializeNativeAsmParser();
      LLVMInitializeNativeDisassembler();
      LLVMInitializeNativeTarget();
      LLVMModuleRef mod = LLVMModuleCreateWithName("__eplan_module");
      LLVMBuilderRef builder = LLVMCreateBuilder();

      // print_double prototype
      LLVMTypeRef[] params = {LLVMDoubleType()};
      LLVMTypeRef funcType = LLVMFunctionType(LLVMVoidType(), new PointerPointer(params), 1, 0);
      LLVMValueRef func = LLVMAddFunction(mod, "__eplan_print_double", funcType);
      LLVMSetLinkage(func, LLVMExternalLinkage);

      // main function generation
      LLVMTypeRef main_type = LLVMFunctionType(LLVMInt32Type(), new PointerPointer((Pointer) null), 0, 0);
      LLVMValueRef main = LLVMAddFunction(mod, "main", main_type);
      LLVMBasicBlockRef entry = LLVMAppendBasicBlock(main, "entry");
      LLVMPositionBuilderAtEnd(builder, entry);
      LLVMValueRef exp_value = exp.codegen(mod, builder);
      LLVMValueRef[] print_double_args = {exp_value};
      LLVMValueRef print_double = LLVMGetNamedFunction(mod, "__eplan_print_double");
      LLVMValueRef print_double_result = LLVMBuildCall(builder, print_double, new PointerPointer(print_double_args), 1, "");
      LLVMValueRef result = LLVMConstInt(LLVMInt32Type(), 0, 0);
      LLVMBuildRet(builder, result);

      // analysis and execution
      LLVMVerifyModule(mod, LLVMAbortProcessAction, error);
      LLVMDisposeMessage(error); // Handler == LLVMAbortProcessAction -> No need to check errors

      LLVMExecutionEngineRef engine = new LLVMExecutionEngineRef();
      if (LLVMCreateJITCompilerForModule(engine, mod, 2, error) != 0) {
         em.fatal(error.getString());
         LLVMDisposeMessage(error);
      }

      // LLVMDumpModule(mod);

      if (LLVMPrintModuleToFile(mod, name + ".ll", error) != 0) {
         em.fatal(error.getString());
         LLVMDisposeMessage(error);
      }
      else
         System.out.printf("target code written to %s.ll%n", name);

      LLVMDisposeBuilder(builder);
      LLVMDisposeExecutionEngine(engine);
   }

}

@@ -1154,305 +1154,53 @@
  // 
-print_double prototype%0A      LLVMTypeRef%5B%5D params = %7BLLVMDoubleType()%7D;%0A      LLVMTypeRef funcType = LLVMFunctionType(LLVMVoidType(), new PointerPointer(params), 1, 0);%0A      LLVMValueRef func = LLVMAddF
+default library%0A      addR
 un
-c
 ti
-on
+me
 (mod, 
-%22__eplan_print_double%22, funcType);%0A      LLVMSetLinkage(func, LLVMExternalLinkage
+builder
 );%0A%0A
@@ -1604,10 +1604,8 @@
 eRef
-%5B%5D
  pri
@@ -1618,235 +1618,72 @@
 ble_
-args = %7Bexp_value%7D;%0A      LLVMValueRef print_double = LLVMGetNamedFunction(mod, %22__eplan_print_double%22);%0A      LLVMValueRef print_double_result = LLVMBuildCall(builder, print_double, new PointerPointer(print_double_args), 1, %22%22
+result = addCall(mod, builder, %22__eplan_print_double%22, exp_value
 );%0A 
@@ -2483,32 +2483,32 @@
 ilder(builder);%0A
-
       LLVMDispos
@@ -2539,10 +2539,1221 @@
 ;%0A   %7D%0A%0A
+   public static LLVMValueRef addCall(LLVMModuleRef module,%0A                                      LLVMBuilderRef builder,%0A                                      String function,%0A                                      LLVMValueRef... args) %7B%0A      LLVMValueRef f = LLVMGetNamedFunction(module, function);%0A      return LLVMBuildCall(builder, f, new PointerPointer(args), args.length, %22%22);%0A   %7D%0A%0A   public static void addPrototype(LLVMModuleRef module,%0A                                   LLVMBuilderRef builder,%0A                                   String function,%0A                                   LLVMTypeRef result,%0A                                   LLVMTypeRef... args) %7B%0A      LLVMTypeRef funcType = LLVMFunctionType(result, new PointerPointer(args), args.length, 0);%0A      LLVMValueRef f = LLVMAddFunction(module, function, funcType);%0A      LLVMSetLinkage(f, LLVMExternalLinkage);%0A   %7D%0A%0A   public static void addRuntime(LLVMModuleRef module,%0A                                 LLVMBuilderRef builder) %7B%0A      addPrototype(module, builder, %22__eplan_print_double%22, LLVMVoidType(), LLVMDoubleType());%0A      addPrototype(module, builder, %22llvm.pow.f64%22, LLVMDoubleType(), LLVMDoubleType(), LLVMDoubleType());%0A   %7D%0A%0A
 %7D%0A

Add auxiliary methods to Generator