package cn.ibizlab.codegen.cmd;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.concurrent.*;

public class LocalCommandExecutor {

    static final Logger logger = LoggerFactory.getLogger(LocalCommandExecutor.class);

    static ExecutorService pool = new ThreadPoolExecutor(0, 50, 3L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());

    private static LocalCommandExecutor instance;

    static {
        instance = new LocalCommandExecutor();
    }

    public static LocalCommandExecutor getInstance() {
        return instance;
    }

    private void closeQuietly(Closeable c) {
        try {
            if (c != null) {
                c.close();
            }
        } catch (IOException e) {
            logger.error("exception", e);
        }
    }



    public ExecuteResult executeCommand(String command, long timeout) {

        Future<ExecuteResult> executeFuture = null;


        try {
            logger.info(command);

            // create a Callable for the command's Process which can be called by an Executor
            Callable<ExecuteResult> call = new Callable<ExecuteResult>() {
                public ExecuteResult call() throws Exception {
                    ProcessBuilder processBuilder = null;
                    Process process = null;
                    InputStream pIn = null;
                    StreamGobbler outputGobbler = null;

                    try
                    {
                        processBuilder=new ProcessBuilder(new String[]{"sh","-c",command});
                        processBuilder.redirectErrorStream(true);

                        process=processBuilder.start();
                        logger.debug(command+ " started()");

                        // close process's output stream.
                        process.getOutputStream().close();

                        pIn = process.getInputStream();
                        outputGobbler = new StreamGobbler(pIn, "OUTPUT");
                        outputGobbler.start();


                        System.out.println(Thread.currentThread().getName() + " is running");
                        process.waitFor();
                        return new ExecuteResult(process.exitValue(), outputGobbler.getContent());
                    }
                    catch (Exception ex)
                    {
                        String errorMessage = "The command [" + command + "] execute failed.";
                        logger.error(errorMessage, ex);
                        return new ExecuteResult(-1, "");
                    }
                    finally {
                        if (pIn != null) {
                            closeQuietly(pIn);
                            if (outputGobbler != null && !outputGobbler.isInterrupted()) {
                                outputGobbler.interrupt();
                            }
                        }

                        if ( process != null) {
                            process.destroy();
                        }
                    }


                }
            };

            // submit the command's call and get the result from a
            executeFuture = pool.submit(call);
            return executeFuture.get(timeout, TimeUnit.MILLISECONDS);

        } catch (TimeoutException ex) {
            String errorMessage = "The command [" + command + "] timed out.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(-1, "");
        } catch (ExecutionException ex) {
            String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(-1, "");
        } catch (InterruptedException ex) {
            String errorMessage = "The command [" + command + "] did not complete due to an interrupted error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(-1, "");
        } finally {
            if (executeFuture != null) {
                try {
                    executeFuture.cancel(true);
                } catch (Exception ignore) {
                    ignore.printStackTrace();
                }
            }

        }
    }
}