package org.smoothbuild.parse;

import static org.smoothbuild.parse.AssignArgsToParams.assignArgsToParams;
import static org.smoothbuild.parse.AssignNatives.assignNatives;
import static org.smoothbuild.parse.ConstructorLoader.loadConstructor;
import static org.smoothbuild.parse.FindNatives.findNatives;
import static org.smoothbuild.parse.FindSemanticErrors.findSemanticErrors;
import static org.smoothbuild.parse.ScriptParser.parseScript;
import static org.smoothbuild.util.Lists.list;
import static org.smoothbuild.util.Maybe.invokeWrap;
import static org.smoothbuild.util.Paths.changeExtension;

import java.nio.file.Path;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.smoothbuild.antlr.SmoothParser.ModuleContext;
import org.smoothbuild.lang.function.Native;
import org.smoothbuild.lang.runtime.SRuntime;
import org.smoothbuild.parse.ast.Ast;
import org.smoothbuild.parse.ast.AstCreator;
import org.smoothbuild.parse.ast.FuncNode;
import org.smoothbuild.parse.ast.StructNode;
import org.smoothbuild.util.Maybe;

public class ModuleLoader {
  private final SRuntime runtime;
  private final AssignTypes assignTypes;
  private final FunctionLoader functionLoader;

  @Inject
  public ModuleLoader(SRuntime runtime, AssignTypes assignTypes, FunctionLoader functionLoader) {
    this.runtime = runtime;
    this.assignTypes = assignTypes;
    this.functionLoader = functionLoader;
  }

  public List<? extends Object> loadModule(Path script) {
    Maybe<ModuleContext> module = parseScript(script);
    Maybe<Ast> maybeAst = invokeWrap(module, m -> AstCreator.fromParseTree(script, m));
    if (!maybeAst.hasValue()) {
      return maybeAst.errors();
    }
    Ast ast = maybeAst.value();
    List<? extends Object> errors = findSemanticErrors(runtime, ast);
    if (!errors.isEmpty()) {
      return errors;
    }
    errors = ast.sortFuncsByDependencies(runtime.functions());
    if (!errors.isEmpty()) {
      return errors;
    }
    errors = ast.sortTypesByDependencies(runtime.types());
    if (!errors.isEmpty()) {
      return errors;
    }
    errors = assignTypes.assignTypes(runtime.functions(), ast);
    if (!errors.isEmpty()) {
      return errors;
    }
    errors = assignArgsToParams(runtime.functions(), ast);
    if (!errors.isEmpty()) {
      return errors;
    }
    Maybe<Map<String, Native>> maybeNatives = findNatives(changeExtension(script, "jar"));
    if (!maybeNatives.hasValue()) {
      return maybeNatives.errors();
    }
    Map<String, Native> natives = maybeNatives.value();
    errors = assignNatives(ast, natives);
    if (!errors.isEmpty()) {
      return errors;
    }
    loadFunctions(ast);
    return list();
  }

  private void loadFunctions(Ast ast) {
    for (StructNode struct : ast.structs()) {
      runtime.functions().add(loadConstructor(struct));
    }
    for (FuncNode func : ast.funcs()) {
      runtime.functions().add(functionLoader.loadFunction(runtime.functions(), func));
    }
  }
}

@@ -485,61 +485,8 @@
 st;%0A
-import static org.smoothbuild.util.Maybe.invokeWrap;%0A
 impo
@@ -1451,16 +1451,21 @@
 ntext%3E m
+aybeM
 odule = 
@@ -1493,109 +1493,24 @@
     
-Maybe%3CAst%3E maybeAst = invokeWrap(module, m -%3E AstCreator.fromParseTree(script, m));%0A    if (!maybeAst
+if (!maybeModule
 .has
@@ -1538,19 +1538,22 @@
 rn maybe
-Ast
+Module
 .errors(
@@ -1579,24 +1579,60 @@
 t = 
-maybeAst
+AstCreator.fromParseTree(script, maybeModule
 .value()
 ;%0A  
@@ -1623,24 +1623,25 @@
 dule.value()
+)
 ;%0A    List%3C?

refactor ModuleLoader