package com.github.sormuras.bach.customizable;

import com.github.sormuras.bach.Bach;
import com.github.sormuras.bach.Command;
import com.github.sormuras.bach.Project;
import com.github.sormuras.bach.command.JarCommand;
import com.github.sormuras.bach.command.JavacCommand;
import com.github.sormuras.bach.project.DeclaredModule;
import com.github.sormuras.bach.project.FolderType;
import com.github.sormuras.bach.project.ProjectSpace;
import java.nio.file.Path;

/** Compiles and archives Java source files. */
public class CompileWorkflow extends Workflow {

  protected final ProjectSpace space;

  public CompileWorkflow(Bach bach, Project project, ProjectSpace space) {
    super(bach, project);
    this.space = space;
  }

  protected JavacCommand computeJavacCommand(Path classes) {
    var computedModuleSourcePath = ModuleSourcePathComputer.compute(space);
    return Command.javac()
        .modules(space.modules().names())
        .option(computedModuleSourcePath.patterns())
        .option(computedModuleSourcePath.specifics())
        .outputDirectoryForClasses(classes);
  }

  protected JarCommand computeJarCommand(DeclaredModule module, Path classes, Path modules) {
    var name = module.name();
    var version = project.version().value();
    var file = modules.resolve(name + "@" + version + ".jar");

    var jar = Command.jar().mode("--create").file(file);

    if (module.mainClass().isPresent()) jar = jar.main(module.mainClass().get());

    var folders = module.folders();
    var baseSources = folders.list(0, FolderType.SOURCES);
    if (!baseSources.isEmpty()) jar = jar.filesAdd(classes.resolve(name));
    var baseResources = folders.list(0, FolderType.RESOURCES);
    for (var resource : baseResources) jar = jar.filesAdd(resource);

    //    if (computedModulePatches.mode() == PatchMode.CLASSES) {
    //      var list = computedModulePatches.map().getOrDefault(name, List.of());
    //      jar = jar.forEach(list, (call, path) -> call.with("-C", path, "."));
    //    }

    // multi-release
    for (var release = 9; release <= Runtime.version().feature(); release++) {
      if (!folders.list(release, FolderType.SOURCES).isEmpty()) {
        jar = jar.filesAdd(release, computeOutputDirectoryForClasses(name, release));
      }
      for (var resource : folders.list(release, FolderType.RESOURCES)) {
        jar = jar.filesAdd(release, resource);
      }
    }

    return jar;
  }

  protected Path computeOutputDirectoryForClasses() {
    return bach.path().workspace(space.name(), "classes-" + computeRelease());
  }

  protected Path computeOutputDirectoryForClasses(String module, int release) {
    return bach.path().workspace(space.name(), "classes-mr-" + release, module);
  }

  protected Path computeOutputDirectoryForModules() {
    return bach.path().workspace(space.name(), "modules");
  }

  protected int computeRelease() {
    return /*space.release().map(JavaRelease::feature).orElse*/ Runtime.version().feature();
  }

  @Override
  public void run() {
    var modules = space.modules().values();
    if (modules.isEmpty()) {
      bach.logMessage("No %s module present".formatted(space.name()));
      return;
    }
    var size = modules.size();
    bach.logCaption("Compile %d %s module%s".formatted(size, space.name(), size == 1 ? "" : "s"));

    var classesDirectory = computeOutputDirectoryForClasses();
    var modulesDirectory = computeOutputDirectoryForModules();
    bach.run(computeJavacCommand(classesDirectory));
    // TODO Compile sources targeted to Java 8
    // TODO Compile sources targeted to Java 9+
    bach.run(Command.of("directories", "clean", modulesDirectory));
    modules.stream()
        .parallel()
        .map(module -> computeJarCommand(module, classesDirectory, modulesDirectory))
        .forEach(bach::run);
  }
}

@@ -259,32 +259,91 @@
 d.JavacCommand;%0A
+import com.github.sormuras.bach.command.ModulePathsOption;%0A
 import com.githu
@@ -382,16 +382,16 @@
 Module;%0A
-
 import c
@@ -488,16 +488,44 @@
 tSpace;%0A
+import java.nio.file.Files;%0A
 import j
@@ -1119,24 +1119,68 @@
 pecifics())%0A
+        .option(computeModulePathsOption())%0A
         .out
@@ -2907,16 +2907,34 @@
 Modules(
+ProjectSpace space
 ) %7B%0A    
@@ -3126,16 +3126,378 @@
 );%0A  %7D%0A%0A
+  protected ModulePathsOption computeModulePathsOption() %7B%0A    var paths = ModulePathsOption.empty();%0A    for (var parent : space.parents()) paths = paths.add(computeOutputDirectoryForModules(parent));%0A    var externalModules = bach.path().externalModules();%0A    if (Files.isDirectory(externalModules)) paths = paths.add(externalModules);%0A    return paths;%0A  %7D%0A%0A
   @Overr
@@ -3876,24 +3876,24 @@
 rClasses();%0A
-
     var modu
@@ -3936,24 +3936,29 @@
 yForModules(
+space
 );%0A    bach.

Compute `--module-path` option