package org.westmalle.wayland.bootstrap;

import org.freedesktop.jaccall.Pointer;
import org.westmalle.wayland.nativ.glibc.Libc;
import org.westmalle.wayland.nativ.glibc.Libc_Symbols;
import org.westmalle.wayland.nativ.glibc.Libpthread;
import org.westmalle.wayland.nativ.glibc.Libpthread_Symbols;
import org.westmalle.wayland.nativ.glibc.sigset_t;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * This class changes the native signal mask before launching a new jvm. This ensures that we can catch the correct native
 * signal in the thread of our choosing. If we did not change the signal mask, any thread out of our control
 * (like the gc thread) could receive the native signal, and fire the default handler (which will exit the program).
 */
public class Launcher {

    private static final String optionPrefix = "option=";

    public static void main(final String[] args) throws IOException, InterruptedException {

        if (args.length == 1 &&
            (args[0].equals("--help") || args[0].equals("-h"))) {
            System.out.println("This program changes the native signal mask before launching a new jvm.\n" +
                               "This ensures that we can catch the correct native signal in the child jvm thread of our choosing.\n" +
                               "If we did not change the signal mask, any thread out of our control (like the gc thread)\n" +
                               "could receive the native signal, and fire the default handler (which would exit the program).\n\n" +
                               "Usage:\n" +
                               "\t option=<arg> \t - Pass <arg> as a child jvm option, eg. option=-Dkey=value or option=-Xmx=1234m\n" +
                               "\t <args> \t - Pass <args> as-is to the child jvm as program arguments.");
            System.exit(0);
        }

        new Libc_Symbols().link();
        new Libpthread_Symbols().link();

        final Libc       libc       = new Libc();
        final Libpthread libpthread = new Libpthread();

        //we block the signals that we want to catch in our child process.
        final sigset_t sigset = new sigset_t();
        libpthread.sigemptyset(Pointer.ref(sigset).address);
        libpthread.sigaddset(Pointer.ref(sigset).address,
                             libc.SIGRTMIN());
        libpthread.pthread_sigmask(Libc.SIG_BLOCK,
                                   Pointer.ref(sigset).address,
                                   0L);

        final List<String> options     = new LinkedList<>();
        final List<String> programArgs = new LinkedList<>();

        for (final String arg : args) {
            if (arg.startsWith(optionPrefix)) {
                options.add(arg.substring(optionPrefix.length()));
            }
            else {
                programArgs.add(arg);
            }
        }

        System.exit(startNewJavaProcess(options,
                                        Boot.class.getName(),
                                        programArgs).waitFor());
    }

    private static Process startNewJavaProcess(@Nonnull final List<String> options,
                                               @Nonnull final String mainClass,
                                               @Nonnull final List<String> arguments) throws IOException {
        Objects.requireNonNull(options);
        Objects.requireNonNull(mainClass);
        Objects.requireNonNull(arguments);

        final ProcessBuilder processBuilder = createProcess(options,
                                                            mainClass,
                                                            arguments);
        processBuilder.inheritIO();
        return processBuilder.start();
    }


    private static ProcessBuilder createProcess(final List<String> options,
                                                final String mainClass,
                                                final List<String> arguments) {
        final String jvm       = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
        final String classpath = System.getProperty("java.class.path");

        final List<String> command = new ArrayList<>();
        command.add(jvm);
        if (!options.isEmpty()) {
            command.addAll(options);
        }
        command.add(mainClass);
        if (!arguments.isEmpty()) {
            command.addAll(arguments);
        }

        final ProcessBuilder      processBuilder = new ProcessBuilder(command);
        final Map<String, String> environment    = processBuilder.environment();
        environment.put("CLASSPATH",
                        classpath);
        return processBuilder;
    }
}

@@ -1220,18 +1220,26 @@
 ntln(%22Th
-is
+e launcher
  program
@@ -1265,32 +1265,33 @@
  signal mask
+s
  before 
 launching a 
@@ -1270,38 +1270,36 @@
 al masks before 
-launch
+fork
 ing a new jvm.%5Cn
@@ -1288,24 +1288,35 @@
 rking a new 
+compositor 
 jvm.%5Cn%22 +%0A  
@@ -1367,32 +1367,36 @@
 hat 
-we can catch the correct
+the compositor jvm can catch
  nat
@@ -1409,25 +1409,16 @@
 gnal
+s
  in 
-the child jvm
+any
  thr
@@ -1416,35 +1416,35 @@
 n any thread of 
-our
+its
  choosing.%5Cn%22 +%0A
@@ -1511,36 +1511,34 @@
 nal mask
+s
 ,
- any
  thread
+s
  out of 
 our cont
@@ -1521,35 +1521,48 @@
  threads out of 
-our
+normal execution
  control (like t
@@ -1557,33 +1557,48 @@
 trol (like the g
-c
+arbage collector
  thread)%5Cn%22 +%0A  
@@ -1633,36 +1633,32 @@
  %22could receive 
-the 
 native signal, a
@@ -1646,32 +1646,33 @@
 ve native signal
+s
 , and fire the d
@@ -1682,25 +1682,24 @@
 ult handler 
-(
 which would 
@@ -1707,24 +1707,19 @@
 xit the 
-program)
+jvm
 .%5Cn%5Cn%22 +
@@ -1757,16 +1757,57 @@
  %22Usage:
+ java -jar jarfile %5Boption=arg...%5D %5Bargs%5D
 %5Cn%22 +%0A  
@@ -1850,26 +1850,24 @@
 ion=%3Carg%3E %5Ct
- -
  Pass %3Carg%3E 
@@ -1870,22 +1870,42 @@
 rg%3E 
-as a child jvm
+to the compositor jvm option as an
  opt
@@ -2002,18 +2002,16 @@
 args%3E %5Ct
- -
  Pass %3Ca

make launcher help text a bit better