package org.infinispan.server.test.util.arquillian.extensions;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.logging.Logger;

import org.jboss.arquillian.container.spi.Container;
import org.jboss.arquillian.container.spi.ServerKillProcessor;

/**
 * Handles calls to  {@link org.jboss.arquillian.container.test.api.ContainerController#kill(String)}
 *
 * @author <a href="mailto:mgencur@redhat.com">Martin Gencur</a>
 * @author <a href="mailto:tsykora@redhat.com">Tomas Sykora</a>
 */
public class InfinispanServerKillProcessor implements ServerKillProcessor {

    private final Logger log = Logger.getLogger(InfinispanServerKillProcessor.class.getName());

    private static final long TIMEOUT = 1000;

    public void kill(Container container) throws Exception {

        String osName = System.getProperty("os.name", "");
        String killSequence = null;
        Process p = null;

        if (osName.startsWith("Linux")) {
            killSequence = "kill -9 `ps aux | grep -v 'grep' | grep 'jboss.home.dir=[jbossHome] ' | sed -re '1,$s/[ \\t]+/ /g' | cut -d ' ' -f 2`";
            killSequence = killSequence.replace("[jbossHome]", container.getContainerConfiguration().getContainerProperties()
                    .get("jbossHome"));
            p = Runtime.getRuntime().exec(new String[]{"sh", "-c", killSequence});
            executeKill(p, killSequence);
        } else if (osName.startsWith("Mac OS X")) {
            killSequence = "ps aux | grep -v grep | grep 'jboss.home.dir=[jbossHome]' | awk '{ print $2 }' |xargs kill -9";
            killSequence = killSequence.replace("[jbossHome]", container.getContainerConfiguration().getContainerProperties()
                    .get("jbossHome"));
            p = Runtime.getRuntime().exec(new String[]{"sh", "-c", killSequence});
            executeKill(p, killSequence);
        } else if (osName.startsWith("SunOS")) {
            killSequence = "jps | grep jboss-modules.jar | cut -f 1 -d ' ' | head -1 | xargs kill -9";
            p = Runtime.getRuntime().exec(new String[]{"sh", "-c", killSequence});
            executeKill(p, killSequence);
        } else if (osName.startsWith("HP-UX")) {
            // port-offset: node0=0, node0=100, node0=200
            String port = "8080";
            String vmArguments = container.getContainerConfiguration().getContainerProperties().get("javaVmArguments");
            if (vmArguments.contains("jboss.node.name=node1")) {
                port = "8180";
            }
            if (vmArguments.contains("jboss.node.name=node2")) {
                port = "8280";
            }
            killSequence = "lsof | grep '" + port + " (LISTEN)' | awk '{print $2}' | xargs kill -9";
            p = Runtime.getRuntime().exec(new String[]{"sh", "-c", killSequence});
            executeKill(p, killSequence);
        } else if (osName.startsWith("Windows")) {
            // port-offset: node0=0, node0=100, node0=200
            String port = "8080";
            String vmArguments = container.getContainerConfiguration().getContainerProperties().get("javaVmArguments");
            if (vmArguments.contains("jboss.node.name=node1")) {
                port = "8180";
            }
            if (vmArguments.contains("jboss.node.name=node2")) {
                port = "8280";
            }
            Process ptemp = Runtime.getRuntime().exec(
                    new String[]{"cmd", "/c", "netstat -aon | findstr LISTENING | findstr 127.0.0.1:" + port});
            ptemp.waitFor();
            log.info("Exit value of finding process (0 = ok, else num. unexpected): " + ptemp.exitValue());
            if (ptemp.exitValue() == 0) {
                String idForKill = getPidForKill(ptemp.getInputStream());
                log.info("Process ID for kill: " + idForKill + " (port: " + port + ")");
                killSequence = "taskkill /F /T /PID " + idForKill;
                p = Runtime.getRuntime().exec(new String[]{"cmd", "/c", killSequence});
                executeKill(p, killSequence);
            } else {
                throw new RuntimeException("Finding sequence failed => server not killed. (Exit value of finding process: "
                        + p.exitValue() + " OS=" + System.getProperty("os.name") + ")");
            }
        } else {
            throw new RuntimeException("System was probably NOT properly decided. Property os.name = "
                    + System.getProperty("os.name"));
        }
    }

    private void executeKill(Process p, String logKillSequence) throws Exception {
        log.info("Issuing kill sequence (on " + System.getProperty("os.name") + "): " + logKillSequence);
        p.waitFor();
        if (p.exitValue() != 0) {
            throw new RuntimeException("Kill sequence failed => server not killed. (Exit value of killing process: "
                    + p.exitValue() + " OS=" + System.getProperty("os.name") + ")");
        }
        Thread.sleep(TIMEOUT);
        log.info("Kill sequence successfully completed. \n");
    }

    /**
     * Get ID of process listening on 127.0.0.1:8080 (or 8180 or 8280). Used on Windows.
     * Note! This method is closing InputStream.
     */
    private String getPidForKill(InputStream is) throws IOException {
        String line = null;
        String lineForSplit = null;
        BufferedReader in = new BufferedReader(new InputStreamReader(is));
        lineForSplit = in.readLine(); // returns null if end of stream was reached
        log.info("Getting process id from: " + lineForSplit);
        while ((line = in.readLine()) != null) {
            log.warning("There is another line here! : " + line);
        }
        in.close();
        // Expected: [white spaces] TCP 127.0.0.1:8080 0.0.0.0:0 LISTENING 3468
        if (lineForSplit != null) {
            String[] output = lineForSplit.split("\\s+");
            return output[5];
        } else {
            throw new RuntimeException(
                    "Finding sequence failed => server not killed. (Unexpected problem in reading process InputStream)");
        }
    }
}
