/**
 * Access to Java bytecode by conversion to ATerms.
 *
 * @todo Use libstratego-sglr for parsing signatures
 * @author Martin Bravenboer <martin@cs.uu.nl>
 * @author Lennart Kats <l.c.l.kats@tudelft.nl>
 */
module dryad/bytecode/Bridge
imports
  libstratego-jvm
  dryad/bytecode/Signature
  dryad/bytecode/ClassSignature
  dryad/bytecode/MethodSignature
  dryad/bytecode/FieldSignature
  
strategies

  read-class-file =
    ?Jar(jarpath, filepath)
    ; where(get-jvm(|[<xtc-find> "class2aterm.jar"]) => jvm)    
    ; <strjvm-callstatic(|jvm, "str/classtree/Class2ATerm", "disasm_from_jar")> [jarpath, filepath]
    ; parse-signatures
    
  read-class-file =
    (?ClassFile(path) + ?FILE(path))
    ; where(get-jvm(|[<xtc-find> "class2aterm.jar"]) => jvm)
    ; <strjvm-callstatic(|jvm, "str/classtree/Class2ATerm", "disasm")> [path, "off"]
    ; parse-signatures
    
  read-class-file-disasm = 
    (?ClassFile(path) + ?FILE(path))
    ; where(get-jvm(|[<xtc-find> "class2aterm.jar"]) => jvm)
    ; <strjvm-callstatic(|jvm, "str/classtree/Class2ATerm", "disasm")> [path, "on"]
    ; parse-signatures
    
  /**
   * Serializes a Java class file to a file.
   * The output file name and path is determined from the package and class name.
   *
   * @type ClassFile -> FILE
   */
  write-class-file:
    classfile @ ClassFile(_, _, _, ThisClass(name), _, _, _, _, _) -> FILE(output)
    where
      path         := <add-extension> (<string-replace(|".", "/")> name, "class")
    ; FILE(output) := <write-class-file(|path)> classfile

  /**
   * Serializes a Java class file to a file.
   *
   * @type ClassFile -> FILE
   */
  write-class-file(|path):
    ClassFile(_, _, _, _, _, _, _, _, _) -> FILE(path)
    where
      write-to
    ; xtc-transform(!"baffle", !["-ws"]) => FILE(input)
    
    ; where(get-jvm(|[<xtc-find> "class2aterm.jar"]) => jvm)
    ; <strjvm-callstatic(|jvm, "str/classtree/ATerm2Class", "asm")> [input, path]

strategies  
    
  get-jvm(|classpath) =
    GetJVM
    <+ strjvm-create(|classpath) => jvm
       ; rules(GetJVM: _ -> jvm)

strategies

  parse-signatures =
    topdown(try(
      ?ClassSignature(<parse-class-signature>)
    + ?MethodSignature(<parse-method-signature>)
    + ?FieldSignature(<parse-field-signature>)
    ))

  parse-class-signature =
    parse-signature(|"ClassSignature.tbl")

  parse-method-signature =
    parse-signature(|"MethodSignature.tbl")
    
  parse-field-signature =
    parse-signature(|"FieldSignature.tbl")

  parse-signature(|tbl) =
    where(<get-signature-parse-table> tbl => opentbl)
    ; (parse-string(|opentbl) <+ report-parse-error; fail)
    ; desugar-signature

  get-signature-parse-table =
    ?tbl
    ; ( ParseTable
        <+ <xtc-find> tbl
           ; ReadFromFile
           ; open-parse-table => opentbl
           ; rules(ParseTable: tbl -> opentbl)
      )

  desugar-signature =
    alltd(
      ( ?ClassSignature(_, _, _)
      + ?MethodSignature(_, _, _, _)
      + ?FieldSignature(_)
      )
      ; topdown(repeat(DesugarSignature))
    )
    
  DesugarSignature :
    MethodSignature(Some(t1), t2, t3, t4) -> MethodSignature(t1, t2, t3, t4)
    
  DesugarSignature :    
    MethodSignature(None(), t2, t3, t4) -> MethodSignature(TypeParams([]), t2, t3, t4)
    
  DesugarSignature :
    ClassSignature(Some(t1), t2, t3) -> ClassSignature(t1, t2, t3)

  DesugarSignature :
    ClassSignature(None(), t2, t3) -> ClassSignature(TypeParams([]), t2, t3)
    
  DesugarSignature :
    TypeName(Id(x)) -> TypeName(PackageName([]), Id(x))
    
  DesugarSignature :
    TypeArg(None(), type) -> type

  /**
   * @todo Desugar None ClassBound
   */  
  DesugarSignature :
    TypeParam(Id(x), ClassBound(Some(ftype)), is)
      ->
    TypeParam(Id(x), Some(TypeBound([ftype | is])))
    
  DesugarSignature :
    Class(type) -> type

  /**
   * @todo Temporary hack to support ? extends E by rewriting to E.
   */    
  DesugarSignature :    
    TypeArg(Some(Plus), TypeVar(Id(x))) -> TypeArg(None(), TypeVar(Id(x)))