package io.quarkus.deployment.ide;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.quarkus.dev.console.DevConsoleManager;

public enum Ide {

    // see for cli syntax of idea https://www.jetbrains.com/help/idea/opening-files-from-command-line.html
    IDEA("idea", null, "--help"),
    ECLIPSE("eclipse", null, (String[]) null),
    VSCODE("code", null, "--version"),
    NETBEANS("netbeans", null, "--help");

    private final String defaultCommand;
    private final List<String> markerArgs;
    private final String lineNumberArg;
    private String machineSpecificCommand;

    private String effectiveCommand;

    Ide(String defaultCommand, String lineNumberArg, String... markerArgs) {
        this.defaultCommand = defaultCommand;
        this.lineNumberArg = lineNumberArg;
        this.markerArgs = markerArgs != null ? Arrays.asList(markerArgs) : Collections.emptyList();
    }

    /**
     * Attempts to launch the default IDE script. If it succeeds, then that command is used (as the command is on the $PATH),
     * otherwise the full path of the command (determined earlier in the process by looking at the running processes)
     * is used.
     */
    public String getEffectiveCommand() {
        if (effectiveCommand != null) {
            return effectiveCommand;
        }
        effectiveCommand = doGetEffectiveCommand();
        return effectiveCommand;
    }

    private String doGetEffectiveCommand() {
        if (defaultCommand != null) {
            if (markerArgs == null) {
                // in this case there is nothing much we can do but hope that the default command will work
                return defaultCommand;
            } else {
                try {
                    List<String> command = new ArrayList<>(1 + markerArgs.size());
                    command.add(defaultCommand);
                    command.addAll(markerArgs);
                    new ProcessBuilder(command).redirectError(ProcessBuilder.Redirect.DISCARD.file())
                            .redirectOutput(ProcessBuilder.Redirect.DISCARD.file()).start()
                            .waitFor(10,
                                    TimeUnit.SECONDS);
                    return defaultCommand;
                } catch (Exception e) {
                    return machineSpecificCommand;
                }
            }
        } else {
            // in this case the IDE does not provide a default command so we need to rely on what was found
            // from inspecting the running processes
            return machineSpecificCommand;
        }
    }

    public List<String> createFileOpeningArgs(String fileName, String line) {
        if (line == null || line.isEmpty()) {
            return Collections.singletonList(fileName);
        }

        if (lineNumberArg == null) {
            return Collections.singletonList(fileName + ":" + line);
        }

        String formattedLineArg = String.format(lineNumberArg, line);

        return List.of(formattedLineArg, fileName);
    }

    public void setMachineSpecificCommand(String machineSpecificCommand) {
        this.machineSpecificCommand = machineSpecificCommand;
    }

    /**
     * Finds the location of a source file given the path from the source root
     *
     * @param fileName The file name
     * @return The path or null if it could not be found
     */
    public static Path findSourceFile(String fileName) {
        for (var i : DevConsoleManager.getHotReplacementContext().getSourcesDir()) {
            Path resolved = i.resolve(fileName);
            if (Files.exists(resolved)) {
                return resolved;
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return "Ide{" +
                "defaultCommand='" + defaultCommand + '\'' +
                '}';
    }
}

@@ -116,33 +116,8 @@
 st;%0A
-import java.util.Arrays;%0A
 impo
@@ -202,16 +202,52 @@
 imeUnit;
+%0Aimport java.util.stream.Collectors;
 %0A%0Aimport
@@ -433,22 +433,49 @@
 %22idea%22, 
-null, 
+Collections.emptyList(), List.of(
 %22--help%22
@@ -475,16 +475,17 @@
 --help%22)
+)
 ,%0A    EC
@@ -505,57 +505,132 @@
 e%22, 
-null, (String%5B%5D) null),%0A    VSCODE(%22code%22, null, 
+Collections.emptyList(), Collections.emptyList()),%0A    VSCODE(%22code%22, List.of(%22--goto%22, %22%7BfileName%7D:%7BlineNumber%7D%22), List.of(
 %22--v
@@ -637,16 +637,17 @@
 ersion%22)
+)
 ,%0A    NE
@@ -669,14 +669,41 @@
 s%22, 
-null, 
+Collections.emptyList(), List.of(
 %22--h
@@ -707,16 +707,17 @@
 --help%22)
+)
 ;%0A%0A    p
@@ -813,22 +813,28 @@
 e final 
+List%3C
 String
+%3E
  lineNum
@@ -835,24 +835,25 @@
 ineNumberArg
+s
 ;%0A    privat
@@ -955,22 +955,28 @@
 ommand, 
+List%3C
 String
+%3E
  lineNum
@@ -985,19 +985,23 @@
 rArg
+s
 , 
+List%3C
 String
-...
+%3E
  mar
@@ -1083,16 +1083,17 @@
 umberArg
+s
  = lineN
@@ -1100,16 +1100,17 @@
 umberArg
+s
 ;%0A      
@@ -1143,70 +1143,8 @@
 Args
- != null ? Arrays.asList(markerArgs) : Collections.emptyList()
 ;%0A  
@@ -1754,24 +1754,26 @@
 rkerArgs
- == null
+.isEmpty()
 ) %7B%0A    
@@ -3048,24 +3048,27 @@
 umberArg
- == null
+s.isEmpty()
 ) %7B%0A    
@@ -3155,48 +3155,113 @@
     
-String formattedLineArg = String.format(
+return lineNumberArgs.stream()%0A                .map(arg -%3E arg.replace(%22%7BfileName%7D%22, fileName).replace(%22%7B
 line
@@ -3270,20 +3270,18 @@
 mber
-Arg
+%7D%22
 , line)
-;%0A
+)
 %0A   
@@ -3289,49 +3289,44 @@
     
-return List.of(formattedLineArg, fileName
+        .collect(Collectors.toList()
 );%0A 

Use proper syntax to open file at line for Visual Studio Code